diff --git a/pyproject.toml b/pyproject.toml index 8585da6..f9711fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ testcontainers = "4.3.1" profile = "black" [tool.mypy] -packages= "src" +packages = "src" [build-system] requires = ["poetry-core"] diff --git a/src/dependencies.py b/src/dependencies.py new file mode 100644 index 0000000..9ef0a5b --- /dev/null +++ b/src/dependencies.py @@ -0,0 +1,23 @@ +from functools import lru_cache +from typing import Annotated + +from fastapi import Depends + +from .services.member import MemberService +from .services.video import VideoService +from .settings import Settings + + +@lru_cache +def get_settings(): + return Settings() + + +def get_member_service(settings: Annotated[Settings, Depends(get_settings)]): + """Get the member service.""" + return MemberService(settings.server_base_path) + + +def get_video_service(settings: Annotated[Settings, Depends(get_settings)]): + """Get the video service.""" + return VideoService(settings.server_base_path) diff --git a/src/main.py b/src/main.py index dae1d44..fac5702 100644 --- a/src/main.py +++ b/src/main.py @@ -4,17 +4,18 @@ from fastapi import FastAPI +from .dependencies import get_settings from .routers import health, member, video from .services.member import MemberService from .services.video import VideoService -member_service = MemberService() -video_service = VideoService() - @asynccontextmanager async def lifespan(api: FastAPI): # pylint: disable=unused-argument """Create the base paths for the video and member folders on startup.""" + settings = get_settings() + member_service = MemberService(settings.server_base_path) + video_service = VideoService(settings.server_base_path) video_service.create_base_path() member_service.create_base_path() yield diff --git a/src/routers/member.py b/src/routers/member.py index edb2acc..9b1a464 100644 --- a/src/routers/member.py +++ b/src/routers/member.py @@ -1,10 +1,12 @@ """Member endpoints.""" import re +from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, Response, UploadFile, status +from ..dependencies import get_member_service from ..models.member import Member from ..security import authorize from ..services.member import MemberService @@ -12,26 +14,33 @@ router = APIRouter( tags=["Member"], prefix="/api/v1/member", dependencies=[Depends(authorize)] ) -service: MemberService = MemberService() -@router.post("", response_model=Member) -def create_member_folder(member: Member): +@router.post("") +def create_member_folder( + member: Member, + service: Annotated[MemberService, Depends(get_member_service)], +): """ Create a folder structure for a member and return the member object. :param member: Member object + :param service: service dependency :return: 200 and the original member object """ service.create_folder_structure(member) return member -@router.put("", response_model=Member) -def update_member_folder(member: Member): +@router.put("") +def update_member_folder( + member: Member, + service: Annotated[MemberService, Depends(get_member_service)], +): """ Update the folder structure for a member and return the member object. If the member does not exist, return a 404. :param member: Member object + :param service: service dependency :return: 200 and the original member object """ if not service.to_id_path(member.id).exists(): @@ -40,8 +49,12 @@ def update_member_folder(member: Member): return member -@router.post("/{member_id}/profilePicture", response_model=UUID) -async def upload_member_picture(member_id: UUID, file: UploadFile): +@router.post("/{member_id}/profilePicture") +async def upload_member_picture( + member_id: UUID, + file: UploadFile, + service: Annotated[MemberService, Depends(get_member_service)], +): """ Upload a picture for a member to convert and store the profile picture in different formats @@ -49,6 +62,7 @@ async def upload_member_picture(member_id: UUID, file: UploadFile): If the file is not an image, return a 500. :param member_id: the id of the member :param file: the image file + :param service: service dependency :return: 200 and the original member_id """ # pylint: disable=duplicate-code diff --git a/src/routers/video.py b/src/routers/video.py index 1a1360f..c0c99fb 100644 --- a/src/routers/video.py +++ b/src/routers/video.py @@ -1,10 +1,12 @@ """Video endpoints""" import re +from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, Response, UploadFile, status +from ..dependencies import get_video_service from ..models.video import Video from ..security import authorize from ..services.video import VideoService @@ -12,11 +14,13 @@ router = APIRouter( tags=["Video"], prefix="/api/v1/video", dependencies=[Depends(authorize)] ) -service: VideoService = VideoService() @router.post("", response_model=Video) -def create_video_folder(video: Video): +def create_video_folder( + video: Video, + service: Annotated[VideoService, Depends(get_video_service)], +): """ Create a folder structure for a video and return the video object. :param video: Video object @@ -26,8 +30,11 @@ def create_video_folder(video: Video): return video -@router.put("", response_model=Video) -def update_video_folder(video: Video): +@router.put("") +def update_video_folder( + video: Video, + service: Annotated[VideoService, Depends(get_video_service)], +): """ Update the folder structure for a video and return the video object. If the video does not exist, return a 404. @@ -40,8 +47,12 @@ def update_video_folder(video: Video): return video -@router.post("/{video_id}/thumbnail", response_model=UUID) -async def upload_video_poster(video_id: UUID, file: UploadFile): +@router.post("/{video_id}/thumbnail") +async def upload_video_poster( + video_id: UUID, + file: UploadFile, + service: Annotated[VideoService, Depends(get_video_service)], +): """ Upload a picture for a video thumbnail to convert and store the thumbnail in different formats diff --git a/src/services/member.py b/src/services/member.py index 61ae718..645f7ba 100644 --- a/src/services/member.py +++ b/src/services/member.py @@ -4,16 +4,20 @@ from uuid import UUID from ..models.member import Member -from ..settings import settings from .image import ImgFormat, create_images class MemberService: """Member service class.""" - def __init__(self, base_path: str = settings.server_base_path): + def __init__(self, base_path: str): self.id_paths_base = Path(base_path, "m") self.url_paths_base = Path(base_path, "member") + self.profile_picture_sizes = [ + ImgFormat(1920, 1080, "xl"), + ImgFormat(1280, 720, "l"), + ImgFormat(854, 480, "m"), + ] def create_folder_structure(self, member: Member): """ @@ -39,12 +43,7 @@ def create_profile_picture(self, img_file: bytes, member_id: UUID): :return: None """ profile_picture_path = Path(self.to_id_path(member_id), "profile") - profile_picture_sizes = [ - ImgFormat(1920, 1080, "xl"), - ImgFormat(1280, 720, "l"), - ImgFormat(854, 480, "m"), - ] - create_images(img_file, profile_picture_path, profile_picture_sizes) + create_images(img_file, profile_picture_path, self.profile_picture_sizes) def update_symlink(self, member: Member): """ diff --git a/src/services/video.py b/src/services/video.py index 5afd6c3..3d5bd6f 100644 --- a/src/services/video.py +++ b/src/services/video.py @@ -4,16 +4,20 @@ from uuid import UUID from ..models.video import Video -from ..settings import settings from .image import ImgFormat, create_images class VideoService: """Video service class.""" - def __init__(self, base_path: str = settings.server_base_path): + def __init__(self, base_path: str): self.id_paths_base = Path(base_path, "v") self.url_paths_base = Path(base_path, "video") + self.poster_sizes = [ + ImgFormat(1920, 1080, "fhd"), + ImgFormat(1280, 720, "hd"), + ImgFormat(854, 480, "sd"), + ] def create_folder_structure(self, video: Video): """ @@ -41,12 +45,7 @@ def create_thumbnails(self, img_file: bytes, video_id: UUID): :return: None """ thumbnail_path = Path(self.to_id_path(video_id), "thumbnail") - poster_sizes = [ - ImgFormat(1920, 1080, "fhd"), - ImgFormat(1280, 720, "hd"), - ImgFormat(854, 480, "sd"), - ] - create_images(img_file, thumbnail_path, poster_sizes) + create_images(img_file, thumbnail_path, self.poster_sizes) def update_symlinks(self, video: Video): """