Update app.py
Browse files
app.py
CHANGED
|
@@ -15,6 +15,30 @@ def ffmpeg_cut_video(input_path, start_time, duration, output_path):
|
|
| 15 |
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| 16 |
return process.returncode == 0
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
def concat_videos_ffmpeg(file_list, output_path):
|
| 19 |
list_file = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.txt')
|
| 20 |
try:
|
|
@@ -60,7 +84,7 @@ def extract_clips_from_videos(video_files, clip_duration):
|
|
| 60 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
| 61 |
raise
|
| 62 |
|
| 63 |
-
def generate_mixed_videos(clips, num_output_videos):
|
| 64 |
temp_dir = tempfile.mkdtemp()
|
| 65 |
random.shuffle(clips)
|
| 66 |
clips_per_video = max(1, len(clips) // num_output_videos)
|
|
@@ -72,25 +96,31 @@ def generate_mixed_videos(clips, num_output_videos):
|
|
| 72 |
end_idx = len(clips) if i == num_output_videos - 1 else (start_idx + clips_per_video)
|
| 73 |
selected_clips = clips[start_idx:end_idx]
|
| 74 |
|
| 75 |
-
#
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
ok = concat_videos_ffmpeg(selected_clips, output_path)
|
| 80 |
if not ok:
|
| 81 |
raise RuntimeError("视频合并失败")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
output_files.append(output_path)
|
|
|
|
| 83 |
return output_files, temp_dir
|
| 84 |
except Exception:
|
| 85 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
| 86 |
raise
|
| 87 |
|
| 88 |
-
def create_video_package(output_files, original_file_names):
|
| 89 |
"""将生成的视频文件打包成zip,并创建说明文件"""
|
| 90 |
|
| 91 |
-
# 创建临时目录用于打包
|
| 92 |
package_dir = tempfile.mkdtemp()
|
| 93 |
-
zip_path = os.path.join(package_dir, f"混剪视频包_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip")
|
| 94 |
|
| 95 |
try:
|
| 96 |
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
@@ -105,8 +135,26 @@ def create_video_package(output_files, original_file_names):
|
|
| 105 |
## 📊 生成信息
|
| 106 |
- 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 107 |
- 视频数量: {len(output_files)} 个
|
|
|
|
| 108 |
- 源文件: {', '.join(original_file_names)}
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
## 📁 文件列表
|
| 111 |
"""
|
| 112 |
|
|
@@ -115,18 +163,39 @@ def create_video_package(output_files, original_file_names):
|
|
| 115 |
readme_content += f"- 混剪视频_{i}.mp4 ({file_size:.1f}MB)\n"
|
| 116 |
|
| 117 |
readme_content += f"""
|
| 118 |
-
## 🎬
|
| 119 |
-
-
|
| 120 |
-
-
|
| 121 |
-
-
|
|
|
|
| 122 |
|
| 123 |
-
##
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
---
|
| 129 |
-
感谢使用 FFmpeg 自动混剪工具!
|
| 130 |
"""
|
| 131 |
|
| 132 |
# 将说明文件添加到zip
|
|
@@ -138,7 +207,7 @@ def create_video_package(output_files, original_file_names):
|
|
| 138 |
shutil.rmtree(package_dir, ignore_errors=True)
|
| 139 |
raise e
|
| 140 |
|
| 141 |
-
def
|
| 142 |
if not video_files or len(video_files) == 0:
|
| 143 |
return "❌ 请上传至少一个视频文件", None, ""
|
| 144 |
|
|
@@ -148,27 +217,37 @@ def process_and_package(video_files, clip_duration, num_output_videos):
|
|
| 148 |
|
| 149 |
# 1. 视频处理
|
| 150 |
clips, clips_temp_dir = extract_clips_from_videos(video_files, clip_duration)
|
| 151 |
-
output_files, output_temp_dir = generate_mixed_videos(clips, num_output_videos)
|
| 152 |
|
| 153 |
# 清理切片临时目录
|
| 154 |
shutil.rmtree(clips_temp_dir, ignore_errors=True)
|
| 155 |
|
| 156 |
# 2. 打包所有视频
|
| 157 |
-
zip_file_path = create_video_package(output_files, original_names)
|
| 158 |
|
| 159 |
# 计算总大小
|
| 160 |
total_size = os.path.getsize(zip_file_path) / (1024 * 1024) # MB
|
| 161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
status_msg = f"""✅ 处理完成!
|
| 163 |
|
| 164 |
📊 **生成统计:**
|
| 165 |
• 混剪视频数量: {len(output_files)} 个
|
|
|
|
|
|
|
| 166 |
• 打包文件大小: {total_size:.1f}MB
|
| 167 |
• 处理时间: {datetime.now().strftime('%H:%M:%S')}
|
| 168 |
|
| 169 |
📦 **打包内容:**
|
| 170 |
-
• {len(output_files)}
|
| 171 |
-
• 1
|
|
|
|
| 172 |
• 所有文件已压缩打包
|
| 173 |
|
| 174 |
💾 **下载说明:**
|
|
@@ -176,7 +255,7 @@ def process_and_package(video_files, clip_duration, num_output_videos):
|
|
| 176 |
"""
|
| 177 |
|
| 178 |
# 生成详细信息
|
| 179 |
-
details = f"""🎬
|
| 180 |
|
| 181 |
"""
|
| 182 |
for i, video_file in enumerate(output_files, 1):
|
|
@@ -185,24 +264,43 @@ def process_and_package(video_files, clip_duration, num_output_videos):
|
|
| 185 |
|
| 186 |
details += f"""
|
| 187 |
🔧 **技术参数:**
|
|
|
|
| 188 |
• 切片时长: {clip_duration}秒
|
| 189 |
-
•
|
| 190 |
• 视频编码: H.264
|
| 191 |
• 音频编码: AAC
|
|
|
|
| 192 |
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
•
|
| 197 |
-
•
|
|
|
|
|
|
|
| 198 |
|
| 199 |
-
|
| 200 |
-
•
|
| 201 |
-
•
|
| 202 |
-
•
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
"""
|
| 204 |
|
| 205 |
-
#
|
| 206 |
shutil.rmtree(output_temp_dir, ignore_errors=True)
|
| 207 |
|
| 208 |
return status_msg, zip_file_path, details
|
|
@@ -212,14 +310,14 @@ def process_and_package(video_files, clip_duration, num_output_videos):
|
|
| 212 |
|
| 213 |
def main():
|
| 214 |
with gr.Blocks(
|
| 215 |
-
title="FFmpeg
|
| 216 |
theme=gr.themes.Soft()
|
| 217 |
) as demo:
|
| 218 |
|
| 219 |
gr.HTML("""
|
| 220 |
-
<div style="text-align: center; padding: 20px; background: linear-gradient(
|
| 221 |
-
<h1>🎬 FFmpeg 自动剪辑 + 打包下载</h1>
|
| 222 |
-
<p style="margin: 10px 0 0 0;">长视频自动切片 → 智能混剪 → 一键打包下载</p>
|
| 223 |
</div>
|
| 224 |
""")
|
| 225 |
|
|
@@ -246,6 +344,14 @@ def main():
|
|
| 246 |
maximum=8
|
| 247 |
)
|
| 248 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
process_btn = gr.Button(
|
| 250 |
"🎬 开始自动剪辑并打包",
|
| 251 |
variant="primary",
|
|
@@ -255,7 +361,7 @@ def main():
|
|
| 255 |
with gr.Column(scale=1):
|
| 256 |
status_output = gr.Textbox(
|
| 257 |
label="📊 处理状态",
|
| 258 |
-
lines=
|
| 259 |
interactive=False,
|
| 260 |
show_copy_button=True
|
| 261 |
)
|
|
@@ -270,14 +376,14 @@ def main():
|
|
| 270 |
with gr.Column(scale=1):
|
| 271 |
details_output = gr.Textbox(
|
| 272 |
label="📝 详细信息",
|
| 273 |
-
lines=
|
| 274 |
interactive=False,
|
| 275 |
show_copy_button=True
|
| 276 |
)
|
| 277 |
|
| 278 |
process_btn.click(
|
| 279 |
-
fn=
|
| 280 |
-
inputs=[video_input, clip_duration, num_output],
|
| 281 |
outputs=[status_output, download_file, details_output]
|
| 282 |
)
|
| 283 |
|
|
@@ -287,24 +393,34 @@ def main():
|
|
| 287 |
|
| 288 |
**🎬 视频处理流程:**
|
| 289 |
1. **上传视频** → 支持 MP4、MOV、AVI、MKV 格式
|
| 290 |
-
2.
|
| 291 |
-
3.
|
| 292 |
-
4.
|
| 293 |
-
5.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
|
| 295 |
**💾 打包内容:**
|
| 296 |
-
- ✅
|
| 297 |
- ✅ 详细的使用说明文档
|
| 298 |
-
- ✅
|
|
|
|
| 299 |
- ✅ 压缩打包,节省存储空间
|
| 300 |
|
| 301 |
-
**🚀 优势特点:**
|
| 302 |
-
- 🔥 **完全本地化** → 无需任何在线服务
|
| 303 |
-
- ⚡ **批量处理** → 支持多视频同时处理
|
| 304 |
-
- 🎯 **智能算法** → 随机重组避免重复
|
| 305 |
-
- 📱 **适配平台** → 生成适合短视频平台的内容
|
| 306 |
-
- 💾 **打包下载** → 一次下载全部文件
|
| 307 |
-
|
| 308 |
**⚠️ 系统要求:**
|
| 309 |
- 需要安装 FFmpeg 和 FFprobe
|
| 310 |
- 建议使用稳定的网络连接
|
|
|
|
| 15 |
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| 16 |
return process.returncode == 0
|
| 17 |
|
| 18 |
+
def ffmpeg_resize_video(input_path, output_path, target_ratio):
|
| 19 |
+
"""调整视频比例"""
|
| 20 |
+
if target_ratio == '9:16':
|
| 21 |
+
# 竖屏:高度保持,宽度=高度*9/16,居中裁剪
|
| 22 |
+
scale_filter = "scale=-2:1920,crop=1080:1920"
|
| 23 |
+
elif target_ratio == '16:9':
|
| 24 |
+
# 横屏:宽度保持,高度=宽度*9/16,居中裁剪
|
| 25 |
+
scale_filter = "scale=1920:-2,crop=1920:1080"
|
| 26 |
+
else:
|
| 27 |
+
# 默认保持原比例
|
| 28 |
+
scale_filter = "scale=-2:-2"
|
| 29 |
+
|
| 30 |
+
command = [
|
| 31 |
+
'ffmpeg',
|
| 32 |
+
'-i', input_path,
|
| 33 |
+
'-vf', scale_filter,
|
| 34 |
+
'-c:a', 'copy',
|
| 35 |
+
'-y',
|
| 36 |
+
output_path
|
| 37 |
+
]
|
| 38 |
+
|
| 39 |
+
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| 40 |
+
return process.returncode == 0
|
| 41 |
+
|
| 42 |
def concat_videos_ffmpeg(file_list, output_path):
|
| 43 |
list_file = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.txt')
|
| 44 |
try:
|
|
|
|
| 84 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
| 85 |
raise
|
| 86 |
|
| 87 |
+
def generate_mixed_videos(clips, num_output_videos, target_ratio):
|
| 88 |
temp_dir = tempfile.mkdtemp()
|
| 89 |
random.shuffle(clips)
|
| 90 |
clips_per_video = max(1, len(clips) // num_output_videos)
|
|
|
|
| 96 |
end_idx = len(clips) if i == num_output_videos - 1 else (start_idx + clips_per_video)
|
| 97 |
selected_clips = clips[start_idx:end_idx]
|
| 98 |
|
| 99 |
+
# 先合并片段
|
| 100 |
+
temp_video_path = os.path.join(temp_dir, f"temp_mixed_{i+1}.mp4")
|
| 101 |
+
ok = concat_videos_ffmpeg(selected_clips, temp_video_path)
|
|
|
|
|
|
|
| 102 |
if not ok:
|
| 103 |
raise RuntimeError("视频合并失败")
|
| 104 |
+
|
| 105 |
+
# 调整视频比例
|
| 106 |
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 107 |
+
output_path = os.path.join(temp_dir, f"混剪视频_{target_ratio.replace(':', 'x')}_{i+1}_{timestamp}.mp4")
|
| 108 |
+
ok = ffmpeg_resize_video(temp_video_path, output_path, target_ratio)
|
| 109 |
+
if not ok:
|
| 110 |
+
raise RuntimeError("视频比例调整失败")
|
| 111 |
+
|
| 112 |
output_files.append(output_path)
|
| 113 |
+
|
| 114 |
return output_files, temp_dir
|
| 115 |
except Exception:
|
| 116 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
| 117 |
raise
|
| 118 |
|
| 119 |
+
def create_video_package(output_files, original_file_names, target_ratio):
|
| 120 |
"""将生成的视频文件打包成zip,并创建说明文件"""
|
| 121 |
|
|
|
|
| 122 |
package_dir = tempfile.mkdtemp()
|
| 123 |
+
zip_path = os.path.join(package_dir, f"混剪视频包_{target_ratio.replace(':', 'x')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip")
|
| 124 |
|
| 125 |
try:
|
| 126 |
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
|
|
| 135 |
## 📊 生成信息
|
| 136 |
- 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 137 |
- 视频数量: {len(output_files)} 个
|
| 138 |
+
- 视频比例: {target_ratio}
|
| 139 |
- 源文件: {', '.join(original_file_names)}
|
| 140 |
|
| 141 |
+
## 📱 比例说明
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
if target_ratio == '9:16':
|
| 145 |
+
readme_content += """- 🔥 9:16 竖屏格式
|
| 146 |
+
- 📱 适合平台:抖音、快手、小红书、Instagram Stories
|
| 147 |
+
- 🎯 最佳用途:短视频、Vlog、才艺展示
|
| 148 |
+
- 📐 分辨率:1080x1920 (推荐)
|
| 149 |
+
"""
|
| 150 |
+
else:
|
| 151 |
+
readme_content += """- 🖥️ 16:9 横屏格式
|
| 152 |
+
- 📺 适合平台:YouTube、B站、腾讯视频、爱奇艺
|
| 153 |
+
- 🎯 最佳用途:长视频、教程、游戏、影视解说
|
| 154 |
+
- 📐 分辨率:1920x1080 (推荐)
|
| 155 |
+
"""
|
| 156 |
+
|
| 157 |
+
readme_content += f"""
|
| 158 |
## 📁 文件列表
|
| 159 |
"""
|
| 160 |
|
|
|
|
| 163 |
readme_content += f"- 混剪视频_{i}.mp4 ({file_size:.1f}MB)\n"
|
| 164 |
|
| 165 |
readme_content += f"""
|
| 166 |
+
## 🎬 制作技术
|
| 167 |
+
- 切片算法: FFmpeg 自动分割
|
| 168 |
+
- 混剪逻辑: 智能随机重组
|
| 169 |
+
- 比例调整: {target_ratio} 专业适配
|
| 170 |
+
- 视频编码: H.264/AAC 高质���
|
| 171 |
|
| 172 |
+
## 📱 发布建议
|
| 173 |
+
"""
|
| 174 |
+
|
| 175 |
+
if target_ratio == '9:16':
|
| 176 |
+
readme_content += """- 添加动感背景音乐
|
| 177 |
+
- 考虑添加字幕提升观看体验
|
| 178 |
+
- 开头3秒要有吸引力
|
| 179 |
+
- 时长控制在15-60秒最佳
|
| 180 |
+
- 发布时间:晚上7-9点黄金时段
|
| 181 |
+
"""
|
| 182 |
+
else:
|
| 183 |
+
readme_content += """- 制作精美封面图
|
| 184 |
+
- 添加详细标题和描述
|
| 185 |
+
- 考虑制作系列内容
|
| 186 |
+
- 时长可以3-15分钟
|
| 187 |
+
- 注意SEO关键词优化
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
readme_content += """
|
| 191 |
+
## 💡 后续处理
|
| 192 |
+
- 可用剪映、PR等软件进一步编辑
|
| 193 |
+
- 建议添加个人水印
|
| 194 |
+
- 支持再次调整分辨率
|
| 195 |
+
- 可添加转场特效和滤镜
|
| 196 |
|
| 197 |
---
|
| 198 |
+
🎬 感谢使用 FFmpeg 自动混剪工具!
|
| 199 |
"""
|
| 200 |
|
| 201 |
# 将说明文件添加到zip
|
|
|
|
| 207 |
shutil.rmtree(package_dir, ignore_errors=True)
|
| 208 |
raise e
|
| 209 |
|
| 210 |
+
def process_and_package_with_ratio(video_files, clip_duration, num_output_videos, target_ratio):
|
| 211 |
if not video_files or len(video_files) == 0:
|
| 212 |
return "❌ 请上传至少一个视频文件", None, ""
|
| 213 |
|
|
|
|
| 217 |
|
| 218 |
# 1. 视频处理
|
| 219 |
clips, clips_temp_dir = extract_clips_from_videos(video_files, clip_duration)
|
| 220 |
+
output_files, output_temp_dir = generate_mixed_videos(clips, num_output_videos, target_ratio)
|
| 221 |
|
| 222 |
# 清理切片临时目录
|
| 223 |
shutil.rmtree(clips_temp_dir, ignore_errors=True)
|
| 224 |
|
| 225 |
# 2. 打包所有视频
|
| 226 |
+
zip_file_path = create_video_package(output_files, original_names, target_ratio)
|
| 227 |
|
| 228 |
# 计算总大小
|
| 229 |
total_size = os.path.getsize(zip_file_path) / (1024 * 1024) # MB
|
| 230 |
|
| 231 |
+
# 平台信息
|
| 232 |
+
platform_info = ""
|
| 233 |
+
if target_ratio == '9:16':
|
| 234 |
+
platform_info = "📱 抖音、快手、小红书等短视频平台"
|
| 235 |
+
else:
|
| 236 |
+
platform_info = "🖥️ YouTube、B站等长视频平台"
|
| 237 |
+
|
| 238 |
status_msg = f"""✅ 处理完成!
|
| 239 |
|
| 240 |
📊 **生成统计:**
|
| 241 |
• 混剪视频数量: {len(output_files)} 个
|
| 242 |
+
• 视频比例: {target_ratio}
|
| 243 |
+
• 适合平台: {platform_info}
|
| 244 |
• 打包文件大小: {total_size:.1f}MB
|
| 245 |
• 处理时间: {datetime.now().strftime('%H:%M:%S')}
|
| 246 |
|
| 247 |
📦 **打包内容:**
|
| 248 |
+
• {len(output_files)} 个 {target_ratio} 比例混剪视频
|
| 249 |
+
• 1 个详细使用说明文档
|
| 250 |
+
• 平台发布建议和技术参数
|
| 251 |
• 所有文件已压缩打包
|
| 252 |
|
| 253 |
💾 **下载说明:**
|
|
|
|
| 255 |
"""
|
| 256 |
|
| 257 |
# 生成详细信息
|
| 258 |
+
details = f"""🎬 **视频详情 ({target_ratio}):**
|
| 259 |
|
| 260 |
"""
|
| 261 |
for i, video_file in enumerate(output_files, 1):
|
|
|
|
| 264 |
|
| 265 |
details += f"""
|
| 266 |
🔧 **技术参数:**
|
| 267 |
+
• 视频比例: {target_ratio}
|
| 268 |
• 切片时长: {clip_duration}秒
|
| 269 |
+
• 随机混剪: 智能重组算法
|
| 270 |
• 视频编码: H.264
|
| 271 |
• 音频编码: AAC
|
| 272 |
+
"""
|
| 273 |
|
| 274 |
+
if target_ratio == '9:16':
|
| 275 |
+
details += """
|
| 276 |
+
📱 **竖屏优势:**
|
| 277 |
+
• 符合移动端观看习惯
|
| 278 |
+
• 抖音算法友好
|
| 279 |
+
• 全屏沉浸式体验
|
| 280 |
+
• 适合碎片化观看
|
| 281 |
|
| 282 |
+
🎯 **发布建议:**
|
| 283 |
+
• 开头3秒要抓眼球
|
| 284 |
+
• 添加热门背景音乐
|
| 285 |
+
• 考虑添加字幕
|
| 286 |
+
• 时长15-60秒最佳
|
| 287 |
+
"""
|
| 288 |
+
else:
|
| 289 |
+
details += """
|
| 290 |
+
🖥️ **横屏优势:**
|
| 291 |
+
• 传统影视观看体验
|
| 292 |
+
• 适合详细内容展示
|
| 293 |
+
• YouTube推荐算法友好
|
| 294 |
+
• 便于添加复杂字幕
|
| 295 |
+
|
| 296 |
+
🎯 **发布建议:**
|
| 297 |
+
• 制作吸引人的封面
|
| 298 |
+
• 详细标题和描述
|
| 299 |
+
• 3-15分钟时长合适
|
| 300 |
+
• SEO关键词优化
|
| 301 |
"""
|
| 302 |
|
| 303 |
+
# 清理视频临时目录
|
| 304 |
shutil.rmtree(output_temp_dir, ignore_errors=True)
|
| 305 |
|
| 306 |
return status_msg, zip_file_path, details
|
|
|
|
| 310 |
|
| 311 |
def main():
|
| 312 |
with gr.Blocks(
|
| 313 |
+
title="FFmpeg自动剪辑+比例调整+打包下载",
|
| 314 |
theme=gr.themes.Soft()
|
| 315 |
) as demo:
|
| 316 |
|
| 317 |
gr.HTML("""
|
| 318 |
+
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white; margin-bottom: 20px;">
|
| 319 |
+
<h1>🎬 FFmpeg 自动剪辑 + 比例调整 + 打包下载</h1>
|
| 320 |
+
<p style="margin: 10px 0 0 0;">长视频自动切片 → 智能混剪 → 比例调整 → 一键打包下载</p>
|
| 321 |
</div>
|
| 322 |
""")
|
| 323 |
|
|
|
|
| 344 |
maximum=8
|
| 345 |
)
|
| 346 |
|
| 347 |
+
# 新增比例选择
|
| 348 |
+
ratio_selection = gr.Radio(
|
| 349 |
+
choices=["9:16", "16:9"],
|
| 350 |
+
value="9:16",
|
| 351 |
+
label="📐 选择视频比例",
|
| 352 |
+
info="9:16适合抖音快手,16:9适合YouTube B站"
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
process_btn = gr.Button(
|
| 356 |
"🎬 开始自动剪辑并打包",
|
| 357 |
variant="primary",
|
|
|
|
| 361 |
with gr.Column(scale=1):
|
| 362 |
status_output = gr.Textbox(
|
| 363 |
label="📊 处理状态",
|
| 364 |
+
lines=12,
|
| 365 |
interactive=False,
|
| 366 |
show_copy_button=True
|
| 367 |
)
|
|
|
|
| 376 |
with gr.Column(scale=1):
|
| 377 |
details_output = gr.Textbox(
|
| 378 |
label="📝 详细信息",
|
| 379 |
+
lines=18,
|
| 380 |
interactive=False,
|
| 381 |
show_copy_button=True
|
| 382 |
)
|
| 383 |
|
| 384 |
process_btn.click(
|
| 385 |
+
fn=process_and_package_with_ratio,
|
| 386 |
+
inputs=[video_input, clip_duration, num_output, ratio_selection],
|
| 387 |
outputs=[status_output, download_file, details_output]
|
| 388 |
)
|
| 389 |
|
|
|
|
| 393 |
|
| 394 |
**🎬 视频处理流程:**
|
| 395 |
1. **上传视频** → 支持 MP4、MOV、AVI、MKV 格式
|
| 396 |
+
2. **设置参数** → 切片时长、输出数量、视频比例
|
| 397 |
+
3. **自动切片** → 按指定时长将视频切成小段
|
| 398 |
+
4. **智能混剪** → 随机重组片段生成新视频
|
| 399 |
+
5. **比例调整** → 自动适配选择的目标比例
|
| 400 |
+
6. **自动打包** → 所有视频 + 说明文档打包成ZIP
|
| 401 |
+
7. **一键下载** → 保存到本地硬盘
|
| 402 |
+
|
| 403 |
+
**📐 比例选择指南:**
|
| 404 |
+
|
| 405 |
+
**🔥 9:16 竖屏 (推荐短视频)**
|
| 406 |
+
- 📱 **适合平台:** 抖音、快手、小红书、Instagram Stories
|
| 407 |
+
- 🎯 **内容类型:** 短视频、Vlog、才艺展示、产品介绍
|
| 408 |
+
- ⏱️ **最佳时长:** 15-60秒
|
| 409 |
+
- 🎵 **建议配乐:** 动感节拍强的背景音乐
|
| 410 |
+
|
| 411 |
+
**🖥️ 16:9 横屏 (推荐长视频)**
|
| 412 |
+
- 📺 **适合平台:** YouTube、B站、腾讯视频、爱奇艺
|
| 413 |
+
- 🎯 **内容类型:** 教程、游戏、影视解说、直播回放
|
| 414 |
+
- ⏱️ **最佳时长:** 3-15分钟
|
| 415 |
+
- 🖼️ **建议优化:** 精美封面、详细描述
|
| 416 |
|
| 417 |
**💾 打包内容:**
|
| 418 |
+
- ✅ 所有生成的混剪视频文件(指定比例)
|
| 419 |
- ✅ 详细的使用说明文档
|
| 420 |
+
- ✅ 平台发布建议和优化技巧
|
| 421 |
+
- ✅ 技术参数和后续处理指南
|
| 422 |
- ✅ 压缩打包,节省存储空间
|
| 423 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
**⚠️ 系统要求:**
|
| 425 |
- 需要安装 FFmpeg 和 FFprobe
|
| 426 |
- 建议使用稳定的网络连接
|