Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 11 additions & 1 deletion lib/youtube-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
Expand All @@ -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,
},
},
Expand Down
Loading