Docker化Python应用用FFmpeg修剪YouTube视频耗时过长问题排查
YouTube视频修剪Flask后端响应缓慢问题
问题概述
本项目用于修剪YouTube视频,终端直接执行FFmpeg命令时速度正常,但通过Flask后端处理并返回修剪后的视频给前端时响应极慢——修剪10分钟时长的视频约需5分钟才能返回结果,无法定位问题根源。
相关代码
后端main.py
import os import subprocess from flask import Flask, request, send_file from flask_cors import CORS, cross_origin app = Flask(__name__) cors = CORS(app) current_directory = os.getcwd() folder_name = "youtube_videos" save_path = os.path.join(current_directory, folder_name) output_file_path = os.path.join(save_path, 'video.mp4') os.makedirs(save_path, exist_ok=True) def convert_time_seconds(time_str): hours, minutes, seconds = map(int, time_str.split(':')) total_seconds = (hours * 3600) + (minutes * 60) + seconds return total_seconds def convert_seconds_time(total_seconds): new_hours = total_seconds // 3600 total_seconds %= 3600 new_minutes = total_seconds // 60 new_seconds = total_seconds % 60 new_time_str = f'{new_hours:02}:{new_minutes:02}:{new_seconds:02}' return new_time_str def add_seconds_to_time(time_str, seconds_to_add): total_seconds = convert_time_seconds(time_str) total_seconds -= seconds_to_add new_time_str = convert_seconds_time(total_seconds) return new_time_str def get_length(start_time, end_time): start_time_seconds = convert_time_seconds(start_time) end_time_seconds = convert_time_seconds(end_time) length = end_time_seconds - start_time_seconds length_str = convert_seconds_time(length) return length_str def download_url(url): command = [ "yt-dlp", "-g", url ] try: links = subprocess.run(command, capture_output=True, text=True, check=True) video, audio = links.stdout.strip().split("\n") return video, audio except subprocess.CalledProcessError as e: print(f"Command failed with return code {e.returncode}.") print(f"Error output: {e.stderr}") return None except ValueError: print("Error: Could not parse video and audio links.") return None def download_trimmed_video(video_link, audio_link, start_time, end_time): new_start_time = add_seconds_to_time(start_time, 30) new_end_time = get_length(start_time, end_time) if os.path.exists(output_file_path): os.remove(output_file_path) command = [ 'ffmpeg', '-ss', new_start_time + '.00', '-i', video_link, '-ss', new_start_time + '.00', '-i', audio_link, '-map', '0:v', '-map', '1:a', '-ss', '30', '-t', new_end_time + '.00', '-c:v', 'libx264', '-c:a', 'aac', output_file_path ] try: result = subprocess.run(command, capture_output=True, text=True, check=True) if result.returncode == 0: return "Trimmed video downloaded successfully!" else: return "Error occurred while downloading trimmed video" except subprocess.CalledProcessError as e: print(f"Command failed with return code {e.returncode}.") print(f"Error output: {e.stderr}") @app.route('/trimvideo', methods =["POST"]) @cross_origin() def trim_video(): print("here") data = request.get_json() video_link, audio_link = download_url(data["url"]) if video_link and audio_link: print("Downloading trimmed video...") download_trimmed_video(video_link, audio_link, data["start_time"], data["end_time"]) response = send_file(output_file_path, as_attachment=True, download_name='video.mp4') response.status_code = 200 return response else: return "Error downloading video", 400 if __name__ == '__main__': app.run(debug=True, port=5000, host='0.0.0.0')
Dockerfile
FROM ubuntu:latest # Update the package list and install wget and ffmpeg RUN apt-get update \ && apt-get install -y wget ffmpeg python3 python3-pip \ && rm -rf /var/lib/apt/lists/* # Download the latest version of yt-dlp and install it RUN wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp \ && chmod a+rx /usr/local/bin/yt-dlp WORKDIR /app COPY main.py /app/ COPY requirements.txt /app/ RUN pip install --no-cache-dir -r requirements.txt # Set the default command CMD ["python3", "main.py"]
requirements.txt
blinker==1.7.0 click==8.1.7 colorama==0.4.6 Flask==3.0.3 Flask-Cors==4.0.0 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.5 Werkzeug==3.0.2
前端App.js
import React, { useState } from 'react'; import './App.css'; import axios from 'axios'; async function handleSubmit(event, url, start_time, end_time, setVideoUrl, setIsSubmitted){ event.preventDefault(); if( url && start_time && end_time){ try { setIsSubmitted(true); const response = await axios.post('http://127.0.0.1:5000/trimvideo', { url: url, start_time: start_time, end_time: end_time }, { responseType: 'blob', headers: {'Content-Type': 'application/json'} } ) const blob = new Blob([response.data], { type: 'video/mp4' }); const newurl = URL.createObjectURL(blob); setVideoUrl(newurl); } catch (error) { console.error('Error trimming video:', error); } } else { alert('Please fill all the fields'); } } function App() { const [url, setUrl] = useState(''); const [startTime, setStartTime] = useState(''); const [endTime, setEndTime] = useState(''); const [videoUrl, setVideoUrl] = useState(''); const [isSubmitted, setIsSubmitted] = useState(false); return ( <div className="App"> <div className="app-header">TRIM AND DOWNLOAD YOUR YOUTUBE VIDEO HERE</div> <input className = "input-url"placeholder='Enter url' value={url} onChange={e=>setUrl(e.target.value)}/> <div className="input-container"> <input className="start-time-url" placeholder="start time" value={startTime} onChange={(e)=>setStartTime(e.target.value)}/> <input className="end-time-url" placeholder="end time" value={endTime} onChange={e=>setEndTime(e.target.value)}/> </div> { !isSubmitted && <button onClick={(event) => handleSubmit(event, url, startTime, endTime, setVideoUrl, setIsSubmitted)} className='trim-button'>Trim</button> } { ( isSubmitted && !videoUrl) && <div className="dot-pulse"></div> } { videoUrl && <video controls autoPlay width="500" height="360"> <source src={videoUrl} type='video/mp4'></source> </video> } </div> ); } export default App;
内容的提问来源于stack exchange,提问作者Ukpa Uchechi
相关产品推荐
相关产品推荐

