You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:32:03