VeuReu commited on
Commit
6f3fcfa
·
verified ·
1 Parent(s): 86389c6

Update storage/media_routers.py

Browse files
Files changed (1) hide show
  1. storage/media_routers.py +324 -324
storage/media_routers.py CHANGED
@@ -1,324 +1,324 @@
1
- import os
2
- import io
3
- import shutil
4
-
5
- import sqlite3
6
-
7
- from pathlib import Path
8
-
9
- from fastapi import APIRouter, UploadFile, File, Query, HTTPException
10
- from fastapi.responses import FileResponse, JSONResponse
11
-
12
-
13
- from storage.files.file_manager import FileManager
14
- from storage.common import validate_token
15
-
16
- router = APIRouter(prefix="/media", tags=["Media Manager"])
17
- MEDIA_ROOT = Path("/data/media")
18
- file_manager = FileManager(MEDIA_ROOT)
19
- HF_TOKEN = os.getenv("HF_TOKEN")
20
-
21
-
22
- @router.delete("/clear_media", tags=["Media Manager"])
23
- def clear_media(token: str = Query(..., description="Token required for authorization")):
24
- """
25
- Delete all contents of the /data/media folder.
26
-
27
- Steps:
28
- - Validate the token.
29
- - Ensure the folder exists.
30
- - Delete all files and subfolders inside /data/media.
31
- - Return a JSON response confirming the deletion.
32
-
33
- Warning: This will remove all stored videos, clips, and cast CSV files.
34
- """
35
- validate_token(token)
36
-
37
- if not MEDIA_ROOT.exists() or not MEDIA_ROOT.is_dir():
38
- raise HTTPException(status_code=404, detail="/data/media folder does not exist")
39
-
40
- # Delete contents
41
- for item in MEDIA_ROOT.iterdir():
42
- try:
43
- if item.is_dir():
44
- shutil.rmtree(item)
45
- else:
46
- item.unlink()
47
- except Exception as e:
48
- raise HTTPException(status_code=500, detail=f"Failed to delete {item}: {e}")
49
-
50
- return {"status": "ok", "message": "All media files deleted successfully"}
51
-
52
- @router.post("/upload_cast_csv/{sha1}", tags=["Media Manager"])
53
- async def upload_cast_csv(
54
- sha1: str,
55
- cast_file: UploadFile = File(...),
56
- token: str = Query(..., description="Token required for authorization")
57
- ):
58
- """
59
- Upload a cast CSV file for a specific video identified by its SHA-1.
60
-
61
- The CSV will be stored under:
62
- /data/media/<sha1>/cast/cast.csv
63
-
64
- Steps:
65
- - Validate the token.
66
- - Ensure /data/media/<sha1> exists.
67
- - Create /cast folder if missing.
68
- - Save the CSV file inside /cast.
69
- """
70
- validate_token(token)
71
-
72
- base_folder = MEDIA_ROOT / sha1
73
- if not base_folder.exists() or not base_folder.is_dir():
74
- raise HTTPException(status_code=404, detail="SHA1 folder not found")
75
-
76
- cast_folder = base_folder / "cast"
77
- cast_folder.mkdir(parents=True, exist_ok=True)
78
-
79
- final_path = cast_folder / "cast.csv"
80
-
81
- file_bytes = await cast_file.read()
82
- save_result = file_manager.upload_file(io.BytesIO(file_bytes), final_path)
83
- if not save_result["operation_success"]:
84
- raise HTTPException(status_code=500, detail=save_result["error"])
85
-
86
- return JSONResponse(
87
- status_code=200,
88
- content={"status": "ok", "saved_to": str(final_path)}
89
- )
90
-
91
-
92
- @router.get("/download_cast_csv/{sha1}", tags=["Media Manager"])
93
- def download_cast_csv(
94
- sha1: str,
95
- token: str = Query(..., description="Token required for authorization")
96
- ):
97
- """
98
- Download the cast CSV for a specific video identified by its SHA-1.
99
-
100
- The CSV is expected under:
101
- /data/media/<sha1>/cast/cast.csv
102
-
103
- Steps:
104
- - Validate the token.
105
- - Ensure /data/media/<sha1> and /cast exist.
106
- - Return the CSV as a FileResponse.
107
- - Raise 404 if any folder or file is missing.
108
- """
109
- MEDIA_ROOT = Path("/data/media")
110
- file_manager = FileManager(MEDIA_ROOT)
111
- HF_TOKEN = os.getenv("HF_TOKEN")
112
- validate_token(token)
113
-
114
- base_folder = MEDIA_ROOT / sha1
115
- cast_folder = base_folder / "cast"
116
- csv_path = cast_folder / "cast.csv"
117
-
118
- if not base_folder.exists() or not base_folder.is_dir():
119
- raise HTTPException(status_code=404, detail="SHA1 folder not found")
120
- if not cast_folder.exists() or not cast_folder.is_dir():
121
- raise HTTPException(status_code=404, detail="Cast folder not found")
122
- if not csv_path.exists() or not csv_path.is_file():
123
- raise HTTPException(status_code=404, detail="Cast CSV not found")
124
-
125
- # Convert to relative path for FileManager
126
- relative_path = csv_path.relative_to(MEDIA_ROOT)
127
- handler = file_manager.get_file(relative_path)
128
- if handler is None:
129
- raise HTTPException(status_code=404, detail="Cast CSV not accessible")
130
- handler.close()
131
-
132
- return FileResponse(
133
- path=csv_path,
134
- media_type="text/csv",
135
- filename="cast.csv"
136
- )
137
-
138
- @router.post("/upload_original_video", tags=["Media Manager"])
139
- async def upload_video(
140
- video: UploadFile = File(...),
141
- token: str = Query(..., description="Token required for authorization")
142
- ):
143
- """
144
- Saves an uploaded video by hashing it with SHA1 and placing it under:
145
- /data/media/<sha1>/clip/<original_filename>
146
-
147
- Steps:
148
- - Compute SHA1 of the uploaded video.
149
- - Ensure /data/media exists.
150
- - Create folder /data/media/<sha1> if missing.
151
- - Create folder /data/media/<sha1>/clip if missing.
152
- - Save the video inside /data/media/<sha1>/clip/.
153
- """
154
- MEDIA_ROOT = Path("/data/media")
155
- file_manager = FileManager(MEDIA_ROOT)
156
- HF_TOKEN = os.getenv("HF_TOKEN")
157
- validate_token(token)
158
-
159
- # Read content into memory (needed to compute hash twice)
160
- file_bytes = await video.read()
161
-
162
- # Create an in-memory file handler
163
- file_handler = io.BytesIO(file_bytes)
164
-
165
- # Compute SHA1 using your FileManager method
166
- try:
167
- sha1 = file_manager.compute_sha1(file_handler)
168
- except Exception as exc:
169
- raise HTTPException(status_code=500, detail=f"SHA1 computation failed: {exc}")
170
-
171
- # Ensure /data/media exists
172
- MEDIA_ROOT.mkdir(parents=True, exist_ok=True)
173
-
174
- # Path: /data/media/<sha1>
175
- video_root = MEDIA_ROOT / sha1
176
- video_root.mkdir(parents=True, exist_ok=True)
177
-
178
- # Path: /data/media/<sha1>/clip
179
- clip_dir = video_root / "clip"
180
- clip_dir.mkdir(parents=True, exist_ok=True)
181
-
182
- # Final file path
183
- final_path = clip_dir / video.filename
184
-
185
- # Save file using your FileManager.upload_file
186
- save_result = file_manager.upload_file(io.BytesIO(file_bytes), final_path)
187
-
188
- if not save_result["operation_success"]:
189
- raise HTTPException(status_code=500, detail=save_result["error"])
190
-
191
- return JSONResponse(
192
- status_code=200,
193
- content={
194
- "status": "ok",
195
- "sha1": sha1,
196
- "saved_to": str(final_path)
197
- }
198
- )
199
-
200
-
201
- @router.get("/download_original_video/{sha1}", tags=["Media Manager"])
202
- def download_video(
203
- sha1: str,
204
- token: str = Query(..., description="Token required for authorization")
205
- ):
206
- """
207
- Download a stored video by its SHA-1 directory name.
208
-
209
- This endpoint looks for a video stored under the path:
210
- /data/media/<sha1>/clip/
211
- and returns the first MP4 file found in that folder.
212
-
213
- The method performs the following steps:
214
- - Checks if the SHA-1 folder exists inside the media root.
215
- - Validates that the "clip" subfolder exists.
216
- - Searches for the first .mp4 file inside the clip folder.
217
- - Uses the FileManager.get_file method to ensure the file is accessible.
218
- - Returns the video directly as a FileResponse.
219
-
220
- Parameters
221
- ----------
222
- sha1 : str
223
- The SHA-1 hash corresponding to the directory where the video is stored.
224
-
225
- Returns
226
- -------
227
- FileResponse
228
- A streaming response containing the MP4 video.
229
-
230
- Raises
231
- ------
232
- HTTPException
233
- - 404 if the SHA-1 folder does not exist.
234
- - 404 if the clip folder is missing.
235
- - 404 if no MP4 files are found.
236
- - 404 if the file cannot be retrieved using FileManager.
237
- """
238
- MEDIA_ROOT = Path("/data/media")
239
- file_manager = FileManager(MEDIA_ROOT)
240
- HF_TOKEN = os.getenv("HF_TOKEN")
241
- validate_token(token)
242
-
243
- sha1_folder = MEDIA_ROOT / sha1
244
- clip_folder = sha1_folder / "clip"
245
-
246
- if not sha1_folder.exists() or not sha1_folder.is_dir():
247
- raise HTTPException(status_code=404, detail="SHA1 folder not found")
248
-
249
- if not clip_folder.exists() or not clip_folder.is_dir():
250
- raise HTTPException(status_code=404, detail="Clip folder not found")
251
-
252
- # Find first MP4 file
253
- mp4_files = list(clip_folder.glob("*.mp4"))
254
- if not mp4_files:
255
- raise HTTPException(status_code=404, detail="No MP4 files found")
256
-
257
- video_path = mp4_files[0]
258
-
259
- # Convert to relative path for FileManager
260
- relative_path = video_path.relative_to(MEDIA_ROOT)
261
-
262
- handler = file_manager.get_file(relative_path)
263
- if handler is None:
264
- raise HTTPException(status_code=404, detail="Video not accessible")
265
-
266
- handler.close()
267
-
268
- return FileResponse(
269
- path=video_path,
270
- media_type="video/mp4",
271
- filename=video_path.name
272
- )
273
-
274
- @router.get("/list_original_videos", tags=["Media Manager"])
275
- def list_all_videos(
276
- token: str = Query(..., description="Token required for authorization")
277
- ):
278
- """
279
- List all videos stored under /data/media.
280
-
281
- For each SHA1 folder, the endpoint returns:
282
- - sha1: folder name
283
- - video_files: list of mp4 files inside /clip
284
- - latest_video: the most recently modified mp4
285
- - video_count: total number of mp4 files
286
-
287
- Notes:
288
- - Videos may not have a /clip folder.
289
- - SHA1 folders without mp4 files are still returned.
290
- """
291
- validate_token(token)
292
-
293
- results = []
294
-
295
- # If media root does not exist, return empty list
296
- if not MEDIA_ROOT.exists():
297
- return []
298
-
299
- for sha1_dir in MEDIA_ROOT.iterdir():
300
- if not sha1_dir.is_dir():
301
- continue # skip non-folders
302
-
303
- clip_dir = sha1_dir / "clip"
304
-
305
- videos = []
306
- latest_video = None
307
-
308
- if clip_dir.exists() and clip_dir.is_dir():
309
- mp4_files = list(clip_dir.glob("*.mp4"))
310
-
311
- # Sort by modification time (newest first)
312
- mp4_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
313
-
314
- videos = [f.name for f in mp4_files]
315
-
316
- if mp4_files:
317
- latest_video = mp4_files[0].name
318
-
319
- results.append({
320
- "sha1": sha1_dir.name,
321
- "video_name": latest_video
322
- })
323
-
324
- return results
 
