This commit is contained in:
Taras Terletsky 2024-08-05 21:12:32 +03:00
parent fb0ce131e7
commit dea09c2614
6 changed files with 36 additions and 58 deletions

View file

@ -1,3 +1,2 @@
fastapi-cache2[redis]==0.2.2
fastapi==0.112.0
uvicorn==0.30.5
fastapi[standart]==0.112.0

View file

@ -13,7 +13,7 @@ from bot.core.utils import bold
class VideoBotClient(Client):
"""Extended Pyrogram's `Client` class."""
_RUN_FOREVER_SLEEP_SECONDS = 86400
_RUN_FOREVER_SLEEP_SECONDS: int = 86400
def __init__(self, *args, conf: ConfigSchema, **kwargs) -> None:
super().__init__(*args, **kwargs)

View file

@ -61,9 +61,10 @@ class MediaDownloader:
)
with yt_dlp.YoutubeDL(ytdl_opts_model.ytdl_opts) as ytdl:
self._log.info('Downloading %s', url)
self._log.info('Downloading to %s', curr_tmp_dir)
self._log.info('Downloading with options %s', ytdl_opts_model.ytdl_opts)
self._log.info('Downloading "%s" to "%s"', url, curr_tmp_dir)
self._log.info(
'Downloading with options: %s', ytdl_opts_model.ytdl_opts
)
meta: dict | None = ytdl.extract_info(url, download=True)
if not meta:

View file

@ -40,25 +40,24 @@ class MediaService:
self._repository = task_repository
self._media_payload = media_payload
async def process(
self,
) -> tuple[DownMedia | None, Task | None]:
task = await self._repository.get_or_create_task(self._media_payload)
if task.status != TaskStatus.PENDING.value:
self._task: Task | None = None
async def process(self) -> tuple[DownMedia | None, Task | None]:
self._task = await self._repository.get_or_create_task(self._media_payload)
if self._task.status != TaskStatus.PENDING.value:
return None, None
return (
await self._process(task=task),
task,
await self._process(),
self._task,
)
async def _process(self, task: Task) -> DownMedia:
host_conf = self._get_host_conf(url=task.url)
await self._repository.save_as_processing(task)
media = await self._start_download(task=task, host_conf=host_conf)
async def _process(self) -> DownMedia:
host_conf = self._get_host_conf()
await self._repository.save_as_processing(self._task)
media = await self._start_download(host_conf=host_conf)
try:
await self._post_process_media(
media=media,
task=task,
host_conf=host_conf,
)
except Exception:
@ -67,17 +66,13 @@ class MediaService:
raise
return media
@staticmethod
def _get_host_conf(url: str) -> AbstractHostConfig:
def _get_host_conf(self) -> AbstractHostConfig:
url = self._task.url
host_to_cls_map = HostConfRegistry.get_host_to_cls_map()
host_cls = host_to_cls_map.get(urlsplit(url).netloc, host_to_cls_map[None])
return host_cls(url=url)
return host_cls(url)
async def _start_download(
self,
task: Task,
host_conf: AbstractHostConfig,
) -> DownMedia:
async def _start_download(self, host_conf: AbstractHostConfig) -> DownMedia:
try:
return await asyncio.get_running_loop().run_in_executor(
None,
@ -90,26 +85,21 @@ class MediaService:
self._log.exception(
'Failed to download media. Context: %s', self._media_payload
)
await self._handle_download_exception(err, task)
raise DownloadVideoServiceError(message=str(err), task=task)
await self._handle_download_exception(err)
raise DownloadVideoServiceError(message=str(err), task=self._task)
async def _post_process_media(
self,
media: DownMedia,
task: Task,
host_conf: AbstractHostConfig,
self, media: DownMedia, host_conf: AbstractHostConfig
) -> None:
def post_process_audio():
return self._post_process_audio(
media=media,
task=task,
host_conf=host_conf,
)
def post_process_video():
return self._post_process_video(
media=media,
task=task,
host_conf=host_conf,
)
@ -121,13 +111,10 @@ class MediaService:
case DownMediaType.AUDIO_VIDEO:
await asyncio.gather(*(post_process_audio(), post_process_video()))
await self._repository.save_as_done(task)
await self._repository.save_as_done(self._task)
async def _post_process_video(
self,
media: DownMedia,
task: Task,
host_conf: AbstractHostConfig,
self, media: DownMedia, host_conf: AbstractHostConfig
) -> None:
"""Post-process downloaded media files, e.g. make thumbnail and copy to storage."""
video = media.video
@ -138,7 +125,7 @@ class MediaService:
try:
await self._set_probe_ctx(video)
except RuntimeError as err:
raise DownloadVideoServiceError(message=str(err), task=task)
raise DownloadVideoServiceError(message=str(err), task=self._task)
coro_tasks = []
@ -173,16 +160,13 @@ class MediaService:
await asyncio.gather(*coro_tasks)
file = await self._repository.save_file(task, media.video, media.meta)
file = await self._repository.save_file(self._task, media.video, media.meta)
video.orm_file_id = file.id
async def _post_process_audio(
self,
media: DownMedia,
task: Task,
host_conf: AbstractHostConfig,
self, media: DownMedia, host_conf: AbstractHostConfig
) -> None:
coro_tasks = [self._repository.save_file(task, media.audio, media.meta)]
coro_tasks = [self._repository.save_file(self._task, media.audio, media.meta)]
if self._media_payload.save_to_storage:
coro_tasks.append(self._create_copy_file_task(media.audio))
results = await asyncio.gather(*coro_tasks)
@ -218,11 +202,7 @@ class MediaService:
)
def _create_thumb_task(
self,
file_path: Path,
thumb_path: Path,
duration: float,
video_ctx: Video,
self, file_path: Path, thumb_path: Path, duration: float, video_ctx: Video
) -> asyncio.Task:
return create_task(
MakeThumbnailTask(
@ -253,5 +233,5 @@ class MediaService:
self._log.info('Performing error cleanup: removing %s', video.root_path)
remove_dir(video.root_path)
async def _handle_download_exception(self, err: Exception, task: Task) -> None:
await self._repository.save_as_failed(task=task, error_message=str(err))
async def _handle_download_exception(self, err: Exception) -> None:
await self._repository.save_as_failed(task=self._task, error_message=str(err))

View file

@ -2,7 +2,7 @@ import uuid
from abc import ABC
from datetime import datetime, timezone
from pathlib import Path
from typing import Literal
from typing import Annotated, Literal, Self
from PIL import Image
from pydantic import (
@ -12,7 +12,6 @@ from pydantic import (
FilePath,
model_validator,
)
from typing_extensions import Annotated, Self
from yt_shared.enums import DownMediaType, MediaFileType, TaskSource, TelegramChatType
from yt_shared.schemas.base import StrictRealBaseModel

View file

@ -23,8 +23,7 @@ def file_size(filepath: Path) -> int:
return filepath.stat().st_size
def list_files_human(path: Path) -> dict[Path, str]:
def list_files_human(path: Path) -> dict[str, str]:
return {
filename: format_bytes(file_size(path / filename))
for filename in path.iterdir()
filepath.name: format_bytes(file_size(filepath)) for filepath in path.iterdir()
}