Ryanus commited on
Commit
30082cf
·
verified ·
1 Parent(s): 9e5ca8f

Create youtube_uploader.py

Browse files
Files changed (1) hide show
  1. youtube_uploader.py +232 -0
youtube_uploader.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+ import pickle
6
+ import json
7
+ from googleapiclient.discovery import build
8
+ from googleapiclient.http import MediaFileUpload
9
+ from google_auth_oauthlib.flow import InstalledAppFlow
10
+ from google.auth.transport.requests import Request
11
+
12
+ class PyToVideo2YouTubeUploader:
13
+ def __init__(self):
14
+ self.SCOPES = ['https://www.googleapis.com/auth/youtube.upload']
15
+ self.CLIENT_SECRETS_FILE = 'client_secrets.json'
16
+ self.CREDENTIALS_FILE = 'youtube_token.pickle'
17
+ self.youtube = None
18
+
19
+ def authenticate(self):
20
+ """進行 YouTube 認證"""
21
+ credentials = None
22
+
23
+ # 檢查已保存的認證
24
+ if os.path.exists(self.CREDENTIALS_FILE):
25
+ print("🔍 找到已保存的認證...")
26
+ with open(self.CREDENTIALS_FILE, 'rb') as token:
27
+ credentials = pickle.load(token)
28
+
29
+ # 認證流程
30
+ if not credentials or not credentials.valid:
31
+ if credentials and credentials.expired and credentials.refresh_token:
32
+ print("🔄 刷新認證...")
33
+ credentials.refresh(Request())
34
+ else:
35
+ print("🚀 開始首次認證...")
36
+ print("📌 瀏覽器將自動打開,請登入你的 Google 帳戶並授權")
37
+
38
+ flow = InstalledAppFlow.from_client_secrets_file(
39
+ self.CLIENT_SECRETS_FILE,
40
+ self.SCOPES
41
+ )
42
+
43
+ # ⚠️ 重要:使用正確的重定向設置
44
+ credentials = flow.run_local_server(
45
+ port=8080,
46
+ prompt='consent',
47
+ authorization_prompt_message='正在等待授權...'
48
+ )
49
+
50
+ # 保存認證
51
+ with open(self.CREDENTIALS_FILE, 'wb') as token:
52
+ pickle.dump(credentials, token)
53
+ print("💾 認證已保存")
54
+
55
+ # 建立 YouTube 服務
56
+ self.youtube = build('youtube', 'v3', credentials=credentials)
57
+ print("✅ YouTube API 連接成功!")
58
+ return True
59
+
60
+ def upload_video(self, video_path, title, description="", tags="", privacy="private"):
61
+ """上傳視頻到 YouTube"""
62
+
63
+ if not self.youtube:
64
+ print("❌ 請先完成認證")
65
+ return None
66
+
67
+ if not os.path.exists(video_path):
68
+ print(f"❌ 找不到視頻文件: {video_path}")
69
+ return None
70
+
71
+ # 視頻元數據
72
+ body = {
73
+ 'snippet': {
74
+ 'title': title,
75
+ 'description': description,
76
+ 'tags': tags.split(',') if tags else [],
77
+ 'categoryId': '22' # People & Blogs
78
+ },
79
+ 'status': {
80
+ 'privacyStatus': privacy,
81
+ 'selfDeclaredMadeForKids': False
82
+ }
83
+ }
84
+
85
+ # 開始上傳
86
+ print(f"\n📤 開始上傳: {os.path.basename(video_path)}")
87
+ print(f"📝 標題: {title}")
88
+ print(f"🔒 隱私: {privacy}")
89
+
90
+ media = MediaFileUpload(
91
+ video_path,
92
+ chunksize=-1,
93
+ resumable=True,
94
+ mimetype='video/*'
95
+ )
96
+
97
+ try:
98
+ request = self.youtube.videos().insert(
99
+ part=','.join(body.keys()),
100
+ body=body,
101
+ media_body=media
102
+ )
103
+
104
+ response = request.execute()
105
+ video_id = response['id']
106
+ url = f"https://www.youtube.com/watch?v={video_id}"
107
+
108
+ print(f"✅ 上傳成功!")
109
+ print(f"🆔 視頻ID: {video_id}")
110
+ print(f"🔗 YouTube連結: {url}")
111
+
112
+ return {
113
+ 'success': True,
114
+ 'video_id': video_id,
115
+ 'url': url,
116
+ 'title': title
117
+ }
118
+
119
+ except Exception as e:
120
+ print(f"❌ 上傳失敗: {str(e)}")
121
+ return {'success': False, 'error': str(e)}
122
+
123
+ def main():
124
+ """主程序 - pyTovideo2 YouTube 上傳工具"""
125
+
126
+ print("🎬 pyTovideo2 YouTube 上傳工具")
127
+ print("=" * 40)
128
+
129
+ # 檢查必要文件
130
+ if not os.path.exists('client_secrets.json'):
131
+ print("❌ 找不到 client_secrets.json")
132
+ print("請將 Google Cloud 下載的憑據文件保存為 client_secrets.json")
133
+ return
134
+
135
+ # 創建上傳器並認證
136
+ uploader = PyToVideo2YouTubeUploader()
137
+
138
+ if not uploader.authenticate():
139
+ print("❌ 認證失敗")
140
+ return
141
+
142
+ # 定義要上傳的視頻
143
+ videos_to_upload = [
144
+ {
145
+ "path": "混剪視頻_01.mp4",
146
+ "title": "pyTovideo2 混剪作品 #1 - 精彩合集",
147
+ "description": """🎬 使用 pyTovideo2 工具製作的��彩視頻混剪
148
+
149
+ 🔧 製作特色:
150
+ • 智能切片分割
151
+ • 音樂節拍同步
152
+ • 隨機重組排列
153
+ • 鏡像防重複檢測
154
+
155
+ 💡 工具優勢:
156
+ • 批量處理效率高
157
+ • 支持多格式輸入
158
+ • 一鍵生成多個版本
159
+ • 適合短視頻平台
160
+
161
+ 👍 如果覺得有用請:
162
+ • 點贊支持這個項目
163
+ • 訂閱獲取更多內容
164
+ • 分享給需要的朋友
165
+
166
+ 🔗 開源項目: https://github.com/BassZou/pyToVideo2
167
+
168
+ #短視頻 #混剪 #Python #自動化 #pyTovideo2""",
169
+ "tags": "短視頻,混剪,Python,自動化,pyTovideo2,視頻製作",
170
+ "privacy": "private" # 建議先設為 private 測試
171
+ },
172
+
173
+ {
174
+ "path": "混剪視頻_02.mp4",
175
+ "title": "pyTovideo2 混剪作品 #2 - 創意集錦",
176
+ "description": """🎨 第二個混剪作品,展示更多創意可能性
177
+
178
+ ✨ 本期亮點:
179
+ • 不同風格片段混合
180
+ • 動態節奏變化
181
+ • 視覺衝擊力強
182
+ • 觀看體驗流暢
183
+
184
+ 🎯 適用場景:
185
+ • 抖音快手短視頻
186
+ • 小紅書內容創作
187
+ • B站 UP主素材
188
+ • YouTube頻道內容
189
+
190
+ 💬 評論告訴我你最喜歡哪個片段!
191
+
192
+ #創意視頻 #內容創作 #視頻剪輯""",
193
+ "tags": "創意視頻,內容創作,視頻剪輯,混剪",
194
+ "privacy": "private"
195
+ }
196
+ ]
197
+
198
+ # 批量上傳
199
+ print(f"\n🚀 準備上傳 {len(videos_to_upload)} 個視頻...")
200
+ results = []
201
+
202
+ for i, video_info in enumerate(videos_to_upload, 1):
203
+ print(f"\n--- 處理第 {i}/{len(videos_to_upload)} 個視頻 ---")
204
+
205
+ result = uploader.upload_video(
206
+ video_path=video_info['path'],
207
+ title=video_info['title'],
208
+ description=video_info['description'],
209
+ tags=video_info['tags'],
210
+ privacy=video_info['privacy']
211
+ )
212
+
213
+ results.append(result)
214
+
215
+ if result and result.get('success'):
216
+ print(f"✅ 第 {i} 個視頻上傳成功")
217
+ else:
218
+ print(f"❌ 第 {i} 個視頻上傳失敗")
219
+
220
+ # 最終報告
221
+ successful = sum(1 for r in results if r and r.get('success'))
222
+ print(f"\n📊 上傳完成統計:")
223
+ print(f"✅ 成功: {successful}/{len(videos_to_upload)}")
224
+ print(f"❌ 失敗: {len(videos_to_upload) - successful}/{len(videos_to_upload)}")
225
+
226
+ print(f"\n🎉 所有視頻處理完成!")
227
+ for i, result in enumerate(results, 1):
228
+ if result and result.get('success'):
229
+ print(f"✅ 視頻 {i}: {result['url']}")
230
+
231
+ if __name__ == "__main__":
232
+ main()