mirror of
https://github.com/tropicoo/yt-dlp-bot.git
synced 2024-12-27 18:10:30 +08:00
Version 1.4.4
This commit is contained in:
parent
dcb61b84f1
commit
bf881afccc
13 changed files with 112 additions and 29 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
Simple and reliable self-hosted YouTube Download Telegram Bot.
|
||||
|
||||
Version: 1.4.3. [Release details](RELEASES.md).
|
||||
Version: 1.4.4. [Release details](RELEASES.md).
|
||||
|
||||
![frames](.assets/download_success.png)
|
||||
|
||||
|
|
24
RELEASES.md
24
RELEASES.md
|
@ -1,3 +1,27 @@
|
|||
## Release 1.4.4
|
||||
|
||||
Release date: November 11, 2023
|
||||
|
||||
## New Features
|
||||
|
||||
- New boolean config variable per user `is_admin`. Currently, admin users will receive update message when `yt-dlp` needs update.
|
||||
```yaml
|
||||
allowed_users:
|
||||
- id: 11111111111
|
||||
is_admin: !!bool True
|
||||
***
|
||||
```
|
||||
|
||||
## Important
|
||||
|
||||
- Every user in the config must include this new config variable whether it's `True` or `False`
|
||||
|
||||
## Misc
|
||||
|
||||
N/A
|
||||
|
||||
---
|
||||
|
||||
## Release 1.4.3
|
||||
|
||||
Release date: September 20, 2023
|
||||
|
|
|
@ -69,6 +69,7 @@ class TaskService:
|
|||
payload = InbMediaPayload(
|
||||
id=task_id,
|
||||
url=task.url,
|
||||
original_url=task.url,
|
||||
added_at=added_at,
|
||||
source=source,
|
||||
download_media_type=task.download_media_type,
|
||||
|
|
|
@ -27,9 +27,13 @@ class VideoBot(Client):
|
|||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
self._log.info('Initializing bot client')
|
||||
|
||||
self.allowed_users: dict[int, UserSchema] = {
|
||||
user.id: user for user in self.conf.telegram.allowed_users
|
||||
}
|
||||
self.allowed_users: dict[int, UserSchema] = {}
|
||||
self.admin_users: dict[int, UserSchema] = {}
|
||||
|
||||
for user in self.conf.telegram.allowed_users:
|
||||
self.allowed_users[user.id] = user
|
||||
if user.is_admin:
|
||||
self.admin_users[user.id] = user
|
||||
|
||||
async def run_forever(self) -> None:
|
||||
"""Firstly 'await bot.start()' should be called."""
|
||||
|
@ -77,3 +81,13 @@ class VideoBot(Client):
|
|||
user_ids=self.allowed_users.keys(),
|
||||
parse_mode=parse_mode,
|
||||
)
|
||||
|
||||
async def send_message_admins(
|
||||
self, text: str, parse_mode: ParseMode = ParseMode.HTML
|
||||
) -> None:
|
||||
"""Send message to all defined user IDs in config.json."""
|
||||
await self.send_message_to_users(
|
||||
text=text,
|
||||
user_ids=self.admin_users.keys(),
|
||||
parse_mode=parse_mode,
|
||||
)
|
||||
|
|
|
@ -31,10 +31,12 @@ class BotLauncher:
|
|||
|
||||
def _setup_handlers(self) -> None:
|
||||
cb = TelegramCallback()
|
||||
allowed_users = list(self._bot.allowed_users.keys())
|
||||
|
||||
self._bot.add_handler(
|
||||
MessageHandler(
|
||||
cb.on_start,
|
||||
filters=filters.user(list(self._bot.allowed_users.keys()))
|
||||
filters=filters.user(allowed_users)
|
||||
& filters.command(['start', 'help']),
|
||||
),
|
||||
)
|
||||
|
@ -43,10 +45,7 @@ class BotLauncher:
|
|||
cb.on_message,
|
||||
filters=(
|
||||
filters.regex(self.REGEX_NOT_START_WITH_SLASH)
|
||||
& (
|
||||
filters.user(list(self._bot.allowed_users.keys()))
|
||||
| filters.chat(list(self._bot.allowed_users.keys()))
|
||||
)
|
||||
& (filters.user(allowed_users) | filters.chat(allowed_users))
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -38,6 +38,7 @@ class UploadSchema(RealBaseModel):
|
|||
|
||||
|
||||
class UserSchema(AnonymousUserSchema):
|
||||
is_admin: StrictBool
|
||||
send_startup_message: StrictBool
|
||||
download_media_type: DownMediaType
|
||||
save_to_storage: StrictBool
|
||||
|
|
|
@ -3,6 +3,7 @@ import os
|
|||
import traceback
|
||||
|
||||
from pyrogram.enums import ParseMode
|
||||
from pyrogram.errors import MessageIdInvalid, MessageNotModified
|
||||
from yt_shared.emoji import SUCCESS_EMOJI
|
||||
from yt_shared.enums import MediaFileType, TaskSource
|
||||
from yt_shared.rabbit.publisher import RmqPublisher
|
||||
|
@ -19,6 +20,8 @@ from bot.core.utils import bold
|
|||
|
||||
|
||||
class SuccessDownloadHandler(AbstractDownloadHandler):
|
||||
"""Handle successfully downloaded media context."""
|
||||
|
||||
_body: SuccessDownloadPayload
|
||||
_UPLOAD_TASK_MAP = {
|
||||
MediaFileType.AUDIO: AudioUploadTask,
|
||||
|
@ -36,10 +39,13 @@ class SuccessDownloadHandler(AbstractDownloadHandler):
|
|||
self._cleanup()
|
||||
|
||||
async def _handle(self) -> None:
|
||||
coro_tasks = [self._delete_acknowledge_message()]
|
||||
coro_tasks = []
|
||||
for media_object in self._body.media.get_media_objects():
|
||||
coro_tasks.append(self._handle_media_object(media_object))
|
||||
await asyncio.gather(*coro_tasks)
|
||||
try:
|
||||
await asyncio.gather(*coro_tasks)
|
||||
finally:
|
||||
await self._delete_acknowledge_message()
|
||||
|
||||
async def _delete_acknowledge_message(self) -> None:
|
||||
await self._bot.delete_messages(
|
||||
|
@ -47,6 +53,22 @@ class SuccessDownloadHandler(AbstractDownloadHandler):
|
|||
message_ids=[self._body.context.ack_message_id],
|
||||
)
|
||||
|
||||
async def _set_upload_message(self) -> None:
|
||||
try:
|
||||
await self._bot.edit_message_text(
|
||||
chat_id=self._body.from_chat_id,
|
||||
message_id=self._body.context.ack_message_id,
|
||||
text=f'⬆️ {bold("Uploading")}',
|
||||
)
|
||||
except (MessageIdInvalid, MessageNotModified) as err:
|
||||
# Expected behaviour when several links where pasted in one message and
|
||||
# the acknowledgment message was deleted after the first successful upload
|
||||
self._log.warning(
|
||||
'Could not edit the message id "%s": %s',
|
||||
self._body.context.ack_message_id,
|
||||
err,
|
||||
)
|
||||
|
||||
async def _publish_error_message(self, err: Exception) -> None:
|
||||
err_payload = ErrorDownloadGeneralPayload(
|
||||
task_id=self._body.task_id,
|
||||
|
@ -68,7 +90,11 @@ class SuccessDownloadHandler(AbstractDownloadHandler):
|
|||
await self._send_success_text(media_object)
|
||||
if self._upload_is_enabled():
|
||||
self._validate_file_size_for_upload(media_object)
|
||||
await self._create_upload_task(media_object)
|
||||
coros = (
|
||||
self._create_upload_task(media_object),
|
||||
self._set_upload_message(),
|
||||
)
|
||||
await asyncio.gather(*coros)
|
||||
else:
|
||||
self._log.warning(
|
||||
'File "%s" will not be uploaded due to upload configuration',
|
||||
|
@ -149,10 +175,10 @@ class SuccessDownloadHandler(AbstractDownloadHandler):
|
|||
if not os.path.exists(media_obj.filepath):
|
||||
raise ValueError(f'{media_obj.file_type} {media_obj.filepath} not found')
|
||||
|
||||
file_size = os.stat(media_obj.filepath).st_size
|
||||
if file_size > max_file_size:
|
||||
_file_size = media_obj.current_file_size()
|
||||
if _file_size > max_file_size:
|
||||
err_msg = (
|
||||
f'{media_obj.file_type} file size of {file_size} bytes bigger than '
|
||||
f'{media_obj.file_type} file size of {_file_size} bytes bigger than '
|
||||
f'allowed {max_file_size} bytes. Will not upload'
|
||||
)
|
||||
self._log.warning(err_msg)
|
||||
|
|
|
@ -72,7 +72,7 @@ class YtdlpNewVersionNotifyTask(AbstractTask):
|
|||
f'Current version: {bold(ctx.current.version)}\n'
|
||||
f'Rebuild worker with {code("docker compose build --no-cache worker")}'
|
||||
)
|
||||
await self._bot.send_message_all(text)
|
||||
await self._bot.send_message_admins(text)
|
||||
|
||||
async def _notify_up_to_date(
|
||||
self, ctx: VersionContext, user_ids: list[int]
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = '1.4.3'
|
||||
__version__ = '1.4.4'
|
||||
|
|
|
@ -8,6 +8,7 @@ telegram:
|
|||
- "^http(s)?:\\/\\/.+$"
|
||||
allowed_users: # Multiple users/groups are allowed.
|
||||
- id: 11111111111 # User or group ID.
|
||||
is_admin: !! bool True
|
||||
send_startup_message: !!bool True
|
||||
download_media_type: "VIDEO"
|
||||
save_to_storage: !!bool False
|
||||
|
|
23
yt_shared/yt_shared/clients/github.py
Normal file
23
yt_shared/yt_shared/clients/github.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import datetime
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
from yt_shared.schemas.ytdlp import LatestVersion
|
||||
|
||||
|
||||
class YtDlpGithubClient:
|
||||
"""yt-dlp Github version number checker."""
|
||||
|
||||
LATEST_TAG_URL = 'https://github.com/yt-dlp/yt-dlp/releases/latest'
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
async def get_latest_version(self) -> LatestVersion:
|
||||
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: %s', version)
|
||||
return LatestVersion(
|
||||
version=version, retrieved_at=datetime.datetime.utcnow()
|
||||
)
|
|
@ -17,8 +17,8 @@ class RabbitMQ:
|
|||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
self._config = get_rabbit_config()
|
||||
|
||||
self.connection: RobustConnection = None
|
||||
self.channel: RobustChannel = None
|
||||
self.connection: RobustConnection | None = None
|
||||
self.channel: RobustChannel | None = None
|
||||
self.exchanges: dict[str, AbstractRobustExchange] = {}
|
||||
self.queues: dict[str, AbstractRobustQueue] = {}
|
||||
|
||||
|
@ -30,7 +30,7 @@ class RabbitMQ:
|
|||
|
||||
async def _set_connection(self) -> None:
|
||||
self.connection = await aio_pika.connect_robust(
|
||||
settings.RABBITMQ_URI,
|
||||
url=settings.RABBITMQ_URI,
|
||||
loop=get_running_loop(),
|
||||
reconnect_interval=self.RABBITMQ_RECONNECT_INTERVAL,
|
||||
)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from yt_shared.clients.github import YtDlpGithubClient
|
||||
from yt_shared.repositories.ytdlp import YtdlpRepository
|
||||
from yt_shared.schemas.ytdlp import CurrentVersion, LatestVersion, VersionContext
|
||||
|
||||
|
@ -16,6 +15,7 @@ class YtdlpVersionChecker:
|
|||
def __init__(self) -> None:
|
||||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
self._ytdlp_repository = YtdlpRepository()
|
||||
self._ytdlp_client = YtDlpGithubClient()
|
||||
|
||||
async def get_version_context(self, db: AsyncSession) -> VersionContext:
|
||||
latest, current = await asyncio.gather(
|
||||
|
@ -24,13 +24,7 @@ class YtdlpVersionChecker:
|
|||
return VersionContext(latest=latest, current=current)
|
||||
|
||||
async def get_latest_version(self) -> LatestVersion:
|
||||
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: %s', version)
|
||||
return LatestVersion(
|
||||
version=version, retrieved_at=datetime.datetime.utcnow()
|
||||
)
|
||||
return await self._ytdlp_client.get_latest_version()
|
||||
|
||||
async def get_current_version(self, db: AsyncSession) -> CurrentVersion:
|
||||
ytdlp_ = await self._ytdlp_repository.get_current_version(db)
|
||||
|
|
Loading…
Reference in a new issue