engine / storage /pending_videos_routers.py
VeuReu's picture
Upload 5 files
e5dde7c verified
import os
import io
import shutil
import sqlite3
from pathlib import Path
from fastapi import APIRouter, UploadFile, File, Query, HTTPException
from fastapi.responses import FileResponse, JSONResponse
from storage.files.file_manager import FileManager
from storage.common import validate_token
router = APIRouter(prefix="/pending_videos", tags=["Pending Videos Manager"])
MEDIA_ROOT = Path("/data/pending_videos")
file_manager = FileManager(MEDIA_ROOT)
HF_TOKEN = os.getenv("HF_TOKEN")
@router.delete("/clear_pending_videos", tags=["Pending Videos Manager"])
def clear_media(token: str = Query(..., description="Token required for authorization")):
"""
Delete all contents of the /data/pending_videos folder.
Steps:
- Validate the token.
- Ensure the folder exists.
- Delete all files and subfolders inside /data/pending_videos.
- Return a JSON response confirming the deletion.
Warning: This will remove all stored videos, clips, and cast CSV files.
"""
validate_token(token)
if not MEDIA_ROOT.exists() or not MEDIA_ROOT.is_dir():
raise HTTPException(status_code=404, detail="/data/pending_videos folder does not exist")
# Delete contents
for item in MEDIA_ROOT.iterdir():
try:
if item.is_dir():
shutil.rmtree(item)
else:
item.unlink()
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete {item}: {e}")
return {"status": "ok", "message": "All pending_videos files deleted successfully"}
@router.delete("/clear_pending_video", tags=["Pending Videos Manager"])
def clear_pending_video(
sha1: str = Query(..., description="SHA1 folder to delete inside pending_videos"),
token: str = Query(..., description="Token required for authorization")
):
"""
Delete a specific SHA1 folder inside /data/pending_videos.
Steps:
- Validate the token.
- Ensure the folder exists.
- Delete the folder and all its contents.
- Return a JSON response confirming the deletion.
"""
validate_token(token)
PENDING_ROOT = Path("/data/pending_videos")
target_folder = PENDING_ROOT / sha1
if not target_folder.exists() or not target_folder.is_dir():
raise HTTPException(status_code=404, detail=f"Folder {sha1} does not exist in pending_videos")
try:
shutil.rmtree(target_folder)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete {sha1}: {e}")
return {"status": "ok", "message": f"Pending video folder {sha1} deleted successfully"}
@router.post("/upload_pending_video", tags=["Pending Videos Manager"])
async def upload_video(
video: UploadFile = File(...),
token: str = Query(..., description="Token required for authorization")
):
"""
Saves an uploaded video by hashing it with SHA1 and placing it under:
/data/media/<sha1>/<original_filename>
Behavior:
- Compute SHA1 of the uploaded video.
- Ensure folder structure exists.
- Delete any existing .mp4 files under sha1.
- Save the uploaded video in the folder.
"""
# Read content into memory (needed to compute hash twice)
file_bytes = await video.read()
# Create an in-memory file handler for hashing
file_handler = io.BytesIO(file_bytes)
# Compute SHA1
try:
sha1 = file_manager.compute_sha1(file_handler)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"SHA1 computation failed: {exc}")
# Ensure /data/media exists
MEDIA_ROOT.mkdir(parents=True, exist_ok=True)
# Path: /data/media/<sha1>
video_root = MEDIA_ROOT / sha1
video_root.mkdir(parents=True, exist_ok=True)
# Delete old MP4 files
try:
for old_mp4 in video_root.glob("*.mp4"):
old_mp4.unlink()
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Failed to delete old videos: {exc}")
# Save new video path
final_path = video_root / video.filename
# Save file
save_result = file_manager.upload_file(io.BytesIO(file_bytes), final_path)
if not save_result["operation_success"]:
raise HTTPException(status_code=500, detail=save_result["error"])
return JSONResponse(
status_code=200,
content={
"status": "ok",
"sha1": sha1,
"saved_to": str(final_path)
}
)
@router.get("/download_pending_video", tags=["Pending Videos Manager"])
def download_video(
sha1: str,
token: str = Query(..., description="Token required for authorization")
):
"""
Download a stored video by its SHA-1 directory name.
This endpoint looks for a video stored under the path:
/data/media/<sha1>/clip/
and returns the first MP4 file found in that folder.
The method performs the following steps:
- Checks if the SHA-1 folder exists inside the media root.
- Validates that the "clip" subfolder exists.
- Searches for the first .mp4 file inside the clip folder.
- Uses the FileManager.get_file method to ensure the file is accessible.
- Returns the video directly as a FileResponse.
Parameters
----------
sha1 : str
The SHA-1 hash corresponding to the directory where the video is stored.
Returns
-------
FileResponse
A streaming response containing the MP4 video.
Raises
------
HTTPException
- 404 if the SHA-1 folder does not exist.
- 404 if the clip folder is missing.
- 404 if no MP4 files are found.
- 404 if the file cannot be retrieved using FileManager.
"""
sha1_folder = MEDIA_ROOT / sha1
if not sha1_folder.exists() or not sha1_folder.is_dir():
raise HTTPException(status_code=404, detail="SHA1 folder not found")
# Find first MP4 file
mp4_files = list(sha1_folder.glob("*.mp4"))
if not mp4_files:
raise HTTPException(status_code=404, detail="No MP4 files found")
video_path = mp4_files[0]
# Convert to relative path for FileManager
relative_path = video_path.relative_to(MEDIA_ROOT)
handler = file_manager.get_file(relative_path)
if handler is None:
raise HTTPException(status_code=404, detail="Video not accessible")
handler.close()
return FileResponse(
path=video_path,
media_type="video/mp4",
filename=video_path.name
)
@router.get("/list_pending_videos", tags=["Pending Videos Manager"])
def list_all_videos(
token: str = Query(..., description="Token required for authorization")
):
"""
List all videos stored under /data/media.
For each SHA1 folder, the endpoint returns:
- sha1: folder name
- video_files: list of mp4 files inside /clip
- latest_video: the most recently modified mp4
- video_count: total number of mp4 files
Notes:
- Videos may not have a /clip folder.
- SHA1 folders without mp4 files are still returned.
"""
validate_token(token)
results = []
# If media root does not exist, return empty list
if not MEDIA_ROOT.exists():
return []
for sha1_dir in MEDIA_ROOT.iterdir():
if not sha1_dir.is_dir():
continue # skip non-folders
videos = []
latest_video = None
if sha1_dir.exists() and sha1_dir.is_dir():
mp4_files = list(sha1_dir.glob("*.mp4"))
# Sort by modification time (newest first)
mp4_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
videos = [f.name for f in mp4_files]
if mp4_files:
latest_video = mp4_files[0].name
results.append({
"sha1": sha1_dir.name,
"video_name": latest_video
})
return results