Spaces:
Build error
Build error
| 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)) |