Ryanus commited on
Commit
310da0f
·
verified ·
1 Parent(s): b9f2536

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -35
app.py CHANGED
@@ -1,6 +1,8 @@
1
  import os
2
  import gradio as gr
3
  import tempfile
 
 
4
  from datetime import datetime
5
 
6
  try:
@@ -37,7 +39,7 @@ class YouTubeUploader:
37
  self.client_secrets_file = 'client_secrets.json'
38
  self.credentials_file = 'youtube_token.pickle'
39
  self.youtube = None
40
-
41
  def authenticate(self):
42
  creds = None
43
  if os.path.exists(self.credentials_file):
@@ -50,16 +52,17 @@ class YouTubeUploader:
50
  flow = InstalledAppFlow.from_client_secrets_file(
51
  self.client_secrets_file, self.SCOPES
52
  )
53
- creds = flow.run_console()
 
54
  with open(self.credentials_file, 'wb') as token:
55
  pickle.dump(creds, token)
56
  self.youtube = build('youtube', 'v3', credentials=creds)
57
  return True
58
-
59
  def upload_video(self, video_path, title, description="", tags="", privacy="private"):
60
  if not os.path.exists(video_path):
61
  return {"success": False, "error": f"视频文件 {video_path} 不存在"}
62
-
63
  body = {
64
  "snippet": {
65
  "title": title,
@@ -72,10 +75,10 @@ class YouTubeUploader:
72
  "selfDeclaredMadeForKids": False
73
  }
74
  }
75
-
76
  media = MediaFileUpload(video_path, chunksize=-1, resumable=True, mimetype="video/*")
77
  request = self.youtube.videos().insert(part=",".join(body.keys()), body=body, media_body=media)
78
-
79
  response = request.execute()
80
  video_id = response.get("id")
81
  url = f"https://www.youtube.com/watch?v={video_id}"
@@ -101,27 +104,68 @@ def process_videos(video_files, audio_file, clip_duration, num_output):
101
  ok, msg = check_filesize(video_files, audio_file)
102
  if not ok:
103
  return msg, [], ""
104
-
105
- # 真实视频切片、混剪应在这里实现
106
- # 本处仅返回成功信息,结合你已有处理逻辑即可
107
- return f"成功处理 {len(video_files)} 个视频,生成 {num_output} 个混剪视频", [], ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  def generate_ai_analysis(video_count, file_names):
110
  if not OPENAI_AVAILABLE or video_count == 0:
111
- return "⚠️ AI 分析暂不可用或无视频文件。"
112
  try:
113
  file_info = ", ".join([f"'{name}'" for name in file_names[:3]])
114
- prompt = f"""作为专业视频分析师,基于用户上传的 {video_count} 个视频文件(文件名包括:{file_info} 等),请提供详细分析:
115
- 1. 内容类型推测和特点
116
- 2. 最佳背景音乐风格和节拍
117
- 3. 推荐剪辑策略和转场效果
118
- 4. 目标受众分析
119
 
120
- 请用中文回答,格式清晰。"""
121
  response = client.chat.completions.create(
122
  model="gpt-4o-mini",
123
  messages=[
124
- {"role": "system", "content": "你是专业的视频内容分析专家。"},
125
  {"role": "user", "content": prompt}
126
  ],
127
  max_tokens=600,
@@ -129,28 +173,27 @@ def generate_ai_analysis(video_count, file_names):
129
  )
130
  return response.choices[0].message.content
131
  except Exception as e:
132
- return f"❌ AI 分析失败:{str(e)}"
133
 
134
  def main_app(video_files, audio_file, clip_duration, num_output, upload_yt, yt_privacy):
135
  status, outfiles, _ = process_videos(video_files, audio_file, clip_duration, num_output)
136
-
137
  file_names = [os.path.basename(f.name) for f in (video_files or [])]
138
  ai_analysis = generate_ai_analysis(len(video_files or []), file_names)
139
-
140
  yt_result = ""
141
  if upload_yt:
142
  if not YOUTUBE_AVAILABLE:
143
- yt_result = "⚠️ 未安装 YouTube API 库,上传功能不可用"
144
  elif not os.path.exists("client_secrets.json"):
145
- yt_result = "⚠️ client_secrets.json 文件缺失,无法上传"
146
  else:
147
  uploader = YouTubeUploader()
