engine / storage /pending_videos_routers.py
VeuReu's picture
Upload 5 files
e5dde7c verified
raw
history blame
8.07 kB
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