diff --git a/.env.example b/.env.example index 634ff9c2..24c919a3 100644 --- a/.env.example +++ b/.env.example @@ -56,6 +56,7 @@ CRON_SECRET= # Secret for authenticating cron job re # YouTube integration YOUTUBE_API_KEY= # YouTube Data API v3 key YOUTUBE_CHANNEL_ID= # YouTube channel ID to fetch videos from +YOUTUBE_UPLOAD_VISIBILITY= # YouTube upload privacy: "public", "private", or "unlisted" (default: "private") # Vercel VERCEL_PROJECT_PRODUCTION_URL= # Production URL (auto-set by Vercel) diff --git a/lib/youtube-upload.ts b/lib/youtube-upload.ts index 722e7084..6c7954d1 100644 --- a/lib/youtube-upload.ts +++ b/lib/youtube-upload.ts @@ -11,6 +11,14 @@ oauth2Client.setCredentials({ const youtube = google.youtube({ version: "v3", auth: oauth2Client }); +function getDefaultPrivacyStatus(): "public" | "private" | "unlisted" { + const envValue = process.env.YOUTUBE_UPLOAD_VISIBILITY?.toLowerCase(); + if (envValue === "public" || envValue === "private" || envValue === "unlisted") { + return envValue; + } + return "private"; // Default to private when not set +} + interface UploadOptions { title: string; description: string; @@ -28,6 +36,8 @@ export async function uploadVideo(opts: UploadOptions): Promise<{ videoId: strin const response = await fetch(opts.videoUrl); if (!response.ok) throw new Error(`Failed to fetch video: ${response.statusText}`); + const resolvedPrivacyStatus = opts.privacyStatus || getDefaultPrivacyStatus(); + console.log(`[youtube-upload] Uploading "${opts.title.slice(0, 60)}" with privacy: ${resolvedPrivacyStatus}`); // Convert Web ReadableStream to Node.js Readable stream // googleapis expects a Node.js stream with .pipe(), not a Web ReadableStream const buffer = Buffer.from(await response.arrayBuffer()); @@ -44,7 +54,7 @@ export async function uploadVideo(opts: UploadOptions): Promise<{ videoId: strin defaultLanguage: "en", }, status: { - privacyStatus: opts.privacyStatus || "public", + privacyStatus: resolvedPrivacyStatus, selfDeclaredMadeForKids: opts.madeForKids ?? false, }, },