Skip to content

Commit 60f117d

Browse files
authored
Update
1 parent f2339ed commit 60f117d

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

lib/youtubedl.js

+314
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
import axios from 'axios';
2+
import crypto from 'crypto';
3+
4+
const ogmp3 = {
5+
api: {
6+
base: "https://api3.apiapi.lat",
7+
endpoints: {
8+
a: "https://api5.apiapi.lat",
9+
b: "https://api.apiapi.lat",
10+
c: "https://api3.apiapi.lat"
11+
}
12+
},
13+
14+
headers: {
15+
'authority': 'api.apiapi.lat',
16+
'content-type': 'application/json',
17+
'origin': 'https://ogmp3.lat',
18+
'referer': 'https://ogmp3.lat/',
19+
'user-agent': 'Postify/1.0.0'
20+
},
21+
22+
formats: {
23+
video: ['240', '360', '480', '720', '1080'],
24+
audio: ['64', '96', '128', '192', '256', '320']
25+
},
26+
27+
default_fmt: {
28+
video: '720',
29+
audio: '320'
30+
},
31+
32+
restrictedTimezones: new Set(["-330", "-420", "-480", "-540"]),
33+
34+
utils: {
35+
hash: () => {
36+
const array = new Uint8Array(16);
37+
crypto.getRandomValues(array);
38+
return Array.from(array, byte => byte.toString(16).padStart(2, "0")).join("");
39+
},
40+
41+
encoded: (str) => {
42+
let result = "";
43+
for (let i = 0; i < str.length; i++) {
44+
result += String.fromCharCode(str.charCodeAt(i) ^ 1);
45+
}
46+
return result;
47+
},
48+
49+
enc_url: (url, separator = ",") => {
50+
const codes = [];
51+
for (let i = 0; i < url.length; i++) {
52+
codes.push(url.charCodeAt(i));
53+
}
54+
return codes.join(separator).split(separator).reverse().join(separator);
55+
}
56+
},
57+
58+
isUrl: str => {
59+
try {
60+
const url = new URL(str);
61+
const hostname = url.hostname.toLowerCase();
62+
const b = [/^(.+\.)?youtube\.com$/, /^(.+\.)?youtube-nocookie\.com$/, /^youtu\.be$/];
63+
return b.some(a => a.test(hostname)) && !url.searchParams.has("playlist");
64+
} catch (_) {
65+
return false;
66+
}
67+
},
68+
69+
youtube: url => {
70+
if (!url) return null;
71+
const b = [
72+
/youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/,
73+
/youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/,
74+
/youtube\.com\/v\/([a-zA-Z0-9_-]{11})/,
75+
/youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/,
76+
/youtu\.be\/([a-zA-Z0-9_-]{11})/
77+
];
78+
for (let a of b) {
79+
if (a.test(url)) return url.match(a)[1];
80+
}
81+
return null;
82+
},
83+
84+
request: async (endpoint, data = {}, method = 'post') => {
85+
try {
86+
const ae = Object.values(ogmp3.api.endpoints);
87+
const be = ae[Math.floor(Math.random() * ae.length)];
88+
89+
const fe = endpoint.startsWith('http') ? endpoint : `${be}${endpoint}`;
90+
91+
const { data: response } = await axios({
92+
method,
93+
url: fe,
94+
data: method === 'post' ? data : undefined,
95+
headers: ogmp3.headers
96+
});
97+
return {
98+
status: true,
99+
code: 200,
100+
data: response
101+
};
102+
} catch (error) {
103+
return {
104+
status: false,
105+
code: error.response?.status || 500,
106+
error: error.message
107+
};
108+
}
109+
},
110+
111+
async checkStatus(id) {
112+
try {
113+
const c = this.utils.hash();
114+
const d = this.utils.hash();
115+
const endpoint = `/${c}/status/${this.utils.encoded(id)}/${d}/`;
116+
117+
const response = await this.request(endpoint, {
118+
data: id
119+
});
120+
121+
return response;
122+
} catch (error) {
123+
return {
124+
status: false,
125+
code: 500,
126+
error: error.message
127+
};
128+
}
129+
},
130+
131+
async checkProgress(data) {
132+
try {
133+
let attempts = 0;
134+
let maxAttempts = 300;
135+
136+
while (attempts < maxAttempts) {
137+
attempts++;
138+
139+
const res = await this.checkStatus(data.i);
140+
if (!res.status) {
141+
await new Promise(resolve => setTimeout(resolve, 2000));
142+
continue;
143+
}
144+
145+
const stat = res.data;
146+
if (stat.s === "C") {
147+
return stat;
148+
}
149+
150+
if (stat.s === "P") {
151+
await new Promise(resolve => setTimeout(resolve, 2000));
152+
continue;
153+
}
154+
155+
return null;
156+
}
157+
158+
return null;
159+
} catch (error) {
160+
return null;
161+
}
162+
},
163+
164+
download: async (link, format, type = 'video') => {
165+
if (!link) {
166+
return {
167+
status: false,
168+
code: 400,
169+
error: "What to download? Enter a link, idiot."
170+
};
171+
}
172+
173+
if (!ogmp3.isUrl(link)) {
174+
return {
175+
status: false,
176+
code: 400,
177+
error: "This link is invalid. Enter a valid YouTube video link, fools."
178+
};
179+
}
180+
181+
if (type !== 'video' && type !== 'audio') {
182+
return {
183+
status: false,
184+
code: 400,
185+
error: "Choose video or audio?"
186+
};
187+
}
188+
189+
if (!format) {
190+
format = type === 'audio' ? ogmp3.default_fmt.audio : ogmp3.default_fmt.video;
191+
}
192+
193+
const valid_fmt = type === 'audio' ? ogmp3.formats.audio : ogmp3.formats.video;
194+
if (!valid_fmt.includes(format)) {
195+
return {
196+
status: false,
197+
code: 400,
198+
error: `Format ${format} is invalid for ${type}. You can choose from: ${valid_fmt.join(', ')}`
199+
};
200+
}
201+
202+
const id = ogmp3.youtube(link);
203+
if (!id) {
204+
return {
205+
status: false,
206+
code: 400,
207+
error: "Where is the video ID? Cannot extract it, idiot."
208+
};
209+
}
210+
211+
try {
212+
let retries = 0;
213+
const maxRetries = 20;
214+
215+
while (retries < maxRetries) {
216+
retries++;
217+
const c = ogmp3.utils.hash();
218+
const d = ogmp3.utils.hash();
219+
const req = {
220+
data: ogmp3.utils.encoded(link),
221+
format: type === 'audio' ? "0" : "1",
222+
referer: "https://ogmp3.cc",
223+
mp3Quality: type === 'audio' ? format : null,
224+
mp4Quality: type === 'video' ? format : null,
225+
userTimeZone: new Date().getTimezoneOffset().toString()
226+
};
227+
228+
const resx = await ogmp3.request(
229+
`/${c}/init/${ogmp3.utils.enc_url(link)}/${d}/`,
230+
req
231+
);
232+
233+
if (!resx.status) {
234+
if (retries === maxRetries) return resx;
235+
continue;
236+
}
237+
238+
const data = resx.data;
239+
if (data.le) {
240+
return {
241+
status: false,
242+
code: 400,
243+
error: "The video duration is too long. The maximum is 3 hours. Don't exceed it."
244+
};
245+
}
246+
247+
if (data.i === "blacklisted") {
248+
const limit = ogmp3.restrictedTimezones.has(new Date().getTimezoneOffset().toString()) ? 5 : 100;
249+
return {
250+
status: false,
251+
code: 429,
252+
error: `Daily download limit (${limit}) reached. Try again later.`
253+
};
254+
}
255+
256+
if (data.e || data.i === "invalid") {
257+
return {
258+
status: false,
259+
code: 400,
260+
error: "The video doesn't exist. Maybe it was deleted or restricted by YouTube."
261+
};
262+
}
263+
264+
if (data.s === "C") {
265+
return {
266+
status: true,
267+
code: 200,
268+
result: {
269+
title: data.t || "Unknown",
270+
type: type,
271+
format: format,
272+
thumbnail: `https://i.ytimg.com/vi/${id}/maxresdefault.jpg`,
273+
download: `${ogmp3.api.base}/${ogmp3.utils.hash()}/download/${ogmp3.utils.encoded(data.i)}/${ogmp3.utils.hash()}/`,
274+
id: id,
275+
quality: format
276+
}
277+
};
278+
}
279+
280+
const prod = await ogmp3.checkProgress(data);
281+
if (prod && prod.s === "C") {
282+
return {
283+
status: true,
284+
code: 200,
285+
result: {
286+
title: prod.t || "Unknown",
287+
type: type,
288+
format: format,
289+
thumbnail: `https://i.ytimg.com/vi/${id}/maxresdefault.jpg`,
290+
download: `${ogmp3.api.base}/${ogmp3.utils.hash()}/download/${ogmp3.utils.encoded(prod.i)}/${ogmp3.utils.hash()}/`,
291+
id: id,
292+
quality: format
293+
}
294+
};
295+
}
296+
}
297+
298+
return {
299+
status: false,
300+
code: 500,
301+
error: "I'm exhausted... Tried several times, but it still doesn't work. So I'll leave it for now. Goodbye! 😂"
302+
};
303+
304+
} catch (error) {
305+
return {
306+
status: false,
307+
code: 500,
308+
error: error.message
309+
};
310+
}
311+
}
312+
};
313+
314+
export { ogmp3 };

0 commit comments

Comments
 (0)