|
|
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")
|
|
|
|
|
|
|
|
|
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.
|
|
|
"""
|
|
|
|
|
|
file_bytes = await video.read()
|
|
|
|
|
|
|
|
|
file_handler = io.BytesIO(file_bytes)
|
|
|
|
|
|
|
|
|
try:
|
|
|
sha1 = file_manager.compute_sha1(file_handler)
|
|
|
except Exception as exc:
|
|
|
raise HTTPException(status_code=500, detail=f"SHA1 computation failed: {exc}")
|
|
|
|
|
|
|
|
|
MEDIA_ROOT.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
video_root = MEDIA_ROOT / sha1
|
|
|
video_root.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
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}")
|
|
|
|
|
|
|
|
|
final_path = video_root / video.filename
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
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 not MEDIA_ROOT.exists():
|
|
|
return []
|
|
|
|
|
|
for sha1_dir in MEDIA_ROOT.iterdir():
|
|
|
if not sha1_dir.is_dir():
|
|
|
continue
|
|
|
|
|
|
videos = []
|
|
|
latest_video = None
|
|
|
|
|
|
if sha1_dir.exists() and sha1_dir.is_dir():
|
|
|
mp4_files = list(sha1_dir.glob("*.mp4"))
|
|
|
|
|
|
|
|
|
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 |