Добавил новые ЭП для новой версии загрузки видео с YouTube
This commit is contained in:
22
app/api/v2/endpoints/youtube.py
Normal file
22
app/api/v2/endpoints/youtube.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from app.schemas import TaskCreateResponse
|
||||||
|
from app.schemas import DownloadRequest
|
||||||
|
from app.tasks import download_youtube
|
||||||
|
from app.services import YouTubeService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/download", response_model=TaskCreateResponse)
|
||||||
|
async def download_video(data: DownloadRequest):
|
||||||
|
task = download_youtube.delay(url=str(data.url), quality=data.quality)
|
||||||
|
return TaskCreateResponse(task_id=task.id, status=task.status)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/resolutions")
|
||||||
|
async def video_resolutions(url: str):
|
||||||
|
return {"resolutions": YouTubeService.resolutions(url)}
|
||||||
|
|
||||||
|
@router.get("/size")
|
||||||
|
async def video_size(data: DownloadRequest):
|
||||||
|
return {"size": YouTubeService.filesize(data)}
|
||||||
8
app/api/v2/router.py
Normal file
8
app/api/v2/router.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from app.api.v2.endpoints import youtube
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
router.include_router(youtube.router, prefix="/youtube", tags=["YouTube"])
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from app.api.v1.router import router as v1_router
|
from app.api.v1.router import router as v1_router
|
||||||
|
from app.api.v2.router import router as v2_router
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
app.include_router(v1_router, prefix="/api/v1")
|
app.include_router(v1_router, prefix="/api/v1")
|
||||||
|
app.include_router(v2_router, prefix="/api/v2")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/ping")
|
@app.get("/ping")
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ class DownloadResponse(BaseModel):
|
|||||||
audio: str
|
audio: str
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadResponseV2(BaseModel):
|
||||||
|
video: str
|
||||||
|
|
||||||
|
|
||||||
class TaskCreateResponse(BaseModel):
|
class TaskCreateResponse(BaseModel):
|
||||||
task_id: str
|
task_id: str
|
||||||
status: str
|
status: str
|
||||||
|
|||||||
@@ -2,19 +2,52 @@ import os
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.uploader import uploader_backend
|
from app.core.uploader import uploader_backend
|
||||||
from app.utils.youtube import YtDlpManager
|
from app.utils.uploader import S3UploadBackend
|
||||||
from app.schemas import DownloadRequest, DownloadResponse
|
from app.schemas import DownloadRequest, DownloadResponse, DownloadResponseV2
|
||||||
|
|
||||||
|
|
||||||
class YouTubeService:
|
class YouTubeService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def download(data: DownloadRequest):
|
def download(data: DownloadRequest):
|
||||||
|
from app.utils.youtube import YtDlpManager
|
||||||
|
|
||||||
manager = YtDlpManager(str(data.url), uploader_backend)
|
manager = YtDlpManager(str(data.url), uploader_backend)
|
||||||
uploader_backend.key_prefix = f"{manager.id}@{data.quality}@"
|
uploader_backend.key_prefix = f"{manager.id}@{data.quality}@"
|
||||||
video_url = manager.download_video(data.quality)
|
video_url = manager.download_video(data.quality)
|
||||||
audio_url = manager.download_audio()
|
audio_url = manager.download_audio()
|
||||||
return DownloadResponse(video=video_url, audio=audio_url)
|
return DownloadResponse(video=video_url, audio=audio_url)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def download_v2(data: DownloadRequest):
|
||||||
|
filepath = None
|
||||||
|
try:
|
||||||
|
from app.utils.youtubeV2 import YtDlpManager
|
||||||
|
|
||||||
|
manager = YtDlpManager(str(data.url))
|
||||||
|
best_audio = manager.best_audio()
|
||||||
|
best_video = manager.best_video(data.quality)
|
||||||
|
filepath = manager.download(best_video, best_audio)
|
||||||
|
upload_backend = S3UploadBackend(f"{manager.id}@{data.quality}@")
|
||||||
|
video_url = upload_backend.upload(os.path.basename(filepath), filepath)
|
||||||
|
return DownloadResponseV2(video=video_url)
|
||||||
|
finally:
|
||||||
|
if filepath:
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolutions(url: str):
|
||||||
|
from app.utils.youtubeV2 import YtDlpManager
|
||||||
|
manager = YtDlpManager(url)
|
||||||
|
return manager.resolutions
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def filesize(data: DownloadRequest):
|
||||||
|
from app.utils.youtubeV2 import YtDlpManager
|
||||||
|
manager = YtDlpManager((data.url))
|
||||||
|
video_size = manager.best_video(data.quality).filesize
|
||||||
|
audio_size = manager.best_audio().filesize
|
||||||
|
return {"filesize": video_size + audio_size}
|
||||||
|
|
||||||
|
|
||||||
class Files:
|
class Files:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
15
app/tasks.py
15
app/tasks.py
@@ -11,8 +11,19 @@ from app.schemas import DownloadRequest
|
|||||||
retry_backoff=True,
|
retry_backoff=True,
|
||||||
)
|
)
|
||||||
def download_youtube(self, url: str, quality: int) -> dict:
|
def download_youtube(self, url: str, quality: int) -> dict:
|
||||||
print("Get Task. Try to make them")
|
|
||||||
request = DownloadRequest(url=url, quality=quality)
|
request = DownloadRequest(url=url, quality=quality)
|
||||||
response = YouTubeService.download(request)
|
response = YouTubeService.download(request)
|
||||||
print("Task make successfully. Return response")
|
return response.model_dump()
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task(
|
||||||
|
bind=True,
|
||||||
|
name="download_youtube_v2",
|
||||||
|
autoretry_for=(Exception,),
|
||||||
|
retry_kwargs={"max_retries": 0},
|
||||||
|
retry_backoff=True,
|
||||||
|
)
|
||||||
|
def download_youtube_v2(self, url: str, quality: int) -> dict:
|
||||||
|
request = DownloadRequest(url=url, quality=quality)
|
||||||
|
response = YouTubeService.download_v2(request)
|
||||||
return response.model_dump()
|
return response.model_dump()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
from botocore.client import Config
|
from botocore.client import Config
|
||||||
@@ -180,9 +181,10 @@ class S3UploadBackend(UploadBackend):
|
|||||||
self.key_prefix = key_prefix
|
self.key_prefix = key_prefix
|
||||||
|
|
||||||
def upload(self, name: str, file: bytes | str):
|
def upload(self, name: str, file: bytes | str):
|
||||||
response = self.s3.upload_file(
|
key = f"{self.key_prefix}{name}"
|
||||||
Filename=file,
|
if isinstance(file, str):
|
||||||
Bucket=self.bucket,
|
self.s3.upload_file(Filename=file, Bucket=self.bucket, Key=key)
|
||||||
Key=f"{self.key_prefix}{name}",
|
else:
|
||||||
)
|
self.s3.put_object(Bucket=self.bucket, Key=key, Body=file)
|
||||||
return response["Location"]
|
encoded_key = quote(key, "")
|
||||||
|
return f"{settings.S3_ENDPOINT_URL}/{self.bucket}/{encoded_key}"
|
||||||
|
|||||||
@@ -66,20 +66,20 @@ class YtDlpManager:
|
|||||||
return MediaInfo(f, f.get("format_id"))
|
return MediaInfo(f, f.get("format_id"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def download(self, video_id: str | None = None, audio_id: str | None = None):
|
def download(self, video: MediaInfo | None = None, audio: MediaInfo | None = None):
|
||||||
if video_id is None and audio_id is None:
|
if video is None and audio is None:
|
||||||
format_id = self.info.get(
|
format_id = self.info.get(
|
||||||
"format_id",
|
"format_id",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
format_id = "" + str(video_id) if video_id is not None else ""
|
format_id = "" + str(video.id) if video is not None else ""
|
||||||
if audio_id is not None:
|
if audio is not None:
|
||||||
if len(format_id) > 0:
|
if len(format_id) > 0:
|
||||||
format_id += "+"
|
format_id += "+"
|
||||||
format_id += str(audio_id)
|
format_id += str(audio.id)
|
||||||
|
|
||||||
ydl_opts = {
|
ydl_opts = {
|
||||||
"format": f"{video_id}+{audio_id}",
|
"format": f"{format_id}",
|
||||||
"merge_output_format": "mp4",
|
"merge_output_format": "mp4",
|
||||||
"outtmpl": f"{settings.MEDIA_DIR}/%(title)s.%(ext)s",
|
"outtmpl": f"{settings.MEDIA_DIR}/%(title)s.%(ext)s",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user