11import ytdl from '@distube/ytdl-core' ;
22import { Writable } from 'stream' ;
33import ffmpeg from 'fluent-ffmpeg' ;
4- import ffmpegPath from 'ffmpeg-static' ;
5- import fs from 'fs' ;
6-
7- // Ensure ffmpeg binary is available – fallback to system ffmpeg if static build is missing
8- let resolvedFfmpegPath = ffmpegPath ;
9- try {
10- const exists = resolvedFfmpegPath && fs . existsSync ( resolvedFfmpegPath ) ;
11- console . log ( '[ffmpeg-debug] static path:' , resolvedFfmpegPath , 'exists:' , exists ) ;
12- if ( ! exists ) {
13- console . warn ( 'ffmpeg-static binary not found; falling back to system ffmpeg' ) ;
14- resolvedFfmpegPath = 'ffmpeg' ;
15- }
16- } catch ( err ) {
17- console . warn ( '[ffmpeg-debug] access check failed:' , err ) ;
18- resolvedFfmpegPath = 'ffmpeg' ;
19- }
204
21- ffmpeg . setFfmpegPath ( resolvedFfmpegPath ) ;
5+ // On Vercel serverless, ffmpeg-static postinstall scripts don't run,
6+ // so we always use the system ffmpeg binary.
7+ ffmpeg . setFfmpegPath ( 'ffmpeg' ) ;
228
239// Enhanced browser headers (same as info.js)
2410const USER_AGENTS = [
@@ -61,7 +47,7 @@ function randomDelay(min = 1000, max = 3000) {
6147// Enhanced getInfo with retry logic
6248async function getInfoWithRetry ( url , tries = 3 , delayMs = 2000 ) {
6349 await randomDelay ( 500 , 1500 ) ;
64-
50+
6551 try {
6652 const info = await ytdl . getInfo ( url , {
6753 requestOptions : {
@@ -72,30 +58,30 @@ async function getInfoWithRetry(url, tries = 3, delayMs = 2000) {
7258 quality : 'highestvideo' ,
7359 filter : 'audioandvideo' ,
7460 } ) ;
75-
61+
7662 return info ;
7763 } catch ( err ) {
7864 console . log ( `[ytdl-download] Attempt ${ 4 - tries } failed:` , err . message ) ;
79-
65+
8066 const status = typeof err === 'object' && err && ( 'statusCode' in err ? err . statusCode : err . status ) ;
81- const isRetryable =
82- status === 429 ||
83- status === 403 ||
84- status === 502 ||
85- status === 503 ||
67+ const isRetryable =
68+ status === 429 ||
69+ status === 403 ||
70+ status === 502 ||
71+ status === 503 ||
8672 status === 504 ||
8773 err . message ?. includes ( 'Sign in to confirm' ) ||
8874 err . message ?. includes ( 'bot' ) ||
8975 err . message ?. includes ( 'captcha' ) ||
9076 err . message ?. includes ( 'rate limit' ) ||
9177 err . message ?. includes ( 'timeout' ) ;
92-
78+
9379 if ( isRetryable && tries > 1 ) {
9480 console . log ( `[ytdl-download] Retrying in ${ delayMs } ms... (${ tries - 1 } attempts left)` ) ;
9581 await new Promise ( resolve => setTimeout ( resolve , delayMs ) ) ;
9682 return getInfoWithRetry ( url , tries - 1 , delayMs * 1.5 ) ;
9783 }
98-
84+
9985 throw err ;
10086 }
10187}
@@ -137,12 +123,12 @@ export default async function handler(req) {
137123 }
138124
139125 console . log ( '[ytdl-download] Processing download request for:' , url ) ;
140-
126+
141127 // Add delay before processing
142128 await randomDelay ( 500 , 1000 ) ;
143-
129+
144130 const info = await getInfoWithRetry ( url , 4 , 2000 ) ;
145-
131+
146132 if ( ! info || ! info . videoDetails ) {
147133 throw new Error ( 'Failed to fetch video information' ) ;
148134 }
@@ -228,7 +214,7 @@ export default async function handler(req) {
228214
229215 } catch ( err ) {
230216 console . error ( '[ytdl-download] Error:' , err . message ) ;
231-
217+
232218 // Return more user-friendly error messages
233219 let errorMessage = 'Failed to download video' ;
234220 if ( err . message ?. includes ( 'Video unavailable' ) ) {
@@ -240,8 +226,8 @@ export default async function handler(req) {
240226 } else if ( err . message ?. includes ( 'timeout' ) ) {
241227 errorMessage = 'Request timed out. Please try again.' ;
242228 }
243-
244- return new Response ( JSON . stringify ( {
229+
230+ return new Response ( JSON . stringify ( {
245231 error : errorMessage ,
246232 message : errorMessage
247233 } ) , {
0 commit comments