From 51ec17381f8d96f8943780baba1ae89bad237fe4 Mon Sep 17 00:00:00 2001 From: Viner Abubakirov Date: Sat, 21 Feb 2026 00:19:09 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D1=81?= =?UTF-8?q?=D0=BA=D0=B5=D0=BB=D0=B5=D1=82=20=D1=82=D0=BE=D0=B3=D0=BE,=20?= =?UTF-8?q?=D0=BA=D0=B0=D0=BA=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=B1=D1=83=D0=B4=D0=B5=D1=82=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=81=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__init__.py | 4 ++-- app/api/v1/endpoints/youtube.py | 12 ++++++++++++ app/api/v1/router.py | 8 ++++++++ app/main.py | 32 +++++++++----------------------- app/schemas.py | 9 ++++++--- app/services.py | 13 +++++++++++++ app/utils/uploader.py | 4 ++-- app/utils/youtube.py | 7 ++++++- test.py | 7 ++++--- 9 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 app/api/v1/endpoints/youtube.py diff --git a/app/__init__.py b/app/__init__.py index 807e025..0ebc639 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,4 @@ -# from app.main import app +from app.main import app -# __all__ = ["app"] +__all__ = ["app"] diff --git a/app/api/v1/endpoints/youtube.py b/app/api/v1/endpoints/youtube.py new file mode 100644 index 0000000..285aabd --- /dev/null +++ b/app/api/v1/endpoints/youtube.py @@ -0,0 +1,12 @@ +from fastapi import APIRouter +from app.services import YouTubeService +from app.schemas import DownloadResponse +from app.schemas import DownloadRequest + +router = APIRouter() + + +@router.post("/", response_model=DownloadResponse) +async def download_video(data: DownloadRequest): + response_data = YouTubeService.download(data) + return response_data diff --git a/app/api/v1/router.py b/app/api/v1/router.py index e69de29..0b3eb7d 100644 --- a/app/api/v1/router.py +++ b/app/api/v1/router.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter +from app.api.v1.endpoints import youtube + + +router = APIRouter() + + +router.include_router(youtube.router, prefix="/youtube", tags=["YouTube"]) diff --git a/app/main.py b/app/main.py index 818629b..11098cd 100644 --- a/app/main.py +++ b/app/main.py @@ -1,25 +1,11 @@ -from app.utils.downloader import HttpStreamingDownloader -from app.utils.uploader import DiskChunkUploadBackend -from app.utils.youtube import YtDlpInfo +from fastapi import FastAPI +from app.api.v1.router import router as v1_router + +app = FastAPI() + +app.include_router(v1_router, prefix="/api/v1") -def download(url: str): - upload_backend = DiskChunkUploadBackend("trash_holder") - downloader = HttpStreamingDownloader(upload_backend) - youtube = YtDlpInfo(url) - video_url = youtube.get_video_url("480p") - video_name = youtube.title + ".mp4" - audio_url = youtube.get_audio_url() - audio_name = youtube.title + ".mp4a" - - # downloader.download(video_url, video_name) - downloader.download(audio_url, audio_name) - - -def main(): - url = "https://youtu.be/OSAOh4L41Wg" - download(url) - - -if __name__ == "__main__": - main() +@app.get("/ping") +async def ping() -> str: + return "Pong" diff --git a/app/schemas.py b/app/schemas.py index 0d8ebdb..56edd73 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -3,6 +3,9 @@ from pydantic import BaseModel class DownloadRequest(BaseModel): url: str - quality: str - codec: str - identifier: str + quality: int + + +class DownloadResponse(BaseModel): + video: str + audio: str diff --git a/app/services.py b/app/services.py index e69de29..329290d 100644 --- a/app/services.py +++ b/app/services.py @@ -0,0 +1,13 @@ +from app.utils.youtube import YtDlpManager +from app.utils.uploader import S3ChunkUploadBackend +from app.schemas import DownloadRequest, DownloadResponse + +class YouTubeService: + @staticmethod + def download(data: DownloadRequest): + key_prefix = f"{data.url}@{data.quality}@" + s3 = S3ChunkUploadBackend(key_prefix) + manager = YtDlpManager(data.url, s3) + video_url = manager.download_video(data.quality) + audio_url = manager.download_audio() + return DownloadResponse(video=video_url, audio=audio_url) diff --git a/app/utils/uploader.py b/app/utils/uploader.py index 9f4a070..0dea822 100644 --- a/app/utils/uploader.py +++ b/app/utils/uploader.py @@ -15,7 +15,7 @@ class ChunkUploadBackend(ABC): """Загрузка очередного чанка""" @abstractmethod - def finish(self) -> any: + def finish(self) -> str: """Завершение загрузки""" @abstractmethod @@ -109,7 +109,7 @@ class S3ChunkUploadBackend(ChunkUploadBackend): ) # Сбрасываем части self.parts = [] - return response + return response["Location"] def abort(self) -> None: if self.upload_id: diff --git a/app/utils/youtube.py b/app/utils/youtube.py index 2e9a35f..af7a6f8 100644 --- a/app/utils/youtube.py +++ b/app/utils/youtube.py @@ -5,6 +5,9 @@ from dataclasses import dataclass from app.utils.uploader import ChunkUploadBackend +RESOLUTIONS = [240, 360, 480, 720, 1080, 1440, 2160] + + @dataclass class MediaContent: url: str @@ -36,7 +39,9 @@ class YtDlpManager: """Возвращает title видео""" return self.info.get("title", "unknown") - def download_video(self, size: Literal[240, 360, 480, 720, 1080, 1440, 2160]): + def download_video(self, size: int): + if size not in RESOLUTIONS: + raise RuntimeError("Unsupported quality") command = [ "yt-dlp", "-f", diff --git a/test.py b/test.py index 4aaddeb..2de5173 100644 --- a/test.py +++ b/test.py @@ -4,15 +4,16 @@ from app.utils.youtube import YtDlpManager def download(url: str): + from pprint import pprint # upload_backend = DiskChunkUploadBackend("trash_holder") - upload_backend = S3ChunkUploadBackend("2") + upload_backend = S3ChunkUploadBackend("3") youtube = YtDlpManager(url, upload_backend) print("Download Video") res = youtube.download_video(360) - print(res) + pprint(res) print("Download Audio") res = youtube.download_audio() - print(res) + pprint(res) print("Success") def main():