diff --git a/api/api/api_v1/endpoints/ytdlp.py b/api/api/api_v1/endpoints/ytdlp.py index c7ee871..c283ac1 100644 --- a/api/api/api_v1/endpoints/ytdlp.py +++ b/api/api/api_v1/endpoints/ytdlp.py @@ -1,5 +1,3 @@ -import asyncio - from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession @@ -14,8 +12,7 @@ router = APIRouter() response_model_by_alias=False) async def yt_dlp_version(db: AsyncSession = Depends(get_db)): version_checker = VersionChecker() - latest, current = await asyncio.gather( - version_checker.get_latest_version(), - version_checker.get_current_version(db)) - return YTDLPLatestVersion(current=current, latest=latest, - need_upgrade=latest.version != current.version) + ctx = await version_checker.get_version_context(db) + + return YTDLPLatestVersion(current=ctx.current, latest=ctx.latest, + need_upgrade=ctx.has_new_version) diff --git a/api/api/api_v1/schemas/ytdlp.py b/api/api/api_v1/schemas/ytdlp.py index 53b8c65..4261790 100644 --- a/api/api/api_v1/schemas/ytdlp.py +++ b/api/api/api_v1/schemas/ytdlp.py @@ -1,8 +1,8 @@ from yt_shared.schemas.base import RealBaseModel -from yt_shared.schemas.ytdlp import Current, Latest +from yt_shared.schemas.ytdlp import CurrentVersion, LatestVersion class YTDLPLatestVersion(RealBaseModel): - latest: Latest - current: Current + latest: LatestVersion + current: CurrentVersion need_upgrade: bool diff --git a/bot/core/tasks/ytdlp.py b/bot/core/tasks/ytdlp.py index 174a632..aa76965 100644 --- a/bot/core/tasks/ytdlp.py +++ b/bot/core/tasks/ytdlp.py @@ -9,7 +9,7 @@ from core.utils.utils import bold, code from yt_shared.config import YTDLP_VERSION_CHECK_INTERVAL from yt_shared.db import get_db from yt_shared.emoji import INFORMATION_EMOJI -from yt_shared.schemas.ytdlp import Current, Latest +from yt_shared.schemas.ytdlp import VersionContext from yt_shared.ytdlp.version_checker import VersionChecker if TYPE_CHECKING: @@ -32,7 +32,7 @@ class YtdlpNewVersionNotifyTask(AbstractTask): except Exception: self._log.exception('Failed check new yt-dlp version') self._log.info( - 'Next check for new yt-dlp version planned at %s', + 'Next yt-dlp version check planned at %s', self._get_next_check_datetime().isoformat(' ')) await asyncio.sleep(YTDLP_VERSION_CHECK_INTERVAL) @@ -44,28 +44,25 @@ class YtdlpNewVersionNotifyTask(AbstractTask): async def _notify_if_new_version(self) -> None: async for db in get_db(): - latest, current = await asyncio.gather( - self._version_checker.get_latest_version(), - self._version_checker.get_current_version(db)) - - if latest.version != current.version: - await self._notify_outdated(latest, current) + context = await self._version_checker.get_version_context(db) + if context.has_new_version: + await self._notify_outdated(context) elif not self._startup_message_sent: - await self._notify_up_to_date(current) + await self._notify_up_to_date(context) self._startup_message_sent = True - async def _notify_outdated(self, latest: Latest, current: Current) -> None: + async def _notify_outdated(self, ctx: VersionContext) -> None: text = ( - f'New {code("yt-dlp")} version available: {bold(latest.version)}\n' - f'Current version: {bold(current.version)}\n' + f'New {code("yt-dlp")} version available: {bold(ctx.latest.version)}\n' + f'Current version: {bold(ctx.current.version)}\n' f'Rebuild worker with {code("docker-compose build --no-cache worker")}' ) await self._send_to_chat(text) - async def _notify_up_to_date(self, current: Current) -> None: + async def _notify_up_to_date(self, ctx: VersionContext) -> None: """Send startup message that yt-dlp version is up to date.""" text = f'{INFORMATION_EMOJI} Your {code("yt-dlp")} version ' \ - f'{bold(current.version)} is up to date. Have fun.' + f'{bold(ctx.current.version)} is up to date. Have fun.' await self._send_to_chat(text) async def _send_to_chat(self, text: str) -> None: diff --git a/yt_shared/yt_shared/schemas/ytdlp.py b/yt_shared/yt_shared/schemas/ytdlp.py index d3f3161..09044e5 100644 --- a/yt_shared/yt_shared/schemas/ytdlp.py +++ b/yt_shared/yt_shared/schemas/ytdlp.py @@ -1,4 +1,5 @@ import datetime +from typing import Optional from pydantic import Field, StrictStr, validator @@ -9,7 +10,7 @@ def _remove_microseconds(dt_obj: datetime.datetime) -> datetime.datetime: return dt_obj.replace(microsecond=0) -class Latest(RealBaseModel): +class LatestVersion(RealBaseModel): version: StrictStr retrieved_at: datetime.datetime @@ -18,7 +19,7 @@ class Latest(RealBaseModel): return _remove_microseconds(value) -class Current(RealBaseModel): +class CurrentVersion(RealBaseModel): version: StrictStr = Field(..., alias='current_version') updated_at: datetime.datetime @@ -28,3 +29,14 @@ class Current(RealBaseModel): class Config(RealBaseModel.Config): orm_mode = True + + +class VersionContext(RealBaseModel): + latest: LatestVersion + current: CurrentVersion + has_new_version: Optional[bool] + + @validator('has_new_version', always=True) + def check_new_version(cls, value, values: dict) -> bool: + return [int(x) for x in values['latest'].version.split('.')] > \ + [int(x) for x in values['current'].version.split('.')] diff --git a/yt_shared/yt_shared/ytdlp/version_checker.py b/yt_shared/yt_shared/ytdlp/version_checker.py index f772b84..8c059d4 100644 --- a/yt_shared/yt_shared/ytdlp/version_checker.py +++ b/yt_shared/yt_shared/ytdlp/version_checker.py @@ -1,3 +1,4 @@ +import asyncio import datetime import logging @@ -5,7 +6,11 @@ import aiohttp from sqlalchemy.ext.asyncio import AsyncSession from yt_shared.repositories.ytdlp import YtdlpRepository -from yt_shared.schemas.ytdlp import Current, Latest +from yt_shared.schemas.ytdlp import ( + CurrentVersion, + LatestVersion, + VersionContext, +) class VersionChecker: @@ -17,18 +22,23 @@ class VersionChecker: def __init__(self) -> None: self._log = logging.getLogger(self.__class__.__name__) - async def get_latest_version(self) -> Latest: + async def get_version_context(self, db: AsyncSession) -> VersionContext: + latest, current = await asyncio.gather(self.get_latest_version(), + self.get_current_version(db)) + return VersionContext(latest=latest, current=current) + + async def get_latest_version(self) -> LatestVersion: self._log.info('Get latest yt-dlp version number') async with aiohttp.ClientSession() as session: async with session.get(self.LATEST_TAG_URL) as resp: version = resp.url.parts[-1] self._log.info('Latest yt-dlp version number: %s', version) - return Latest(version=version, - retrieved_at=datetime.datetime.utcnow()) + return LatestVersion(version=version, + retrieved_at=datetime.datetime.utcnow()) - async def get_current_version(self, db: AsyncSession) -> Current: + async def get_current_version(self, db: AsyncSession) -> CurrentVersion: self._log.info('Get current yt-dlp version number') ytdlp_ = await self.REPOSITORY_CLS().get_current_version(db) self._log.info('Current yt-dlp version number: %s', ytdlp_.current_version) - return Current.from_orm(ytdlp_) + return CurrentVersion.from_orm(ytdlp_)