from typing import List from fastapi import APIRouter, Depends, HTTPException, UploadFile, File from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session from app.db.session import get_db from app.core.deps import get_current_active_user from app.models import User, Message as MessageModel, Media as MediaModel, ChatMember as ChatMemberModel from app.schemas.message import MediaFile from app.services.media_service import media_service import io router = APIRouter() @router.post("/upload/{message_id}", response_model=List[MediaFile]) async def upload_media( message_id: int, files: List[UploadFile] = File(...), current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Upload media files for a message""" # Verify message exists and user can access it message = db.query(MessageModel).filter(MessageModel.id == message_id).first() if not message: raise HTTPException(status_code=404, detail="Message not found") # Verify user is member of the chat membership = db.query(ChatMemberModel).filter( ChatMemberModel.chat_id == message.chat_id, ChatMemberModel.user_id == current_user.id ).first() if not membership: raise HTTPException(status_code=403, detail="Access denied") # Verify user owns the message if message.sender_id != current_user.id: raise HTTPException(status_code=403, detail="Can only upload media to your own messages") media_files = [] for file in files: try: # Save file filename, file_path, file_size, media_type, width, height, thumbnail_path = await media_service.save_file( file, current_user.id ) # Create media record media = MediaModel( message_id=message_id, uploader_id=current_user.id, filename=filename, original_filename=file.filename, file_path=file_path, file_size=file_size, mime_type=file.content_type or "application/octet-stream", media_type=media_type, width=width, height=height, thumbnail_path=thumbnail_path, is_processed=True ) db.add(media) db.commit() db.refresh(media) # Create response media_file = MediaFile( id=media.id, filename=media.filename, original_filename=media.original_filename, file_size=media.file_size, mime_type=media.mime_type, media_type=media.media_type.value, width=media.width, height=media.height, duration=media.duration, thumbnail_url=f"/api/media/{media.id}/thumbnail" if media.thumbnail_path else None ) media_files.append(media_file) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to upload file: {str(e)}") return media_files @router.get("/{media_id}") async def download_media( media_id: int, current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Download media file""" media = db.query(MediaModel).filter(MediaModel.id == media_id).first() if not media: raise HTTPException(status_code=404, detail="Media not found") # Verify user can access this media membership = db.query(ChatMemberModel).filter( ChatMemberModel.chat_id == media.message.chat_id, ChatMemberModel.user_id == current_user.id ).first() if not membership: raise HTTPException(status_code=403, detail="Access denied") try: content = await media_service.get_file_content(media.filename) return StreamingResponse( io.BytesIO(content), media_type=media.mime_type, headers={ "Content-Disposition": f"attachment; filename={media.original_filename}" } ) except FileNotFoundError: raise HTTPException(status_code=404, detail="File not found") @router.get("/{media_id}/thumbnail") async def get_media_thumbnail( media_id: int, current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Get media thumbnail""" media = db.query(MediaModel).filter(MediaModel.id == media_id).first() if not media: raise HTTPException(status_code=404, detail="Media not found") if not media.thumbnail_path: raise HTTPException(status_code=404, detail="Thumbnail not available") # Verify user can access this media membership = db.query(ChatMemberModel).filter( ChatMemberModel.chat_id == media.message.chat_id, ChatMemberModel.user_id == current_user.id ).first() if not membership: raise HTTPException(status_code=403, detail="Access denied") try: content = await media_service.get_thumbnail_content(media.filename) return StreamingResponse( io.BytesIO(content), media_type="image/jpeg" ) except FileNotFoundError: raise HTTPException(status_code=404, detail="Thumbnail not found") @router.delete("/{media_id}") async def delete_media( media_id: int, current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Delete media file""" media = db.query(MediaModel).filter(MediaModel.id == media_id).first() if not media: raise HTTPException(status_code=404, detail="Media not found") # Verify user owns the media or is admin/owner of chat can_delete = media.uploader_id == current_user.id if not can_delete: from app.models.chat_member import MemberRole membership = db.query(ChatMemberModel).filter( ChatMemberModel.chat_id == media.message.chat_id, ChatMemberModel.user_id == current_user.id, ChatMemberModel.role.in_([MemberRole.ADMIN, MemberRole.OWNER]) ).first() can_delete = membership is not None if not can_delete: raise HTTPException(status_code=403, detail="Insufficient permissions") # Delete file from storage thumbnail_filename = f"thumb_{media.filename}" if media.thumbnail_path else None media_service.delete_file(media.filename, thumbnail_filename) # Delete database record db.delete(media) db.commit() return {"message": "Media deleted successfully"} @router.get("/{media_id}/info", response_model=MediaFile) async def get_media_info( media_id: int, current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ): """Get media file information""" media = db.query(MediaModel).filter(MediaModel.id == media_id).first() if not media: raise HTTPException(status_code=404, detail="Media not found") # Verify user can access this media membership = db.query(ChatMemberModel).filter( ChatMemberModel.chat_id == media.message.chat_id, ChatMemberModel.user_id == current_user.id ).first() if not membership: raise HTTPException(status_code=403, detail="Access denied") return MediaFile( id=media.id, filename=media.filename, original_filename=media.original_filename, file_size=media.file_size, mime_type=media.mime_type, media_type=media.media_type.value, width=media.width, height=media.height, duration=media.duration, thumbnail_url=f"/api/media/{media.id}/thumbnail" if media.thumbnail_path else None )