1
+ import os
2
+ import io
3
+ import shutil
4
+
5
+ import sqlite3
6
+
7
+ from pathlib import Path
8
+
9
+ from fastapi import APIRouter, UploadFile, File, Query, HTTPException
10
+ from fastapi.responses import FileResponse, JSONResponse
11
+
12
+
13
+ from storage.files.file_manager import FileManager
14
+ from storage.common import validate_token
15
+
16
+ router = APIRouter(prefix="/media", tags=["Media Manager"])
17
+ MEDIA_ROOT = Path("/data/media")
18
+ file_manager = FileManager(MEDIA_ROOT)
19
+ HF_TOKEN = os.getenv("HF_TOKEN")
20
+
21
+
22
+ @router.delete("/clear_media", tags=["Media Manager"])
23
+ def clear_media(token: str = Query(..., description="Token required for authorization")):
24
+ """
25
+ Delete all contents of the /data/media folder.
26
+
27
+ Steps:
28
+ - Validate the token.
29
+ - Ensure the folder exists.
30
+ - Delete all files and subfolders inside /data/media.
31
+ - Return a JSON response confirming the deletion.
32
+
33
+ Warning: This will remove all stored videos, clips, and cast CSV files.
34
+ """
35
+ validate_token(token)
36
+
37
+ if not MEDIA_ROOT.exists() or not MEDIA_ROOT.is_dir():
38
+ raise HTTPException(status_code=404, detail="/data/media folder does not exist")
39
+
40
+ # Delete contents
41
+ for item in MEDIA_ROOT.iterdir():
42
+ try:
43
+ if item.is_dir():
44
+ shutil.rmtree(item)
45
+ else:
46
+ item.unlink()
47
+ except Exception as e:
48
+ raise HTTPException(status_code=500, detail=f"Failed to delete {item}: {e}")
49
+
50
+ return {"status": "ok", "message": "All media files deleted successfully"}
51
+
52
+ @router.post("/upload_cast_csv", tags=["Media Manager"])
53
+ async def upload_cast_csv(
54
+ sha1: str,
55
+ cast_file: UploadFile = File(...),
56
+ token: str = Query(..., description="Token required for authorization")
57
+ ):
58
+ """
59
+ Upload a cast CSV file for a specific video identified by its SHA-1.
60
+
61
+ The CSV will be stored under:
62
+ /data/media/<sha1>/cast/cast.csv
63
+
64
+ Steps:
65
+ - Validate the token.
66
+ - Ensure /data/media/<sha1> exists.
67
+ - Create /cast folder if missing.
68
+ - Save the CSV file inside /cast.
69
+ """
70
+ validate_token(token)
71
+
72
+ base_folder = MEDIA_ROOT / sha1
73
+ if not base_folder.exists() or not base_folder.is_dir():
74
+ raise HTTPException(status_code=404, detail="SHA1 folder not found")
75
+
76
+ cast_folder = base_folder / "cast"
77
+ cast_folder.mkdir(parents=True, exist_ok=True)
78
+
79
+ final_path = cast_folder / "cast.csv"
80
+
81
+ file_bytes = await cast_file.read()
82
+ save_result = file_manager.upload_file(io.BytesIO(file_bytes), final_path)
83
+ if not save_result["operation_success"]:
84
+ raise HTTPException(status_code=500, detail=save_result["error"])
85
+
86
+ return JSONResponse(
87
+ status_code=200,
88
+ content={"status": "ok", "saved_to": str(final_path)}
89
+ )
90
+
91
+
92
+ @router.get("/download_cast_csv", tags=["Media Manager"])
93
+ def download_cast_csv(
94
+ sha1: str,
95
+ token: str = Query(..., description="Token required for authorization")
96
+ ):
97
+ """
98
+ Download the cast CSV for a specific video identified by its SHA-1.
99
+
100
+ The CSV is expected under:
101
+ /data/media/<sha1>/cast/cast.csv
102
+
103
+ Steps:
104
+ - Validate the token.
105
+ - Ensure /data/media/<sha1> and /cast exist.
106
+ - Return the CSV as a FileResponse.
107
+ - Raise 404 if any folder or file is missing.
108
+ """
109
+ MEDIA_ROOT = Path("/data/media")
110
+ file_manager = FileManager(MEDIA_ROOT)
111
+ HF_TOKEN = os.getenv("HF_TOKEN")
112
+ validate_token(token)
113
+
114
+ base_folder = MEDIA_ROOT / sha1
115
+ cast_folder = base_folder / "cast"
116
+ csv_path = cast_folder / "cast.csv"
117
+
118
+ if not base_folder.exists() or not base_folder.is_dir():
119
+ raise HTTPException(status_code=404, detail="SHA1 folder not found")
120
+ if not cast_folder.exists() or not cast_folder.is_dir():
121
+ raise HTTPException(status_code=404, detail="Cast folder not found")
122
+ if not csv_path.exists() or not csv_path.is_file():
123
+ raise HTTPException(status_code=404, detail="Cast CSV not found")
124
+
125
+ # Convert to relative path for FileManager
126
+ relative_path = csv_path.relative_to(MEDIA_ROOT)
127
+ handler = file_manager.get_file(relative_path)
128
+ if handler is None:
129
+ raise HTTPException(status_code=404, detail="Cast CSV not accessible")
130
+ handler.close()
131
+
132
+ return FileResponse(
133
+ path=csv_path,
134
+ media_type="text/csv",
135
+ filename="cast.csv"
136
+ )
137
+
138
+ @router.post("/upload_original_video", tags=["Media Manager"])
139
+ async def upload_video(
140
+ video: UploadFile = File(...),
141
+ token: str = Query(..., description="Token required for authorization")
142
+ ):
143
+ """
144
+ Saves an uploaded video by hashing it with SHA1 and placing it under:
145
+ /data/media/<sha1>/clip/<original_filename>
146
+
147
+ Steps:
148
+ - Compute SHA1 of the uploaded video.
149
+ - Ensure /data/media exists.
150
+ - Create folder /data/media/<sha1> if missing.
151
+ - Create folder /data/media/<sha1>/clip if missing.
152
+ - Save the video inside /data/media/<sha1>/clip/.
153
+ """
154
+ MEDIA_ROOT = Path("/data/media")
155
+ file_manager = FileManager(MEDIA_ROOT)
156
+ HF_TOKEN = os.getenv("HF_TOKEN")
157
+ validate_token(token)
158
+
159
+ # Read content into memory (needed to compute hash twice)
160
+ file_bytes = await video.read()
161
+
162
+ # Create an in-memory file handler
163
+ file_handler = io.BytesIO(file_bytes)
164
+
165
+ # Compute SHA1 using your FileManager method
166
+ try:
167
+ sha1 = file_manager.compute_sha1(file_handler)
168
+ except Exception as exc:
169
+ raise HTTPException(status_code=500, detail=f"SHA1 computation failed: {exc}")
170
+
171
+ # Ensure /data/media exists
172
+ MEDIA_ROOT.mkdir(parents=True, exist_ok=True)
173
+
174
+ # Path: /data/media/<sha1>
175
+ video_root = MEDIA_ROOT / sha1
176
+ video_root.mkdir(parents=True, exist_ok=True)
177
+
178
+ # Path: /data/media/<sha1>/clip
179
+ clip_dir = video_root / "clip"
180
+ clip_dir.mkdir(parents=True, exist_ok=True)
181
+
182
+ # Final file path
183
+ final_path = clip_dir / video.filename
184
+
185
+ # Save file using your FileManager.upload_file
186
+ save_result = file_manager.upload_file(io.BytesIO(file_bytes), final_path)
187
+
188
+ if not save_result["operation_success"]:
189
+ raise HTTPException(status_code=500, detail=save_result["error"])
190
+
191
+ return JSONResponse(
192
+ status_code=200,
193
+ content={
194
+ "status": "ok",
195
+ "sha1": sha1,
196
+ "saved_to": str(final_path)
197
+ }
198
+ )
199
+
200
+
201
+ @router.get("/download_original_video", tags=["Media Manager"])
202
+ def download_video(
203
+ sha1: str,
204
+ token: str = Query(..., description="Token required for authorization")
205
+ ):
206
+ """
207
+ Download a stored video by its SHA-1 directory name.
208
+
209
+ This endpoint looks for a video stored under the path:
210
+ /data/media/<sha1>/clip/
211
+ and returns the first MP4 file found in that folder.
212
+
213
+ The method performs the following steps:
214
+ - Checks if the SHA-1 folder exists inside the media root.
215
+ - Validates that the "clip" subfolder exists.
216
+ - Searches for the first .mp4 file inside the clip folder.
217
+ - Uses the FileManager.get_file method to ensure the file is accessible.
218
+ - Returns the video directly as a FileResponse.
219
+
220
+ Parameters
221
+ ----------
222
+ sha1 : str
223
+ The SHA-1 hash corresponding to the directory where the video is stored.
224
+
225
+ Returns
226
+ -------
227
+ FileResponse
228
+ A streaming response containing the MP4 video.
229
+
230
+ Raises
231
+ ------
232
+ HTTPException
233
+ - 404 if the SHA-1 folder does not exist.
234
+ - 404 if the clip folder is missing.
235
+ - 404 if no MP4 files are found.
236
+ - 404 if the file cannot be retrieved using FileManager.
237
+ """
238
+ MEDIA_ROOT = Path("/data/media")
239
+ file_manager = FileManager(MEDIA_ROOT)
240
+ HF_TOKEN = os.getenv("HF_TOKEN")
241
+ validate_token(token)
242
+
243
+ sha1_folder = MEDIA_ROOT / sha1
244
+ clip_folder = sha1_folder / "clip"
245
+
246
+ if not sha1_folder.exists() or not sha1_folder.is_dir():
247
+ raise HTTPException(status_code=404, detail="SHA1 folder not found")
248
+
249
+ if not clip_folder.exists() or not clip_folder.is_dir():
250
+ raise HTTPException(status_code=404, detail="Clip folder not found")
251
+
252
+ # Find first MP4 file
253
+ mp4_files = list(clip_folder.glob("*.mp4"))
254
+ if not mp4_files:
255
+ raise HTTPException(status_code=404, detail="No MP4 files found")
256
+
257
+ video_path = mp4_files[0]
258
+
259
+ # Convert to relative path for FileManager
260
+ relative_path = video_path.relative_to(MEDIA_ROOT)
261
+
262
+ handler = file_manager.get_file(relative_path)
263
+ if handler is None:
264
+ raise HTTPException(status_code=404, detail="Video not accessible")
265
+
266
+ handler.close()
267
+
268
+ return FileResponse(
269
+ path=video_path,
270
+ media_type="video/mp4",
271
+ filename=video_path.name
272
+ )
273
+
274
+ @router.get("/list_original_videos", tags=["Media Manager"])
275
+ def list_all_videos(
276
+ token: str = Query(..., description="Token required for authorization")
277
+ ):
278
+ """
279
+ List all videos stored under /data/media.
280
+
281
+ For each SHA1 folder, the endpoint returns:
282
+ - sha1: folder name
283
+ - video_files: list of mp4 files inside /clip
284
+ - latest_video: the most recently modified mp4
285
+ - video_count: total number of mp4 files
286
+
287
+ Notes:
288
+ - Videos may not have a /clip folder.
289
+ - SHA1 folders without mp4 files are still returned.
290
+ """
291
+ validate_token(token)
292
+
293
+ results = []
294
+
295
+ # If media root does not exist, return empty list
296
+ if not MEDIA_ROOT.exists():
297
+ return []
298
+
299
+ for sha1_dir in MEDIA_ROOT.iterdir():
300
+ if not sha1_dir.is_dir():
301
+ continue # skip non-folders
302
+
303
+ clip_dir = sha1_dir / "clip"
304
+
305
+ videos = []
306
+ latest_video = None
307
+
308
+ if clip_dir.exists() and clip_dir.is_dir():
309
+ mp4_files = list(clip_dir.glob("*.mp4"))
310
+
311
+ # Sort by modification time (newest first)
312
+ mp4_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
313
+
314
+ videos = [f.name for f in mp4_files]
315
+
316
+ if mp4_files:
317
+ latest_video = mp4_files[0].name
318
+
319
+ results.append({
320
+ "sha1": sha1_dir.name,
321
+ "video_name": latest_video
322
+ })
323
+
324
+ return results