45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
import logging
|
|
import requests
|
|
from moviepy import VideoFileClip, concatenate_videoclips
|
|
from utils.retry import download_retry
|
|
|
|
|
|
@download_retry
|
|
def download_video(url, save_path):
|
|
try:
|
|
logging.info(f"Downloading video from {url} to {save_path}")
|
|
|
|
response = requests.get(url, stream=True, timeout=(10, 300))
|
|
response.raise_for_status() # 检查请求是否成功
|
|
|
|
with open(save_path, 'wb') as f:
|
|
for chunk in response.iter_content(chunk_size=8192):
|
|
f.write(chunk)
|
|
|
|
logging.info(f"Video downloaded successfully to {save_path}")
|
|
|
|
except Exception as e:
|
|
logging.error(f"Error downloading video: {e}")
|
|
raise e
|
|
|
|
|
|
def concatenate_video_files(video_paths, output_path, codec="libx264", preset="medium"):
|
|
"""Concatenate video files, releasing every ffmpeg reader even on failure.
|
|
|
|
Each VideoFileClip keeps an ffmpeg subprocess and file handle open until
|
|
closed; leaking them exhausts file descriptors on long multi-scene runs.
|
|
"""
|
|
clips = []
|
|
final = None
|
|
try:
|
|
for path in video_paths:
|
|
clips.append(VideoFileClip(path))
|
|
final = concatenate_videoclips(clips)
|
|
final.write_videofile(output_path, codec=codec, preset=preset)
|
|
finally:
|
|
if final is not None:
|
|
final.close()
|
|
for clip in clips:
|
|
clip.close()
|
|
return output_path
|