yt2mp3.gs scrape by claidex ( alfidev ) A developer known as claidex (alfidev) created a script to scrape the YouTube-to-MP3 conversion service yt2mp3.gs. The script automates the authentication, conversion, and download process, handling redirects and progress polling. It demonstrates how to programmatically interact with the service's API endpoints hosted on epsilon.epsiloncloud.org. | import { createWriteStream } from "fs"; | | | import { pipeline } from "stream/promises"; | | | const EPS = "epsilon.epsiloncloud.org"; | | | const ORIGIN = "https://yt2mp3.gs"; | | | const youtube = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; | | | const FORMAT = "mp3"; | | | let key, geo; | | | const HEADERS = { | | | "User-Agent": "Mozilla/5.0 Windows NT 10.0; Win64; x64; rv:152.0 Gecko/20100101 Firefox/152.0", | | | "Accept": " / ", | | | "Accept-Language": "en-US,en;q=0.9", | | | "Origin": ORIGIN, | | | "Referer": ${ORIGIN}/ , | | | }; | | | async function req url, extra = {} { | | | const res = await fetch url, { headers: { ...HEADERS, ...extra } } ; | | | const text = await res.text ; | | | if res.ok throw new Error HTTP ${res.status} ${url} ; | | | return JSON.parse text ; | | | } | | | async function auth vid, fmt { | | | const r = await req https://${EPS}/api/v1/auth? =${Date.now } ; | | | if r.error 0 throw new Error auth: ${r.error} ; | | | geo = r.geo; | | | key = r.key; | | | await initialize vid, fmt ; | | | } | | | async function initialize vid, fmt { | | | console.log "→ initializing..." ; | | | const r = await req https://${EPS}/api/v1/init? =${Date.now } , { Authorization: Bearer ${key} } ; | | | if r.error 0 throw new Error init: ${r.error} ; | | | await convert r.convertURL, vid, fmt, 0 ; | | | } | | | async function convert cUrl, vid, fmt, redirected { | | | const r = await req ${cUrl}&v=${vid}&f=${fmt}& =${Date.now } ; | | | if r.error 0 throw new Error convert: ${r.error} ; | | | if redirected === 1 { | | | return geo | | | ? waitProgress r.progressURL, r.downloadURL, vid, fmt, r.title | | | : download r.downloadURL, vid, fmt, r.title ; | | | } | | | if r.redirect === 1 return convert r.redirectURL, vid, fmt, 1 ; | | | await pollProgress r.progressURL, r.downloadURL, vid, fmt ; | | | } | | | const STATES = "checking video", "extracting video", "converting video", "done" ; | | | async function pollProgress pUrl, dlUrl, vid, fmt, prev = -1 { | | | const r = await req ${pUrl}& =${Date.now } ; | | | if r.error 0 throw new Error progress: ${r.error} ; | | | if r.title console.log title: ${r.title} ; | | | if r.progress == prev console.log ${STATES r.progress || r.progress} ; | | | if r.progress < 3 { | | | await delay 3000 ; | | | return pollProgress pUrl, dlUrl, vid, fmt, r.progress ; | | | } | | | await download dlUrl, vid, fmt ; | | | } | | | async function waitProgress pUrl, dlUrl, vid, fmt, title { | | | for const s of "extracting video", "converting video" { | | | await delay 3000 ; | | | console.log ${s} ; | | | } | | | await download dlUrl, vid, fmt, title ; | | | } | | | async function download dlUrl, vid, fmt, title { | | | const safe = ${ title || vid .replace / /\\?% :|"< /g, " " }.${fmt} ; | | | console.log \n✓ downloading: ${safe} ; | | | const res = await fetch ${dlUrl}&v=${vid}&f=${fmt}&r=cli , { headers: HEADERS, redirect: "follow" } ; | | | if res.ok throw new Error download: HTTP ${res.status} ; | | | await pipeline res.body, createWriteStream safe ; | | | console.log ✓ saved: ${safe} ; | | | } | | | const delay = ms = new Promise r = setTimeout r, ms ; | | | function extractId str { | | | return / ?:youtu\.be\/|youtube\.com\/ ?:embed\/|live\/|shorts\/ | ?& v= a-zA-Z0-9- {11} /.exec str || 1 || null; | | | } | | | const vid = extractId youtube ; | | | if vid { console.error "invalid URL" ; process.exit 1 ; } | | | if FORMAT == "mp3" && FORMAT == "mp4" { console.error "invalid format" ; process.exit 1 ; } | | | console.log \n→ processing ${vid} as ${FORMAT} ; | | | try { await auth vid, FORMAT ; } | | | catch e { console.error "✗", e.message ; process.exit 1 ; } |