Migrate clip/convert to V3 actions; add background clip support, redirect legacy route; update frontend guide

This commit is contained in:
Scott Idem
2026-03-11 14:51:08 -04:00
parent fbbc186af0
commit a20c436013
8 changed files with 283 additions and 64 deletions

View File

@@ -1,6 +1,7 @@
import aiofiles, datetime, hashlib, mimetypes, os, pathlib, random, shutil, subprocess, shlex, tempfile, time
from fastapi import APIRouter, Body, Depends, File, Form, Header, HTTPException, Path, Query, Response, status, UploadFile
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.responses import FileResponse, StreamingResponse, RedirectResponse
from urllib.parse import quote
from pydantic import BaseModel, EmailStr, Field
from typing import Dict, List, Optional, Set, Union
@@ -9,9 +10,9 @@ from app.config import settings
from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, redis_lookup_id_random
from app.methods.hosted_file_methods import (
create_hosted_file_obj, handle_delete_hosted_file, load_hosted_file_obj,
save_file, save_file_to_hosted_file, create_hosted_file_link,
delete_hosted_file_link, get_hosted_file_link_rec_list, lookup_file_hash,
create_hosted_file_obj, handle_delete_hosted_file, load_hosted_file_obj,
save_file, save_file_to_hosted_file, create_hosted_file_link,
delete_hosted_file_link, get_hosted_file_link_rec_list, lookup_file_hash,
check_for_hosted_file_hash_file, directory_check_method
)
from app.methods.lib_media import clip_video_method, convert_file_method
@@ -35,10 +36,10 @@ async def directory_check(
"""
log.setLevel(logging.INFO)
result_list = directory_check_method(rm_orphan=rm_orphan)
if result_list is False:
return mk_resp(data=False, status_code=500, response=commons.response, status_message='Hosted files directory not found.')
return mk_resp(data=result_list, response=commons.response, status_message=f'Processed {len(result_list)} files.')
# ### END ### API Hosted File ### directory_check() ###
@@ -52,7 +53,7 @@ async def download_hosted_file(
commons: Common_Route_Params = Depends(common_route_params),
):
log.setLevel(logging.INFO)
# ID Resolve
if hfid_int := redis_lookup_id_random(record_id_random=hosted_file_id, table_name='hosted_file'): pass
else: return mk_resp(data=None, status_code=404, response=commons.response, status_message='The hosted_file ID was invalid or not found.')
@@ -63,7 +64,7 @@ async def download_hosted_file(
target_filename = filename or hosted_file_obj.filename
hash_sha256 = hosted_file_obj.hash_sha256
hosted_files_path = settings.FILES_PATH['hosted_files_root']
subdir = hosted_file_obj.subdirectory_path or ''
file_path = os.path.join(hosted_files_path, subdir, f'{hash_sha256}.file')
@@ -151,7 +152,7 @@ async def upload_files(
Legacy Upload Route (V2). Preserved for frontend compatibility.
"""
log.setLevel(logging.INFO)
acc_id_rand = account_id
if acc_id_int := redis_lookup_id_random(record_id_random=acc_id_rand, table_name='account'): pass
else: return mk_resp(data=None, status_code=400, response=response)
@@ -192,7 +193,7 @@ async def upload_files(
# Final metadata and linking
hosted_file_dict.update({'saved': True, 'already_exists': file_info['already_exists'], 'copy_timer': file_info['copy_timer']})
if link_to_type not in ['event', 'event_location', 'event_session', 'event_presentation', 'event_presenter', 'event_badge', 'event_exhibit', 'event_person']:
create_hosted_file_link(account_id=acc_id_int, hosted_file_id=hfid_int, link_to_type=link_to_type, link_to_id=lid_int)
@@ -301,21 +302,23 @@ async def clip_video(
"""
Modularized video clipping route.
"""
if lid_int := redis_lookup_id_random(record_id_random=link_to_id, table_name=link_to_type):
result = await clip_video_method(
hosted_file_id = hosted_file_id,
start_time = start_time,
end_time = end_time,
account_id = commons.x_account_id,
account_id_random = commons.x_account_id_random,
link_to_type = link_to_type,
link_to_id = lid_int,
filename_no_ext = filename_no_ext,
reencode = reencode,
scale_down = scale_down
)
if result: return mk_resp(data=result, response=commons.response)
return mk_resp(data=None, status_code=400, response=commons.response)
# Deprecated legacy route — redirect clients to the V3 action endpoint
# New V3 action: /v3/action/hosted_file/{hosted_file_id}/clip_video
params = [
f"link_to_type={quote(link_to_type)}",
f"link_to_id={quote(link_to_id)}",
f"start_time={quote(start_time)}",
f"end_time={quote(end_time)}",
]
if filename_no_ext:
params.append(f"filename_no_ext={quote(filename_no_ext)}")
if reencode:
params.append(f"reencode=true")
if scale_down:
params.append(f"scale_down=true")
target = f"/v3/action/hosted_file/{hosted_file_id}/clip_video?" + "&".join(params)
return RedirectResponse(url=target, status_code=307)
# ### END ### API Hosted File ### clip_video() ###
@@ -332,7 +335,7 @@ async def create_video(
# Specialized utility route kept inline for now.
from wand.image import Image
from wand.drawing import Drawing
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as audio_file:
audio_file.write(await file.read())
audio_file_path = audio_file.name
@@ -359,5 +362,5 @@ async def create_video(
video_name = f"{tempfile.gettempdir()}/{title_part_1}.mp4"
cmd = f"ffmpeg -y -loop 1 -i {title_image_path} -i {audio_file_path} -c:a copy -c:v libx264 -shortest {video_name}"
subprocess.run(shlex.split(cmd), check=True)
return FileResponse(video_name, media_type='video/mp4', filename=f'{title_part_1}.mp4')