Skip to content

Instantly share code, notes, and snippets.

@samelie
Created February 26, 2024 05:41
Show Gist options
  • Save samelie/5c987298c40d5e1faec9293b436ec463 to your computer and use it in GitHub Desktop.
Save samelie/5c987298c40d5e1faec9293b436ec463 to your computer and use it in GitHub Desktop.
browser code
1. Deploy to Cloudflare workers
2. Proxy ytdl-core thru the proxy
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
import ytdl from 'ytdl-core'
import './style.css'
// https://github.com/darenliang/darenliang.com/blob/3debad5aaeadf6581dfab36f32e5166c01b7739b/misc/ytdl-ffmpeg-demo/main.js
// https://github.com/ffmpegwasm/ffmpeg.wasm/blob/main/apps/vue-vite-app/src/components/FFmpegDemo.vue
const baseURL = 'https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm'
const ffmpeg = new FFmpeg()
// REPLACE this!
const WORKER_URL = `https://MY_WORKER.workers.dev`
const cleanMemory = () => {
console.log('[info] unlinking old data')
try {
ffmpeg.FS('unlink', 'output.mp3')
} catch {}
try {
ffmpeg.FS('unlink', 'output.mp4')
} catch {}
}
const ytdlOptions = {
requestOptions: {
maxRetries: 2,
backoff: { inc: 200, max: 500 },
transform: (parsed) => {
const originURL = parsed.protocol + '//' + parsed.hostname + parsed.path
parsed.host = WORKER_URL.split('https://')[1]
parsed.hostname = WORKER_URL.split('https://')[1]
parsed.path = '/?url=' + encodeURIComponent(originURL)
parsed.protocol = 'https:'
return parsed
},
},
}
const proxy = (url) => {
return `${WORKER_URL}/?url=` + encodeURIComponent(url)
}
const getAudioBuffer = async (audioInfo) => {
cleanMemory()
console.log('[info] fetching data')
await ffmpeg.writeFile('test.m4a', await fetchFile(proxy(audioInfo.url)))
await ffmpeg.exec(['-i', 'test.m4a', 'output.mp3'])
const data = await ffmpeg.readFile('output.mp3')
document.getElementById('track').src = URL.createObjectURL(
new Blob([(data as Uint8Array).buffer], { type: 'video/mp4' }),
)
await document.getElementById('track')?.play()
}
const getBestAudioBuffer = async (url) => {
const info = await getInfo(url)
console.log('[info] choosing format')
const audioInfo = ytdl.chooseFormat(info.formats, {
quality: 'highestaudio',
filter: 'audioonly',
})
return await getAudioBuffer(audioInfo)
}
const getInfo = async (url) => {
console.log('[info] getting info')
return await ytdl.getInfo(url, ytdlOptions)
}
;(async () => {
ffmpeg.on('log', ({ message }) => {
console.log(message)
})
ffmpeg.on('progress', ({ progress, time }) => {
console.log(progress)
})
await ffmpeg.load({
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
workerURL: await toBlobURL(
`${baseURL}/ffmpeg-core.worker.js`,
'text/javascript',
),
})
getBestAudioBuffer('https://www.youtube.com/watch?v=ui77nVbJZDw').then(
(i) => {
console.log(i)
},
)
})()
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const params = new URLSearchParams(url.search);
if (!params.has('url')) {
return new Response('400 Bad Request', { status: 400 });
}
const response = await fetch(decodeURIComponent(params.get('url')), {
headers: request.headers,
redirect: 'follow',
});
const { readable, writable } = new TransformStream();
response.body?.pipeTo(writable);
const newResponse = new Response(readable, response);
newResponse.headers.append('Access-Control-Allow-Origin', 'http://localhost:5173');
newResponse.headers.append('Access-Control-Allow-Headers', '*');
newResponse.headers.append('Access-Control-Allow-Methods', '*');
return newResponse;
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment