1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| import fs from "fs"; import readline from "readline"; import { google } from "googleapis"; import path from "path";
const CLIENT_ID = "你的 CLIENT_ID"; const CLIENT_SECRET = "你的 CLIENT_SECRET"; const REDIRECT_URI = "OAuth2 REDIRECT_URI"; const TOKEN_PATH = "token.json"; const PLAYLIST_ID = "播放清單ID";
const oauth2Client = new google.auth.OAuth2( CLIENT_ID, CLIENT_SECRET, REDIRECT_URI );
// 產生授權網址 function getAuthUrl() { const authUrl = oauth2Client.generateAuthUrl({ access_type: "offline", scope: [ "https://www.googleapis.com/auth/youtube.upload", "https://www.googleapis.com/auth/youtube" ], }); console.log("第一次使用請打開網址授權:", authUrl); }
// 儲存 token async function saveToken(code) { const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens)); console.log("已儲存 token.json,之後可自動上傳影片"); }
// 讀取 token function loadToken() { if (fs.existsSync(TOKEN_PATH)) { const token = JSON.parse(fs.readFileSync(TOKEN_PATH)); oauth2Client.setCredentials(token); return true; } return false; }
// 上傳影片並加入播放清單 // 假設 uploadVideo 改成接受 mp4 檔案路徑和 title async function uploadVideo(filePath, videoTitle) { const youtube = google.youtube({ version: "v3", auth: oauth2Client }); const fileSize = fs.statSync(filePath).size;
const res = await youtube.videos.insert( { part: "snippet,status", requestBody: { snippet: { title: videoTitle, // 使用輸入的 title //description: "這是測試上傳的影片", //tags: ["測試", "YouTube API"], //categoryId: "22", }, status: { privacyStatus: "unlisted", // 限定公開 selfDeclaredMadeForKids: false, // 非兒童影片 }, }, media: { body: fs.createReadStream(filePath), }, }, { onUploadProgress: (evt) => { const progress = (evt.bytesRead / fileSize) * 100; process.stdout.write(`\r上傳進度: ${progress.toFixed(2)}%`); }, } );
console.log("\n影片上傳成功! 影片ID:", res.data.id);
await youtube.playlistItems.insert({ part: "snippet", requestBody: { snippet: { playlistId: PLAYLIST_ID, resourceId: { kind: "youtube#video", videoId: res.data.id, }, }, }, });
console.log("影片已加入播放清單:", PLAYLIST_ID); }
// 主程式 (async () => { const folderPath = "./"; const files = fs.readdirSync(folderPath) .filter(file => path.extname(file).toLowerCase() === ".mp4");
if (files.length === 0) { throw new Error("資料夾裡沒有 mp4 檔案!"); } if (files.length > 1) { throw new Error("資料夾裡有多個 mp4 檔案,無法自動選擇!"); }
const mp4File = path.join(folderPath, files[0]); console.log("找到的 mp4 檔案:", mp4File);
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, });
rl.question("請輸入影片標題:", async (title) => { rl.close();
if (!loadToken()) { // 第一次授權 getAuthUrl();
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout, });
rl2.question("請貼上授權碼(code):", async (code) => { await saveToken(code); rl2.close(); await uploadVideo(mp4File, title); }); } else { // 已有 token,自動上傳 await uploadVideo(mp4File, title); } }); })();
|