-
Notifications
You must be signed in to change notification settings - Fork 2
feat(auth): add Google OAuth provider and expand login options #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f1686c4
65966cc
6de1450
fd0ad7a
bb6a227
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,10 @@ | ||
| import ytdl from '@distube/ytdl-core'; | ||
| import { Writable } from 'stream'; | ||
| import ffmpeg from 'fluent-ffmpeg'; | ||
| import ffmpegPath from 'ffmpeg-static'; | ||
| import fs from 'fs'; | ||
|
|
||
| // Ensure ffmpeg binary is available – fallback to system ffmpeg if static build is missing | ||
| let resolvedFfmpegPath = ffmpegPath; | ||
| try { | ||
| const exists = resolvedFfmpegPath && fs.existsSync(resolvedFfmpegPath); | ||
| console.log('[ffmpeg-debug] static path:', resolvedFfmpegPath, 'exists:', exists); | ||
| if (!exists) { | ||
| console.warn('ffmpeg-static binary not found; falling back to system ffmpeg'); | ||
| resolvedFfmpegPath = 'ffmpeg'; | ||
| } | ||
| } catch (err) { | ||
| console.warn('[ffmpeg-debug] access check failed:', err); | ||
| resolvedFfmpegPath = 'ffmpeg'; | ||
| } | ||
|
|
||
| ffmpeg.setFfmpegPath(resolvedFfmpegPath); | ||
| // On Vercel serverless, ffmpeg-static postinstall scripts don't run, | ||
| // so we always use the system ffmpeg binary. | ||
| ffmpeg.setFfmpegPath('ffmpeg'); | ||
|
|
||
| // Enhanced browser headers (same as info.js) | ||
| const USER_AGENTS = [ | ||
|
|
@@ -61,7 +47,7 @@ function randomDelay(min = 1000, max = 3000) { | |
| // Enhanced getInfo with retry logic | ||
| async function getInfoWithRetry(url, tries = 3, delayMs = 2000) { | ||
| await randomDelay(500, 1500); | ||
|
|
||
| try { | ||
| const info = await ytdl.getInfo(url, { | ||
| requestOptions: { | ||
|
|
@@ -72,30 +58,30 @@ async function getInfoWithRetry(url, tries = 3, delayMs = 2000) { | |
| quality: 'highestvideo', | ||
| filter: 'audioandvideo', | ||
| }); | ||
|
|
||
| return info; | ||
| } catch (err) { | ||
| console.log(`[ytdl-download] Attempt ${4 - tries} failed:`, err.message); | ||
|
|
||
| const status = typeof err === 'object' && err && ('statusCode' in err ? err.statusCode : err.status); | ||
| const isRetryable = | ||
| status === 429 || | ||
| status === 403 || | ||
| status === 502 || | ||
| status === 503 || | ||
| const isRetryable = | ||
| status === 429 || | ||
| status === 403 || | ||
| status === 502 || | ||
| status === 503 || | ||
| status === 504 || | ||
| err.message?.includes('Sign in to confirm') || | ||
| err.message?.includes('bot') || | ||
| err.message?.includes('captcha') || | ||
| err.message?.includes('rate limit') || | ||
| err.message?.includes('timeout'); | ||
|
|
||
| if (isRetryable && tries > 1) { | ||
| console.log(`[ytdl-download] Retrying in ${delayMs}ms... (${tries - 1} attempts left)`); | ||
| await new Promise(resolve => setTimeout(resolve, delayMs)); | ||
| return getInfoWithRetry(url, tries - 1, delayMs * 1.5); | ||
| } | ||
|
|
||
| throw err; | ||
| } | ||
| } | ||
|
|
@@ -137,12 +123,12 @@ export default async function handler(req) { | |
| } | ||
|
|
||
| console.log('[ytdl-download] Processing download request for:', url); | ||
|
|
||
|
Comment on lines
125
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging raw user-provided URLs. This logs full query strings, which can leak user-specific tokens/identifiers into logs. Proposed fix- console.log('[ytdl-download] Processing download request for:', url);
+ let safeUrlForLog = url;
+ try {
+ const parsed = new URL(url);
+ parsed.search = '';
+ safeUrlForLog = parsed.toString();
+ } catch {}
+ console.log('[ytdl-download] Processing download request for:', safeUrlForLog);🤖 Prompt for AI Agents |
||
| // Add delay before processing | ||
| await randomDelay(500, 1000); | ||
|
|
||
| const info = await getInfoWithRetry(url, 4, 2000); | ||
|
|
||
|
Comment on lines
130
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Retry attempt logging is now inconsistent with 4 attempts.
Proposed fix-async function getInfoWithRetry(url, tries = 3, delayMs = 2000) {
+async function getInfoWithRetry(url, tries = 3, delayMs = 2000, maxTries = tries) {
@@
- console.log(`[ytdl-download] Attempt ${4 - tries} failed:`, err.message);
+ console.log(`[ytdl-download] Attempt ${maxTries - tries + 1} failed:`, err.message);
@@
- return getInfoWithRetry(url, tries - 1, delayMs * 1.5);
+ return getInfoWithRetry(url, tries - 1, delayMs * 1.5, maxTries);🤖 Prompt for AI Agents |
||
| if (!info || !info.videoDetails) { | ||
| throw new Error('Failed to fetch video information'); | ||
| } | ||
|
|
@@ -228,7 +214,7 @@ export default async function handler(req) { | |
|
|
||
| } catch (err) { | ||
| console.error('[ytdl-download] Error:', err.message); | ||
|
|
||
| // Return more user-friendly error messages | ||
| let errorMessage = 'Failed to download video'; | ||
| if (err.message?.includes('Video unavailable')) { | ||
|
|
@@ -240,8 +226,8 @@ export default async function handler(req) { | |
| } else if (err.message?.includes('timeout')) { | ||
| errorMessage = 'Request timed out. Please try again.'; | ||
| } | ||
| return new Response(JSON.stringify({ | ||
|
|
||
| return new Response(JSON.stringify({ | ||
| error: errorMessage, | ||
| message: errorMessage | ||
| }), { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Vercel Node.js serverless ffmpeg availability PATH 2026💡 Result:
On Vercel Node.js Serverless / Vercel Functions (Fluid compute) in 2026, FFmpeg is not something you can assume is preinstalled or on
PATH(so callingspawn("ffmpeg", ...)is not reliable). The supported approach is to bundle an FFmpeg binary with your function and execute it via an explicit path. [1]Working pattern (recommended):
ffmpeg-static.outputFileTracingIncludesto include./node_modules/ffmpeg-static/ffmpeg. [2]./node_modules/ffmpeg-static/ffmpeg, though you can also use the path exported byffmpeg-static). [3]Operational constraints to keep in mind:
/tmpwritable (up to 500 MB). [4]So, for “PATH availability”: treat it as not available; always ship the binary and invoke it by explicit path. [1][3]
Sources: [1] [2] [3] [4] [5]
🏁 Script executed:
Repository: creatorcluster/renderdragon.org
Length of output: 5083
🏁 Script executed:
# Check git history to understand the context of this change git log --oneline -n 20 -- api/download.jsRepository: creatorcluster/renderdragon.org
Length of output: 127
Revert to bundled ffmpeg-static or ffmpeg will not be available on Vercel.
The code hard-codes
ffmpeg.setFfmpegPath('ffmpeg')assuming system-level ffmpeg availability, but the comment's premise is flawed: ffmpeg-static dependency was removed and Vercel Node.js Serverless does not include ffmpeg in PATH by default. When users attempt merged downloads (video + audio), the ffmpeg command will fail at runtime with a generic error.Solution: Restore ffmpeg-static as a dependency and use the bundled binary path. Ensure the binary is included in function tracing (via
outputFileTracingIncludesin Vercel config if needed). Alternatively, if ffmpeg-static was intentionally removed, the merged download path must be disabled on Vercel.🤖 Prompt for AI Agents