Spaces:
Sleeping
Sleeping
from fastapi import APIRouter, File, UploadFile, Depends, HTTPException, Form | |
from firebase_admin import firestore | |
import hashlib | |
import uuid | |
from ...core.auth import require_role, get_user | |
from typing import List, Optional, Dict | |
from app.services.youtube_downloader import download_youtube_video, parse_urls | |
from ...services.processor import process_video | |
from typing import Dict | |
from ...core.firebase import db | |
from ...services.clip_assignment import ClipAssignmentService | |
router = APIRouter() | |
async def get_videos(user_info=Depends(get_user)): | |
try: | |
# Récupération des vidéos globales | |
videos_ref = db.collection('videos') | |
videos = [] | |
# Récupération du statut utilisateur | |
user_status_ref = db.collection('user_video_status').document(user_info['uid']) | |
user_status_doc = user_status_ref.get() | |
user_statuses = user_status_doc.to_dict() if user_status_doc.exists else {"video_status": []} | |
# Fusion des données | |
for doc in videos_ref.stream(): | |
video_data = doc.to_dict() | |
video_data['id'] = doc.id | |
# Ajout du statut spécifique à l'utilisateur | |
user_status = next( | |
(status for status in user_status_doc.get('video_status', []) | |
if status['uuid'] == doc.id), | |
None | |
) | |
video_data['user_status'] = user_status['status'] if user_status else 'ready' | |
videos.append(video_data) | |
return {"videos": videos} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def upload_video( | |
sport_id: str = Form(...), | |
file: Optional[UploadFile] = File(None), | |
youtube_url: Optional[str] = Form(None), | |
user_info=Depends(require_role(["admin", "user_intern"])) | |
): | |
try: | |
responses = [] | |
urls_to_process = [] | |
# Collecter toutes les URLs à traiter | |
if youtube_url: | |
# Cas URL YouTube directe - peut contenir plusieurs URLs séparées par des points-virgules | |
urls_to_process.extend(parse_urls(youtube_url)) | |
elif file and file.content_type in ['text/plain', 'text/csv']: | |
# Cas fichier texte avec URLs | |
text_content = (await file.read()).decode('utf-8') | |
urls_to_process.extend(parse_urls(text_content)) | |
elif file: | |
# Cas upload direct de vidéo | |
content = await file.read() | |
return await process_single_video(content, sport_id, user_info, file.filename) | |
if not urls_to_process and not file: | |
raise HTTPException(status_code=400, detail="Aucune URL YouTube valide trouvée") | |
# Traiter chaque URL | |
for url in urls_to_process: | |
try: | |
print(f"[DEBUG] Processing YouTube URL: {url}") | |
content = await download_youtube_video(url) | |
result = await process_single_video(content, sport_id, user_info, f"YouTube: {url}") | |
responses.append({ | |
"url": url, | |
"status": "success", | |
"video_id": result["video_id"] | |
}) | |
except Exception as e: | |
print(f"[ERROR] Failed to process URL {url}: {str(e)}") | |
responses.append({ | |
"url": url, | |
"status": "error", | |
"error": str(e) | |
}) | |
return { | |
"message": f"Traitement terminé pour {len(responses)} vidéos", | |
"results": responses | |
} | |
except Exception as e: | |
print(f"[ERROR] Upload failed: {str(e)}") | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def process_single_video(content: bytes, sport_id: str, user_info: dict, title: str): | |
"""Traite une seule vidéo et retourne son ID""" | |
video_uuid = str(uuid.uuid4()) | |
active_assignement = True | |
# Calcul du hash MD5 | |
md5_hash = hashlib.md5() | |
md5_hash.update(content) | |
file_hash = md5_hash.hexdigest() | |
# Vérification des doublons | |
existing_videos = db.collection('videos').where('md5_hash', '==', file_hash).get() | |
if len(existing_videos) > 0: | |
return {"message": "Cette vidéo existe déjà", "video_id": existing_videos[0].id} | |
# Création de l'entrée Firestore | |
video_data = { | |
"uuid": video_uuid, | |
"sport_id": sport_id, | |
"upload_date": firestore.SERVER_TIMESTAMP, | |
"uploaded_by": user_info['uid'], | |
"title": title, | |
"status": "downloading", | |
"md5_hash": file_hash, | |
"paths": { | |
"raw": f"{sport_id}/raw/{video_uuid}_raw.mp4", | |
"compressed": f"{sport_id}/compressed/{video_uuid}_compressed.mp4", | |
"reduced_videos": [] | |
}, | |
"scenes": [], | |
"reduced_scenes": [] | |
} | |
db.collection('videos').document(video_uuid).set(video_data) | |
await process_video(video_uuid, content, user_info['uid'], sport_id) | |
if user_info['role'] in ["admin", "user_intern"] and active_assignement: | |
clip_service = ClipAssignmentService() | |
await clip_service.assign_clips_to_user(user_info["uid"], user_info["role"]) | |
return {"message": "Upload initié", "video_id": video_uuid} | |
async def update_video_status( | |
video_id: str, | |
status: str, | |
user_info=Depends(get_user) | |
): | |
try: | |
# Vérifier si la vidéo existe | |
video_ref = db.collection('videos').document(video_id) | |
video_doc = video_ref.get() | |
if not video_doc.exists: | |
raise HTTPException(status_code=404, detail="Vidéo non trouvée") | |
# Mettre à jour ou créer le statut utilisateur | |
user_status_ref = db.collection('user_video_status').document(user_info['uid']) | |
user_status_doc = user_status_ref.get() | |
if user_status_doc.exists: | |
statuses = user_status_doc.get('video_status', []) | |
# Mettre à jour le statut existant ou ajouter un nouveau | |
status_updated = False | |
for s in statuses: | |
if s['uuid'] == video_id: | |
s['status'] = status | |
status_updated = True | |
break | |
if not status_updated: | |
statuses.append({ | |
'uuid': video_id, | |
'status': status | |
}) | |
user_status_ref.update({'video_status': statuses}) | |
else: | |
# Créer un nouveau document avec le statut | |
user_status_ref.set({ | |
'video_status': [{ | |
'uuid': video_id, | |
'status': status | |
}] | |
}) | |
return {"message": "Statut mis à jour avec succès"} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def assign_clips(user_info=Depends(require_role(["admin", "user_intern"]))): | |
try: | |
clip_service = ClipAssignmentService() | |
await clip_service.assign_clips_to_user(user_info["uid"], user_info["role"]) | |
return {"message": "Clips assignés avec succès"} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def remove_clip(clip_id: str, user_info=Depends(require_role(["admin"]))): | |
try: | |
clip_service = ClipAssignmentService() | |
await clip_service.remove_clip_from_user(user_info["uid"], clip_id) | |
return {"message": "Clip retiré avec succès"} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def sync_user_clips(user_info=Depends(require_role(["admin", "user_intern"]))): | |
"""Synchronise les clips disponibles pour l'utilisateur lors de sa connexion""" | |
try: | |
clip_service = ClipAssignmentService() | |
await clip_service.assign_clips_to_user(user_info["uid"], user_info["role"]) | |
return {"message": "Clips synchronisés avec succès"} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def debug_user_clips(user_id: str, user_info=Depends(require_role(["admin"]))): | |
"""Endpoint de débogage pour vérifier les clips d'un utilisateur""" | |
try: | |
user_ref = db.collection('users').document(user_id) | |
user_doc = user_ref.get() | |
if not user_doc.exists: | |
raise HTTPException(status_code=404, detail="Utilisateur non trouvé") | |
user_data = user_doc.to_dict() | |
return { | |
"clips_count": len(user_data.get("clips", [])), | |
"clips": user_data.get("clips", []) | |
} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) |