|
| 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