initial commit - LeDiscord plateforme des copains

This commit is contained in:
EvanChal
2025-08-21 00:28:21 +02:00
commit b7a84a53aa
93 changed files with 16247 additions and 0 deletions

View File

@@ -0,0 +1,374 @@
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form
from sqlalchemy.orm import Session
from typing import List
import os
import uuid
from pathlib import Path
from config.database import get_db
from config.settings import settings
from models.vlog import Vlog, VlogLike, VlogComment
from models.user import User
from schemas.vlog import VlogCreate, VlogUpdate, VlogResponse, VlogCommentCreate
from utils.security import get_current_active_user
from utils.video_utils import generate_video_thumbnail, get_video_duration
from utils.settings_service import SettingsService
router = APIRouter()
@router.post("/", response_model=VlogResponse)
async def create_vlog(
vlog_data: VlogCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Create a new vlog."""
vlog = Vlog(
author_id=current_user.id,
**vlog_data.dict()
)
db.add(vlog)
db.commit()
db.refresh(vlog)
return format_vlog_response(vlog, db, current_user.id)
@router.get("/", response_model=List[VlogResponse])
async def get_vlogs(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user),
limit: int = 20,
offset: int = 0
):
"""Get all vlogs."""
vlogs = db.query(Vlog).order_by(Vlog.created_at.desc()).limit(limit).offset(offset).all()
return [format_vlog_response(vlog, db, current_user.id) for vlog in vlogs]
@router.get("/{vlog_id}", response_model=VlogResponse)
async def get_vlog(
vlog_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Get a specific vlog."""
vlog = db.query(Vlog).filter(Vlog.id == vlog_id).first()
if not vlog:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Vlog not found"
)
# Increment view count
vlog.views_count += 1
db.commit()
return format_vlog_response(vlog, db, current_user.id)
@router.put("/{vlog_id}", response_model=VlogResponse)
async def update_vlog(
vlog_id: int,
vlog_update: VlogUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Update a vlog."""
vlog = db.query(Vlog).filter(Vlog.id == vlog_id).first()
if not vlog:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Vlog not found"
)
if vlog.author_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to update this vlog"
)
for field, value in vlog_update.dict(exclude_unset=True).items():
setattr(vlog, field, value)
db.commit()
db.refresh(vlog)
return format_vlog_response(vlog, db, current_user.id)
@router.delete("/{vlog_id}")
async def delete_vlog(
vlog_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Delete a vlog."""
vlog = db.query(Vlog).filter(Vlog.id == vlog_id).first()
if not vlog:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Vlog not found"
)
if vlog.author_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to delete this vlog"
)
# Delete video files
try:
if vlog.video_url:
os.remove(settings.UPLOAD_PATH + vlog.video_url)
if vlog.thumbnail_url:
os.remove(settings.UPLOAD_PATH + vlog.thumbnail_url)
except:
pass
db.delete(vlog)
db.commit()
return {"message": "Vlog deleted successfully"}
@router.post("/{vlog_id}/like")
async def toggle_vlog_like(
vlog_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Toggle like on a vlog."""
vlog = db.query(Vlog).filter(Vlog.id == vlog_id).first()
if not vlog:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Vlog not found"
)
existing_like = db.query(VlogLike).filter(
VlogLike.vlog_id == vlog_id,
VlogLike.user_id == current_user.id
).first()
if existing_like:
# Unlike
db.delete(existing_like)
vlog.likes_count = max(0, vlog.likes_count - 1)
message = "Like removed"
else:
# Like
like = VlogLike(vlog_id=vlog_id, user_id=current_user.id)
db.add(like)
vlog.likes_count += 1
message = "Vlog liked"
db.commit()
return {"message": message, "likes_count": vlog.likes_count}
@router.post("/{vlog_id}/comment")
async def add_vlog_comment(
vlog_id: int,
comment_data: VlogCommentCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Add a comment to a vlog."""
vlog = db.query(Vlog).filter(Vlog.id == vlog_id).first()
if not vlog:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Vlog not found"
)
comment = VlogComment(
vlog_id=vlog_id,
user_id=current_user.id,
content=comment_data.content
)
db.add(comment)
db.commit()
db.refresh(comment)
return {
"message": "Comment added successfully",
"comment": {
"id": comment.id,
"content": comment.content,
"user_id": comment.user_id,
"username": current_user.username,
"full_name": current_user.full_name,
"avatar_url": current_user.avatar_url,
"created_at": comment.created_at
}
}
@router.delete("/{vlog_id}/comment/{comment_id}")
async def delete_vlog_comment(
vlog_id: int,
comment_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Delete a comment from a vlog."""
comment = db.query(VlogComment).filter(
VlogComment.id == comment_id,
VlogComment.vlog_id == vlog_id
).first()
if not comment:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Comment not found"
)
if comment.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to delete this comment"
)
db.delete(comment)
db.commit()
return {"message": "Comment deleted successfully"}
@router.post("/upload")
async def upload_vlog_video(
title: str = Form(...),
description: str = Form(None),
video: UploadFile = File(...),
thumbnail: UploadFile = File(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""Upload a vlog video."""
# Validate video file
if not SettingsService.is_file_type_allowed(db, video.content_type or "unknown"):
allowed_types = SettingsService.get_setting(db, "allowed_video_types",
["video/mp4", "video/mpeg", "video/quicktime", "video/webm"])
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid video type. Allowed types: {', '.join(allowed_types)}"
)
# Check file size
video_content = await video.read()
max_size = SettingsService.get_max_upload_size(db, video.content_type or "video/mp4")
if len(video_content) > max_size:
max_size_mb = max_size // (1024 * 1024)
raise HTTPException(
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
detail=f"Video file too large. Maximum size: {max_size_mb}MB"
)
# Create vlog directory
vlog_dir = Path(settings.UPLOAD_PATH) / "vlogs" / str(current_user.id)
vlog_dir.mkdir(parents=True, exist_ok=True)
# Save video
video_extension = video.filename.split(".")[-1]
video_filename = f"{uuid.uuid4()}.{video_extension}"
video_path = vlog_dir / video_filename
with open(video_path, "wb") as f:
f.write(video_content)
# Process thumbnail
thumbnail_url = None
if thumbnail:
if thumbnail.content_type not in settings.ALLOWED_IMAGE_TYPES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid thumbnail type"
)
thumbnail_content = await thumbnail.read()
thumbnail_extension = thumbnail.filename.split(".")[-1]
thumbnail_filename = f"thumb_{uuid.uuid4()}.{thumbnail_extension}"
thumbnail_path = vlog_dir / thumbnail_filename
with open(thumbnail_path, "wb") as f:
f.write(thumbnail_content)
thumbnail_url = f"/vlogs/{current_user.id}/{thumbnail_filename}"
else:
# Generate automatic thumbnail from video
try:
thumbnail_filename = f"auto_thumb_{uuid.uuid4()}.jpg"
thumbnail_path = vlog_dir / thumbnail_filename
if generate_video_thumbnail(str(video_path), str(thumbnail_path)):
thumbnail_url = f"/vlogs/{current_user.id}/{thumbnail_filename}"
except Exception as e:
print(f"Error generating automatic thumbnail: {e}")
# Continue without thumbnail if generation fails
# Get video duration
duration = None
try:
duration = int(get_video_duration(str(video_path)))
except Exception as e:
print(f"Error getting video duration: {e}")
# Create vlog record
vlog = Vlog(
author_id=current_user.id,
title=title,
description=description,
video_url=f"/vlogs/{current_user.id}/{video_filename}",
thumbnail_url=thumbnail_url,
duration=duration
)
db.add(vlog)
db.commit()
db.refresh(vlog)
return format_vlog_response(vlog, db, current_user.id)
def format_vlog_response(vlog: Vlog, db: Session, current_user_id: int) -> dict:
"""Format vlog response with author information, likes, and comments."""
# Check if current user liked this vlog
is_liked = db.query(VlogLike).filter(
VlogLike.vlog_id == vlog.id,
VlogLike.user_id == current_user_id
).first() is not None
# Format likes
likes = []
for like in vlog.likes:
user = db.query(User).filter(User.id == like.user_id).first()
if user:
likes.append({
"id": like.id,
"user_id": user.id,
"username": user.username,
"full_name": user.full_name,
"avatar_url": user.avatar_url,
"created_at": like.created_at
})
# Format comments
comments = []
for comment in vlog.comments:
user = db.query(User).filter(User.id == comment.user_id).first()
if user:
comments.append({
"id": comment.id,
"user_id": user.id,
"username": user.username,
"full_name": user.full_name,
"avatar_url": user.avatar_url,
"content": comment.content,
"created_at": comment.created_at,
"updated_at": comment.updated_at
})
return {
"id": vlog.id,
"author_id": vlog.author_id,
"title": vlog.title,
"description": vlog.description,
"video_url": vlog.video_url,
"thumbnail_url": vlog.thumbnail_url,
"duration": vlog.duration,
"views_count": vlog.views_count,
"likes_count": vlog.likes_count,
"created_at": vlog.created_at,
"updated_at": vlog.updated_at,
"author_name": vlog.author.full_name,
"author_avatar": vlog.author.avatar_url,
"is_liked": is_liked,
"likes": likes,
"comments": comments
}