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
0 commit comments