VSEEE2 / app.py
Fred808's picture
Update app.py
1c1d58b verified
from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks, Query
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import os
import shutil
import uuid
import time
from typing import Dict, List, Optional
import threading
import logging
from pathlib import Path
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Import processing functions and variables
from processing_logic import (
processing_status,
uploaded_mp4s,
log_message,
process_hf_files_background,
UPLOAD_DIRECTORY,
MP4_OUTPUT_FOLDER,
hf_api,
DEFAULT_RAR_LIMIT
)
app = FastAPI(title="MP4 Processing API", description="API for uploading, processing, and downloading MP4 files")
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Define MP4_UPLOAD_FOLDER if not imported from processing_logic
MP4_UPLOAD_FOLDER = os.path.join(UPLOAD_DIRECTORY, "uploads") if 'UPLOAD_DIRECTORY' in globals() else "uploads"
os.makedirs(MP4_UPLOAD_FOLDER, exist_ok=True)
os.makedirs(MP4_OUTPUT_FOLDER, exist_ok=True)
processing_thread = None
# ==== HELPER FUNCTIONS ====
def save_file(uploaded_file: UploadFile, save_path: str):
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, "wb") as f:
shutil.copyfileobj(uploaded_file.file, f)
def log_request(endpoint: str, params: dict = None):
"""Log API requests for debugging"""
logger.info(f"API Request: {endpoint} - Params: {params}")
# === ROUTES ===
@app.get("/")
async def root():
"""API root endpoint"""
return {
"message": "MP4 Processing API",
"version": "1.0.0",
"status": "running",
"endpoints": {
"upload": "POST /upload - Upload MP4 file",
"download": "GET /download?course={course}&file={file} - Download MP4 file",
"list": "GET /list - List uploaded files",
"courses": "GET /courses - List all course folders",
"images": "GET /images/{course_folder:path} - List MP4s in course",
"debug": "GET /debug/structure - Debug file structure"
}
}
@app.get("/courses")
async def get_courses():
"""List all top-level course folders."""
try:
courses = [d.name for d in Path(MP4_OUTPUT_FOLDER).iterdir() if d.is_dir()]
return {"courses": courses, "total": len(courses)}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to list courses: {e}")
@app.get("/images/{course_folder:path}")
async def get_mp4_list(course_folder: str):
"""List all MP4 files within a specific course folder."""
course_path = Path(MP4_OUTPUT_FOLDER) / course_folder
if not course_path.is_dir():
raise HTTPException(status_code=404, detail="Course folder not found")
try:
mp4_files = [f.name for f in course_path.iterdir() if f.is_file() and f.suffix.lower() == ".mp4"]
return mp4_files
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to list MP4s: {e}")
@app.get("/download")
async def download_mp4(course: str, file: str):
"""Download a specific MP4 file from a course folder."""
file_path = Path(MP4_OUTPUT_FOLDER) / course / file
if not file_path.is_file():
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(path=file_path, media_type="video/mp4", filename=file)
@app.get("/debug/structure")
async def debug_structure():
"""Debug endpoint to inspect the file structure and sizes."""
mp4_output_folder_path = Path(MP4_OUTPUT_FOLDER)
structure = {}
total_size_bytes = 0
total_mp4_files = 0
if not mp4_output_folder_path.exists():
return JSONResponse(content={
"mp4_output_folder": str(mp4_output_folder_path),
"folder_exists": False,
"total_mp4_files": 0,
"total_size_bytes": 0,
"structure": {}
})
for root, dirs, files in os.walk(mp4_output_folder_path):
current_path = Path(root)
relative_path = str(current_path.relative_to(mp4_output_folder_path))
if relative_path == ".":
relative_path = "/"
structure[relative_path] = {
"directories": [d for d in dirs],
"mp4_files": [],
"other_files": []
}
for file in files:
file_full_path = current_path / file
file_size = file_full_path.stat().st_size
total_size_bytes += file_size
if file.lower().endswith(".mp4"):
structure[relative_path]["mp4_files"].append({"name": file, "size": file_size})
total_mp4_files += 1
else:
structure[relative_path]["other_files"].append({"name": file, "size": file_size})
return {
"mp4_output_folder": str(mp4_output_folder_path),
"folder_exists": mp4_output_folder_path.exists(),
"total_mp4_files": total_mp4_files,
"total_size_bytes": total_size_bytes,
"structure": structure
}
@app.on_event("startup")
async def startup_event():
"""Run the processing loop in the background when the API starts"""
global processing_thread
logger.info("Starting up MP4 Processing API...")
if not (processing_thread and processing_thread.is_alive()):
logger.info("πŸš€ Starting background processing thread...")
processing_thread = threading.Thread(target=process_hf_files_background)
processing_thread.daemon = True
processing_thread.start()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)