148
- authed = uploader.authenticate()
149
- if not authed:
150
- yt_result = "❌ YouTube 认证失败"
151
  else:
152
- yt_result = "请配合实际生成的视频文件路径调用上传函数"
153
-
154
  return status, ai_analysis, yt_result
155
 
156
  try:
@@ -160,20 +203,20 @@ except ImportError:
160
  YOUTUBE_AVAILABLE = False
161
 
162
  with gr.Blocks() as demo:
163
- gr.Markdown("## pyTovideo2 视频处理+YouTube 上传 (实用版)")
164
-
165
  video_files = gr.File(file_types=[".mp4", ".mov", ".avi"], label="上传视频文件", file_count="multiple")
166
  audio_file = gr.File(file_types=[".mp3", ".wav", ".m4a"], label="上传音频文件")
167
  clip_duration = gr.Number(value=2, label="切片时长 (秒)", minimum=1, maximum=10)
168
  num_output = gr.Number(value=3, label="输出视频数量", minimum=1, maximum=10)
169
- upload_yt = gr.Checkbox(label="处理后上传至 YouTube")
170
- yt_privacy = gr.Dropdown(choices=["public", "private", "unlisted"], value="private", label="YouTube 视频隐私")
171
  btn = gr.Button("开始处理")
172
-
173
  status_out = gr.Textbox(label="处理状态", lines=4, interactive=False)
174
  ai_out = gr.Textbox(label="AI 内容分析", lines=12)
