Skip to content

Commit 9b581bf

Browse files
Stephen G. PopeStephen G. Pope
authored andcommitted
fixes to media -> video on cut
1 parent fd3d9c2 commit 9b581bf

3 files changed

Lines changed: 111 additions & 9 deletions

File tree

app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def wrapper(*args, **kwargs):
168168
from routes.v1.media.convert.media_convert import v1_media_convert_bp
169169
from routes.v1.audio.concatenate import v1_audio_concatenate_bp
170170
from routes.v1.media.silence import v1_media_silence_bp
171-
from routes.v1.media.cut import v1_media_cut_bp
171+
from routes.v1.video.cut import v1_video_cut_bp
172172

173173
app.register_blueprint(v1_ffmpeg_compose_bp)
174174
app.register_blueprint(v1_media_transcribe_bp)
@@ -191,7 +191,7 @@ def wrapper(*args, **kwargs):
191191
app.register_blueprint(v1_media_convert_bp)
192192
app.register_blueprint(v1_audio_concatenate_bp)
193193
app.register_blueprint(v1_media_silence_bp)
194-
app.register_blueprint(v1_media_cut_bp)
194+
app.register_blueprint(v1_video_cut_bp)
195195

196196
return app
197197

routes/v1/video/cut.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) 2025 Stephen G. Pope
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation; either version 2 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License along
14+
# with this program; if not, write to the Free Software Foundation, Inc.,
15+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16+
17+
18+
19+
from flask import Blueprint
20+
from app_utils import *
21+
import logging
22+
from services.v1.video.cut import cut_media
23+
from services.authentication import authenticate
24+
25+
v1_video_cut_bp = Blueprint('v1_video_cut', __name__)
26+
logger = logging.getLogger(__name__)
27+
28+
@v1_video_cut_bp.route('/v1/video/cut', methods=['POST'])
29+
@authenticate
30+
@validate_payload({
31+
"type": "object",
32+
"properties": {
33+
"video_url": {"type": "string", "format": "uri"},
34+
"cuts": {
35+
"type": "array",
36+
"items": {
37+
"type": "object",
38+
"properties": {
39+
"start": {"type": "string"},
40+
"end": {"type": "string"}
41+
},
42+
"required": ["start", "end"],
43+
"additionalProperties": False
44+
},
45+
"minItems": 1
46+
},
47+
"video_codec": {"type": "string"},
48+
"video_preset": {"type": "string"},
49+
"video_crf": {"type": "number", "minimum": 0, "maximum": 51},
50+
"audio_codec": {"type": "string"},
51+
"audio_bitrate": {"type": "string"},
52+
"webhook_url": {"type": "string", "format": "uri"},
53+
"id": {"type": "string"}
54+
},
55+
"required": ["video_url", "cuts"],
56+
"additionalProperties": False
57+
})
58+
@queue_task_wrapper(bypass_queue=False)
59+
def video_cut(job_id, data):
60+
"""Cut specified segments from a video file with optional encoding settings."""
61+
video_url = data['video_url']
62+
cuts = data['cuts']
63+
64+
# Extract encoding settings with defaults
65+
video_codec = data.get('video_codec', 'libx264')
66+
video_preset = data.get('video_preset', 'medium')
67+
video_crf = data.get('video_crf', 23)
68+
audio_codec = data.get('audio_codec', 'aac')
69+
audio_bitrate = data.get('audio_bitrate', '128k')
70+
71+
logger.info(f"Job {job_id}: Received video cut request for {video_url}")
72+
73+
try:
74+
# Process the video file and get local file paths
75+
output_filename, input_filename = cut_media(
76+
video_url=video_url,
77+
cuts=cuts,
78+
job_id=job_id,
79+
video_codec=video_codec,
80+
video_preset=video_preset,
81+
video_crf=video_crf,
82+
audio_codec=audio_codec,
83+
audio_bitrate=audio_bitrate
84+
)
85+
86+
# Upload the processed file to cloud storage
87+
from services.cloud_storage import upload_file
88+
cloud_url = upload_file(output_filename)
89+
logger.info(f"Job {job_id}: Uploaded output to cloud: {cloud_url}")
90+
91+
# Clean up temporary files
92+
import os
93+
os.remove(input_filename)
94+
os.remove(output_filename)
95+
logger.info(f"Job {job_id}: Removed temporary files")
96+
97+
logger.info(f"Job {job_id}: Video cut operation completed successfully")
98+
return cloud_url, "/v1/video/cut", 200
99+
100+
except Exception as e:
101+
logger.error(f"Job {job_id}: Error during video cut process - {str(e)}")
102+
return str(e), "/v1/video/cut", 500
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ def time_to_seconds(time_str):
5252
except ValueError:
5353
raise ValueError(f"Invalid time format: {time_str}. Expected HH:MM:SS[.mmm]")
5454

55-
def cut_media(media_url, cuts, job_id=None, video_codec='libx264', video_preset='medium',
55+
def cut_media(video_url, cuts, job_id=None, video_codec='libx264', video_preset='medium',
5656
video_crf=23, audio_codec='aac', audio_bitrate='128k'):
5757
"""
58-
Cuts specified segments from a media file with customizable encoding settings.
58+
Cuts specified segments from a video file with customizable encoding settings.
5959
6060
Args:
61-
media_url (str): URL of the media file to cut
61+
video_url (str): URL of the video file to cut
6262
cuts (list): List of dictionaries with 'start' and 'end' timestamps
6363
job_id (str, optional): Unique job identifier
6464
video_codec (str, optional): Video codec to use for encoding (default: 'libx264')
@@ -70,9 +70,9 @@ def cut_media(media_url, cuts, job_id=None, video_codec='libx264', video_preset=
7070
Returns:
7171
str: Path to the processed local file
7272
"""
73-
logger.info(f"Starting media cut operation for {media_url}")
74-
input_filename = download_file(media_url, os.path.join(LOCAL_STORAGE_PATH, f"{job_id}_input"))
75-
logger.info(f"Downloaded media to local file: {input_filename}")
73+
logger.info(f"Starting video cut operation for {video_url}")
74+
input_filename = download_file(video_url, os.path.join(LOCAL_STORAGE_PATH, f"{job_id}_input"))
75+
logger.info(f"Downloaded video to local file: {input_filename}")
7676

7777
try:
7878
# Get the file extension
@@ -182,7 +182,7 @@ def cut_media(media_url, cuts, job_id=None, video_codec='libx264', video_preset=
182182
return output_filename, input_filename
183183

184184
except Exception as e:
185-
logger.error(f"Media cut operation failed: {str(e)}")
185+
logger.error(f"Video cut operation failed: {str(e)}")
186186
# Clean up all temporary files if they exist
187187
if 'input_filename' in locals() and os.path.exists(input_filename):
188188
os.remove(input_filename)

0 commit comments

Comments
 (0)