mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 03:57:13 -05:00
279 lines
7.9 KiB
Python
279 lines
7.9 KiB
Python
"""Video streaming (BlackStream) routes"""
|
|
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File as FastAPIFile
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, and_, desc
|
|
from typing import List, Optional
|
|
from pydantic import BaseModel
|
|
from datetime import datetime
|
|
|
|
from app.database import get_db
|
|
from app.models.user import User
|
|
from app.models.video import Video, VideoView, VideoLike
|
|
from app.auth import get_current_active_user
|
|
from app.utils import utc_now
|
|
|
|
router = APIRouter(prefix="/api/videos", tags=["Videos"])
|
|
|
|
|
|
class VideoCreate(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
video_url: str
|
|
thumbnail_url: Optional[str] = None
|
|
category: Optional[str] = None
|
|
tags: Optional[str] = None
|
|
|
|
|
|
class VideoResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
username: str
|
|
avatar_url: Optional[str]
|
|
title: str
|
|
description: Optional[str]
|
|
thumbnail_url: Optional[str]
|
|
video_url: str
|
|
duration: Optional[int]
|
|
views_count: int
|
|
likes_count: int
|
|
dislikes_count: int
|
|
comments_count: int
|
|
is_public: bool
|
|
created_at: datetime
|
|
is_liked: Optional[bool] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
@router.get("/", response_model=List[VideoResponse])
|
|
async def get_videos(
|
|
db: AsyncSession = Depends(get_db),
|
|
category: Optional[str] = None,
|
|
limit: int = 20,
|
|
offset: int = 0,
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
"""Get videos"""
|
|
query = select(Video, User).join(User, Video.user_id == User.id).where(Video.is_public == True)
|
|
|
|
if category:
|
|
query = query.where(Video.category == category)
|
|
|
|
query = query.order_by(desc(Video.created_at)).limit(limit).offset(offset)
|
|
|
|
result = await db.execute(query)
|
|
videos_with_users = result.all()
|
|
|
|
# Check which videos current user has liked
|
|
video_ids = [video.id for video, _ in videos_with_users]
|
|
liked_result = await db.execute(
|
|
select(VideoLike)
|
|
.where(
|
|
and_(
|
|
VideoLike.user_id == current_user.id,
|
|
VideoLike.video_id.in_(video_ids),
|
|
VideoLike.is_like == True
|
|
)
|
|
)
|
|
)
|
|
liked_video_ids = {like.video_id for like in liked_result.scalars().all()}
|
|
|
|
return [
|
|
VideoResponse(
|
|
id=video.id,
|
|
user_id=video.user_id,
|
|
username=user.username,
|
|
avatar_url=user.avatar_url,
|
|
title=video.title,
|
|
description=video.description,
|
|
thumbnail_url=video.thumbnail_url,
|
|
video_url=video.video_url,
|
|
duration=video.duration,
|
|
views_count=video.views_count,
|
|
likes_count=video.likes_count,
|
|
dislikes_count=video.dislikes_count,
|
|
comments_count=video.comments_count,
|
|
is_public=video.is_public,
|
|
created_at=video.created_at,
|
|
is_liked=video.id in liked_video_ids
|
|
)
|
|
for video, user in videos_with_users
|
|
]
|
|
|
|
|
|
@router.post("/", response_model=VideoResponse, status_code=status.HTTP_201_CREATED)
|
|
async def upload_video(
|
|
video_data: VideoCreate,
|
|
current_user: User = Depends(get_current_active_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Upload a video"""
|
|
video = Video(
|
|
user_id=current_user.id,
|
|
title=video_data.title,
|
|
description=video_data.description,
|
|
video_url=video_data.video_url,
|
|
thumbnail_url=video_data.thumbnail_url,
|
|
category=video_data.category,
|
|
tags=video_data.tags,
|
|
is_public=True,
|
|
published_at=utc_now()
|
|
)
|
|
|
|
db.add(video)
|
|
await db.commit()
|
|
await db.refresh(video)
|
|
|
|
return VideoResponse(
|
|
id=video.id,
|
|
user_id=video.user_id,
|
|
username=current_user.username,
|
|
avatar_url=current_user.avatar_url,
|
|
title=video.title,
|
|
description=video.description,
|
|
thumbnail_url=video.thumbnail_url,
|
|
video_url=video.video_url,
|
|
duration=video.duration,
|
|
views_count=0,
|
|
likes_count=0,
|
|
dislikes_count=0,
|
|
comments_count=0,
|
|
is_public=True,
|
|
created_at=video.created_at,
|
|
is_liked=False
|
|
)
|
|
|
|
|
|
@router.get("/{video_id}", response_model=VideoResponse)
|
|
async def get_video(
|
|
video_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
"""Get a specific video"""
|
|
result = await db.execute(
|
|
select(Video, User)
|
|
.join(User, Video.user_id == User.id)
|
|
.where(Video.id == video_id)
|
|
)
|
|
video_with_user = result.first()
|
|
|
|
if not video_with_user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Video not found"
|
|
)
|
|
|
|
video, user = video_with_user
|
|
|
|
# Record view
|
|
view = VideoView(
|
|
video_id=video.id,
|
|
user_id=current_user.id
|
|
)
|
|
db.add(view)
|
|
video.views_count += 1
|
|
await db.commit()
|
|
|
|
# Check if liked
|
|
liked_result = await db.execute(
|
|
select(VideoLike)
|
|
.where(
|
|
and_(
|
|
VideoLike.user_id == current_user.id,
|
|
VideoLike.video_id == video_id,
|
|
VideoLike.is_like == True
|
|
)
|
|
)
|
|
)
|
|
is_liked = liked_result.scalar_one_or_none() is not None
|
|
|
|
return VideoResponse(
|
|
id=video.id,
|
|
user_id=video.user_id,
|
|
username=user.username,
|
|
avatar_url=user.avatar_url,
|
|
title=video.title,
|
|
description=video.description,
|
|
thumbnail_url=video.thumbnail_url,
|
|
video_url=video.video_url,
|
|
duration=video.duration,
|
|
views_count=video.views_count,
|
|
likes_count=video.likes_count,
|
|
dislikes_count=video.dislikes_count,
|
|
comments_count=video.comments_count,
|
|
is_public=video.is_public,
|
|
created_at=video.created_at,
|
|
is_liked=is_liked
|
|
)
|
|
|
|
|
|
@router.post("/{video_id}/like")
|
|
async def like_video(
|
|
video_id: int,
|
|
is_like: bool = True,
|
|
current_user: User = Depends(get_current_active_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Like or dislike a video"""
|
|
# Check if video exists
|
|
result = await db.execute(select(Video).where(Video.id == video_id))
|
|
video = result.scalar_one_or_none()
|
|
|
|
if not video:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Video not found"
|
|
)
|
|
|
|
# Check if already liked/disliked
|
|
result = await db.execute(
|
|
select(VideoLike).where(
|
|
and_(
|
|
VideoLike.user_id == current_user.id,
|
|
VideoLike.video_id == video_id
|
|
)
|
|
)
|
|
)
|
|
existing_like = result.scalar_one_or_none()
|
|
|
|
if existing_like:
|
|
# Update or remove
|
|
if existing_like.is_like == is_like:
|
|
# Remove like/dislike
|
|
await db.delete(existing_like)
|
|
if is_like:
|
|
video.likes_count = max(0, video.likes_count - 1)
|
|
else:
|
|
video.dislikes_count = max(0, video.dislikes_count - 1)
|
|
else:
|
|
# Change from like to dislike or vice versa
|
|
existing_like.is_like = is_like
|
|
if is_like:
|
|
video.likes_count += 1
|
|
video.dislikes_count = max(0, video.dislikes_count - 1)
|
|
else:
|
|
video.dislikes_count += 1
|
|
video.likes_count = max(0, video.likes_count - 1)
|
|
else:
|
|
# New like/dislike
|
|
like = VideoLike(
|
|
user_id=current_user.id,
|
|
video_id=video_id,
|
|
is_like=is_like
|
|
)
|
|
db.add(like)
|
|
if is_like:
|
|
video.likes_count += 1
|
|
else:
|
|
video.dislikes_count += 1
|
|
|
|
await db.commit()
|
|
|
|
return {
|
|
"liked": is_like if existing_like is None or existing_like.is_like != is_like else None,
|
|
"likes_count": video.likes_count,
|
|
"dislikes_count": video.dislikes_count
|
|
}
|