175
- yt_out = gr.Textbox(label="YouTube 上传结果", lines=6)
176
-
177
  btn.click(
178
  main_app,
179
  inputs=[video_files, audio_file, clip_duration, num_output, upload_yt, yt_privacy],
 
1
  import os
2
  import gradio as gr
3
  import tempfile
4
+ import random
5
+ import shutil
6
  from datetime import datetime
7
 
8
  try:
 
39
  self.client_secrets_file = 'client_secrets.json'
40
  self.credentials_file = 'youtube_token.pickle'
41
  self.youtube = None
42
+
43
  def authenticate(self):
44
  creds = None
45
  if os.path.exists(self.credentials_file):
 
52
  flow = InstalledAppFlow.from_client_secrets_file(
53
  self.client_secrets_file, self.SCOPES
54
  )
55
+ creds = flow.run_local_server(port=0, open_browser=False)
56
+ print("请复制下面的链接到浏览器打开并完成授权")
57
  with open(self.credentials_file, 'wb') as token:
58
  pickle.dump(creds, token)
59
  self.youtube = build('youtube', 'v3', credentials=creds)
60
  return True
61
+
62
  def upload_video(self, video_path, title, description="", tags="", privacy="private"):
63
  if not os.path.exists(video_path):
64
  return {"success": False, "error": f"视频文件 {video_path} 不存在"}
65
+
66
  body = {
67
  "snippet": {
68
  "title": title,
 
75
  "selfDeclaredMadeForKids": False
76
  }
77
  }
78
+
79
  media = MediaFileUpload(video_path, chunksize=-1, resumable=True, mimetype="video/*")
80
  request = self.youtube.videos().insert(part=",".join(body.keys()), body=body, media_body=media)
81
+
82
  response = request.execute()
83
  video_id = response.get("id")
84
  url = f"https://www.youtube.com/watch?v={video_id}"
 
104
  ok, msg = check_filesize(video_files, audio_file)
105
  if not ok:
106
  return msg, [], ""
107
+
108
+ out_files = []
109
+ try:
110
+ clips = []
111
+ for video in video_files:
112
+ clip = VideoFileClip(video.name)
113
+ duration = clip.duration
114
+
115
+ for start in range(0, int(duration), int(clip_duration)):
116
+ end = min(start + clip_duration, duration)
117
+ subclip = clip.subclip(start, end).without_audio()
118
+ clips.append(subclip)
119
+
120
+ random.shuffle(clips)
121
+
122
+ clips_per_video = max(1, len(clips) // num_output)
123
+ output_dir = tempfile.mkdtemp()
124
+
125
+ for i in range(num_output):
126
+ start_idx = i * clips_per_video
127
+ end_idx = len(clips) if i == num_output - 1 else (start_idx + clips_per_video)
128
+ selected_clips = clips[start_idx:end_idx]
129
+ if not selected_clips:
130
+ continue
131
+
132
+ final_clip = concatenate_videoclips(selected_clips)
133
+
134
+ if audio_file:
135
+ audio_clip = AudioFileClip(audio_file.name)
136
+ min_duration = min(final_clip.duration, audio_clip.duration)
137
+ final_clip = final_clip.subclip(0, min_duration)
138
+ audio_clip = audio_clip.subclip(0, min_duration)
139
+ final_clip = final_clip.set_audio(audio_clip)
140
+
141
+ output_path = os.path.join(output_dir, f"mixcut_{i+1}.mp4")
142
+ final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", verbose=False, logger=None)
143
+ out_files.append(output_path)
144
+ final_clip.close()
145
+
146
+ shutil.rmtree(output_dir, ignore_errors=True) # 可选,处理完删除临时文件
147
+
148
+ return f"成功生成 {len(out_files)} 个混剪视频", out_files, output_dir
149
+
150
+ except Exception as e:
151
+ return f"❌ 视频处理出错: {str(e)}", [], ""
152
 
153
  def generate_ai_analysis(video_count, file_names):
154
  if not OPENAI_AVAILABLE or video_count == 0:
155
+ return "⚠️ 无视频文件或 AI 配置不可用。"
156
  try:
157
  file_info = ", ".join([f"'{name}'" for name in file_names[:3]])
158
+ prompt = f"""作为专业视频分析师,基于用户上传的 {video_count} 个视频文件(包括:{file_info} 等),请提供分析:
159
+ 1. 内容类型推测
160
+ 2. 背景音乐建议
161
+ 3. 剪辑策略
162
+ 4. 目标用户
163
 
164
+ 用中文回答,格式清晰。"""
165
  response = client.chat.completions.create(
166
  model="gpt-4o-mini",
167
  messages=[
168
+ {"role": "system", "content": "你是视频内容专家。"},
169
  {"role": "user", "content": prompt}
170
  ],
171
  max_tokens=600,
 
173
  )
174
  return response.choices[0].message.content
175
  except Exception as e:
176
+ return f"❌ AI 分析失败: {str(e)}"
177
 
178
  def main_app(video_files, audio_file, clip_duration, num_output, upload_yt, yt_privacy):
179
  status, outfiles, _ = process_videos(video_files, audio_file, clip_duration, num_output)
180
+
181
  file_names = [os.path.basename(f.name) for f in (video_files or [])]
182
  ai_analysis = generate_ai_analysis(len(video_files or []), file_names)
183
+
184
  yt_result = ""
185
  if upload_yt:
186
  if not YOUTUBE_AVAILABLE:
187
+ yt_result = "⚠️ 未安装 YouTube API "
188
  elif not os.path.exists("client_secrets.json"):
189
+ yt_result = "⚠️ 缺少 client_secrets.json 文件"
190
  else:
191
  uploader = YouTubeUploader()
192
+ if uploader.authenticate():
193
+ yt_result = "认证成功,上传功能请手动调用上传接口。"
 
194
  else:
195
+ yt_result = "❌ 认证失败"
196
+
197
  return status, ai_analysis, yt_result
198
 
199
  try:
 
203
  YOUTUBE_AVAILABLE = False
204
 
205
  with gr.Blocks() as demo:
206
+ gr.Markdown("## pyTovideo2 完整视频切割混剪 + YouTube上传(需预先配置)")
207
+
208
  video_files = gr.File(file_types=[".mp4", ".mov", ".avi"], label="上传视频文件", file_count="multiple")
209
  audio_file = gr.File(file_types=[".mp3", ".wav", ".m4a"], label="上传音频文件")
210
  clip_duration = gr.Number(value=2, label="切片时长 (秒)", minimum=1, maximum=10)
211
  num_output = gr.Number(value=3, label="输出视频数量", minimum=1, maximum=10)
212
+ upload_yt = gr.Checkbox(label="处理后上传到 YouTube")
213
+ yt_privacy = gr.Dropdown(choices=["public", "private", "unlisted"], value="private", label="选择 YouTube 视频隐私")
214
  btn = gr.Button("开始处理")
215
+
216
  status_out = gr.Textbox(label="处理状态", lines=4, interactive=False)
217
  ai_out = gr.Textbox(label="AI 内容分析", lines=12)
218
+ yt_out = gr.Textbox(label="YouTube 上传状态", lines=6)
219
+
220
  btn.click(
221
  main_app,
222
  inputs=[video_files, audio_file, clip_duration, num_output, upload_yt, yt_privacy],