Fred808 commited on
Commit
298adbb
·
verified ·
1 Parent(s): 6813b1d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -0
app.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks, Query
2
+ from fastapi.responses import FileResponse, JSONResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import os
5
+ import uuid
6
+ import time
7
+ from typing import Dict, List, Optional
8
+ import threading
9
+
10
+ # Import processing functions and variables
11
+ from processing_logic import (
12
+ processing_status,
13
+ uploaded_mp4s,
14
+ log_message,
15
+ process_hf_files_background,
16
+ UPLOAD_DIRECTORY,
17
+ MP4_OUTPUT_FOLDER,
18
+ hf_api,
19
+ DEFAULT_RAR_LIMIT
20
+ )
21
+
22
+ app = FastAPI(title="MP4 Processing API", description="API for uploading, processing, and downloading MP4 files")
23
+
24
+ # Configure CORS
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=["*"], # Allows all origins
28
+ allow_credentials=True,
29
+ allow_methods=["*"], # Allows all methods
30
+ allow_headers=["*"], # Allows all headers
31
+ )
32
+
33
+ processing_thread = None
34
+
35
+ # ==== API ENDPOINTS ====
36
+
37
+ def save_file(uploaded_file: UploadFile, save_path: str):
38
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
39
+ with open(save_path, "wb") as f:
40
+ shutil.copyfileobj(uploaded_file.file, f)
41
+
42
+ # === ROUTES ===
43
+
44
+ @app.get("/")
45
+ def root():
46
+ return {"message": "FastAPI Florence-2 backend"}
47
+
48
+ @app.post("/upload/")
49
+ async def upload_file(file: UploadFile = File(...)):
50
+ """Uploads a file and stores it by filename inside the upload folder"""
51
+ save_path = os.path.join(MP4_UPLOAD_FOLDER, file.filename)
52
+ save_file(file, save_path)
53
+ return {"status": "uploaded", "filename": file.filename}
54
+
55
+ @app.get("/list")
56
+ async def list_uploaded_files():
57
+ """List uploaded MP4 files"""
58
+ files = []
59
+ if os.path.exists(MP4_UPLOAD_FOLDER):
60
+ for file in os.listdir(MP4_UPLOAD_FOLDER):
61
+ if file.lower().endswith(".mp4"):
62
+ files.append(file)
63
+ return JSONResponse(content=files)
64
+
65
+
66
+ @app.get("/download/{mp4_path:path}")
67
+ async def download_file(mp4_path: str):
68
+ """
69
+ Download any MP4 under processed_mp4s, including nested course folders.
70
+ e.g. GET /download/MDS_CGAdventure_DownloadPirate.com/1.mp4
71
+ """
72
+ # Compose the full file path
73
+ full_path = os.path.join(MP4_OUTPUT_FOLDER, mp4_path)
74
+
75
+ # Security: Prevent escaping outside of MP4_OUTPUT_FOLDER
76
+ full_path = os.path.normpath(full_path)
77
+ if not full_path.startswith(os.path.abspath(MP4_OUTPUT_FOLDER)):
78
+ raise HTTPException(status_code=400, detail="Invalid path")
79
+
80
+ if not os.path.exists(full_path) or not full_path.lower().endswith(".mp4"):
81
+ raise HTTPException(status_code=404, detail="File not found")
82
+
83
+ return FileResponse(path=full_path, filename=os.path.basename(full_path), media_type="video/mp4")
84
+
85
+ @app.get("/courses")
86
+ async def list_courses():
87
+ """List all unique course folder names based on extracted MP4 paths."""
88
+ course_folders = set()
89
+
90
+ if os.path.exists(MP4_OUTPUT_FOLDER):
91
+ for root, dirs, files in os.walk(MP4_OUTPUT_FOLDER):
92
+ for file in files:
93
+ if file.lower().endswith(".mp4"):
94
+ rel_path = os.path.relpath(os.path.join(root, file), MP4_OUTPUT_FOLDER)
95
+ course_folder = os.path.dirname(rel_path)
96
+ if course_folder:
97
+ course_folders.add(course_folder)
98
+
99
+ return JSONResponse(content={"courses": sorted(course_folders), "total": len(course_folders)})
100
+
101
+ @app.get("/images/{course_folder:path}")
102
+ async def list_mp4s_in_course(course_folder: str):
103
+ """List MP4s available for a specific course folder"""
104
+ course_path = os.path.join(MP4_OUTPUT_FOLDER, course_folder)
105
+ if not os.path.exists(course_path):
106
+ raise HTTPException(status_code=404, detail="Course folder not found")
107
+
108
+ mp4s = []
109
+ for file in os.listdir(course_path):
110
+ if file.lower().endswith(".mp4"):
111
+ mp4s.append(file)
112
+
113
+ return JSONResponse(content=mp4s)
114
+
115
+ @app.get("/")
116
+ async def root():
117
+ """API root endpoint"""
118
+ return {
119
+ "message": "MP4 Processing API",
120
+ "version": "1.0.0",
121
+ "endpoints": {
122
+ "upload": "POST /upload - Upload MP4 file",
123
+ "download": "GET /download/{mp4_id} - Download MP4 file",
124
+ "delete": "DELETE /delete/{mp4_id} - Delete MP4 file",
125
+ "list": "GET /mp4s - List all MP4 files",
126
+ "start_processing": "POST /process/start - Start HF processing",
127
+ "processing_status": "GET /process/status - Get processing status",
128
+ "stop_processing": "POST /process/stop - Stop processing"
129
+ }
130
+ }
131
+
132
+ @app.on_event("startup")
133
+ async def startup_event():
134
+ """Run the processing loop in the background when the API starts"""
135
+ global processing_thread
136
+ if not (processing_thread and processing_thread.is_alive()):
137
+ # log_message("🚀 Starting RAR extraction, frame extraction, and vision analysis pipeline in background...")
138
+ processing_thread = threading.Thread(target=process_hf_files_background)
139
+ processing_thread.daemon = True
140
+ processing_thread.start()
141
+
142
+ if __name__ == "__main__":
143
+ import uvicorn
144
+ uvicorn.run(app, host="0.0.0.0", port=8000)