From fea447d1d3227ccd855f66bd684440432ed8b466 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 13:32:05 +0800 Subject: [PATCH 001/297] uncache --- README.md | 1 + ytdlbot/db.py | 2 +- ytdlbot/limit.py | 9 +++++++++ ytdlbot/ytdl_bot.py | 9 +++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ebe4bb4..1054cc19 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,7 @@ direct - Download file directly sub - Subscribe to YouTube Channel unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. +uncache - Delete cache for this link ``` # Test data diff --git a/ytdlbot/db.py b/ytdlbot/db.py index 26d60fea..a398d0ea 100644 --- a/ytdlbot/db.py +++ b/ytdlbot/db.py @@ -147,7 +147,7 @@ def get_send_cache(self, unique) -> "str": return self.r.hget("cache", unique) def del_send_cache(self, unique): - self.r.hdel("cache", unique) + return self.r.hdel("cache", unique) class MySQL: diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 76cd288e..d9dbf47e 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -207,6 +207,15 @@ def sub_count(self): text += "{} ==> [{}]({})\n".format(*item) return text + def del_cache(self, user_link: "str"): + unique = self.extract_canonical_link(user_link) + caches = self.r.hgetall("cache") + count = 0 + for key in caches: + if key.startswith(unique): + count += self.del_send_cache(key) + return count + class BuyMeACoffee: def __init__(self): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ef4e523c..76a7019e 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -143,6 +143,15 @@ def patch_handler(client: "Client", message: "types.Message"): hot_patch() +@app.on_message(filters.command(["uncache"])) +def patch_handler(client: "Client", message: "types.Message"): + username = message.from_user.username + link = message.text.split()[1] + if username == OWNER: + count = VIP().del_cache(link) + message.reply_text(f"{count} cache(s) deleted.", quote=True) + + @app.on_message(filters.command(["ping"])) def ping_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id From 6a15d6a68dff3db4da2e9815c7283a874dd54c05 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 14:26:53 +0800 Subject: [PATCH 002/297] upgrade yt-dlp --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eefc3a76..1d36c5fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.8 tgcrypto==1.2.3 -yt-dlp==2022.2.4 +yt-dlp==2022.3.8.2 APScheduler==3.9.1 beautifultable==1.0.1 ffmpeg-python==0.2.0 From 848ba3166839609b9514667d92ce8d99a1bcf670 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 15:04:41 +0800 Subject: [PATCH 003/297] delete bad users for subscription --- ytdlbot/db.py | 3 ++- ytdlbot/limit.py | 8 ++++++-- ytdlbot/ytdl_bot.py | 14 +++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ytdlbot/db.py b/ytdlbot/db.py index a398d0ea..7d6f7910 100644 --- a/ytdlbot/db.py +++ b/ytdlbot/db.py @@ -195,7 +195,8 @@ class MySQL: create table if not exists subscribe ( user_id bigint null, - channel_id varchar(256) null + channel_id varchar(256) null, + is_valid boolean default 1 null ) CHARSET=utf8mb4; """ diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index d9dbf47e..dbf00952 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -187,14 +187,18 @@ def get_user_subscription(self, user_id: "int"): def group_subscriber(self): # {"channel_id": [user_id, user_id, ...]} - self.cur.execute("select * from subscribe") + self.cur.execute("select * from subscribe where is_valid=1") data = self.cur.fetchall() group = {} for item in data: group.setdefault(item[1], []).append(item[0]) - logging.info("Checking peroidic subscriber...") + logging.info("Checking periodic subscriber...") return group + def deactivate_user_subscription(self, user_id: "int"): + self.cur.execute("UPDATE subscribe set is_valid=0 WHERE user_id=%s", (user_id,)) + self.con.commit() + def sub_count(self): sql = """ select user_id, channel.title, channel.link diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 76a7019e..e8d4802f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -334,14 +334,22 @@ def owner_local_callback(client: "Client", callback_query: types.CallbackQuery): def periodic_sub_check(): vip = VIP() + exceptions = pyrogram.errors.exceptions for cid, uids in vip.group_subscriber().items(): video_url = vip.has_newer_update(cid) if video_url: logging.info(f"periodic update:{video_url} - {uids}") for uid in uids: - bot_msg = app.send_message(uid, f"{video_url} is downloading...", disable_web_page_preview=True) - ytdl_download_entrance(bot_msg, app, video_url) - time.sleep(random.random()) + try: + bot_msg = app.send_message(uid, f"{video_url} is downloading...", disable_web_page_preview=True) + ytdl_download_entrance(bot_msg, app, video_url) + except(exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: + logging.warning("User is blocked or deleted. %s", e) + vip.deactivate_user_subscription(uid) + except Exception as e: + logging.error("Unknown error when sending message to user. %s", e) + finally: + time.sleep(random.random() * 3) if __name__ == '__main__': From 296efdbd1ba3b90fd34c94c118cc78fa9974fd03 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 15:16:30 +0800 Subject: [PATCH 004/297] update supervisor conf --- conf/supervisor_main.conf | 2 ++ conf/supervisor_worker.conf | 2 ++ 2 files changed, 4 insertions(+) diff --git a/conf/supervisor_main.conf b/conf/supervisor_main.conf index 5a6c9929..cd2130cd 100644 --- a/conf/supervisor_main.conf +++ b/conf/supervisor_main.conf @@ -16,6 +16,8 @@ command=python ytdl_bot.py autorestart=true priority=900 stopasgroup=true +startsecs = 5 +startretries = 5 redirect_stderr=true stdout_logfile_maxbytes = 50MB diff --git a/conf/supervisor_worker.conf b/conf/supervisor_worker.conf index 6c4dccfe..545bde71 100644 --- a/conf/supervisor_worker.conf +++ b/conf/supervisor_worker.conf @@ -12,6 +12,8 @@ command=python tasks.py autorestart=true priority=900 stopasgroup=true +startsecs = 5 +startretries = 5 redirect_stderr=true stdout_logfile_maxbytes = 50MB From 49095af26807727d1f1d0aba265f5dc999f0d4f6 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 15:21:18 +0800 Subject: [PATCH 005/297] uppercase IPv6 --- ytdlbot/client_init.py | 2 +- ytdlbot/downloader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index cb1718e1..f1635e68 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -17,7 +17,7 @@ def create_app(session="ytdl", workers=PYRO_WORKERS): _app = Client(session, APP_ID, APP_HASH, bot_token=TOKEN, workers=workers, - ipv6=os.getenv("ipv6", False), + ipv6=os.getenv("IPv6", False), # proxy={"hostname": "host.docker.internal", "port": 1080} ) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index fc7801d8..4ae9405e 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -202,7 +202,7 @@ def ytdl_download(url, tempdir, bm) -> dict: adjust_formats(chat_id, url, formats) add_instagram_cookies(url, ydl_opts) - address = ["::", "0.0.0.0"] if os.getenv("ipv6") else [None] + address = ["::", "0.0.0.0"] if os.getenv("IPv6") else [None] for format_ in formats: ydl_opts["format"] = format_ From ff5259367bfa7098dccf02d030518fc0ed0f211c Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 15:47:34 +0800 Subject: [PATCH 006/297] limit ffmpeg --- ytdlbot/config.py | 2 +- ytdlbot/downloader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 9f4860c1..2bc0f4d2 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -31,7 +31,7 @@ USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 ENABLE_VIP = os.getenv("VIP", False) -MAX_DURATION = int(os.getenv("MAX_DURATION", 300)) +MAX_DURATION = int(os.getenv("MAX_DURATION", 0)) AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") COFFEE_TOKEN = os.getenv("COFFEE_TOKEN") diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 4ae9405e..dff3a05e 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -138,7 +138,7 @@ def convert_to_mp4(resp: dict, bot_msg): logging.warning("Conversion abort for non VIP %s", bot_msg.chat.id) bot_msg._client.send_message( bot_msg.chat.id, - "You're not VIP, so you can't convert longer video to streaming formats.") + f"You're not VIP, so you can't convert video {MAX_DURATION}s to streaming formats.") break edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") new_file_path = path.with_suffix(".mp4") From 77a4b3467473d4de61aa9273ac4923dbbc7b43d8 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 22:05:42 +0800 Subject: [PATCH 007/297] disable ffmpeg by default --- ytdlbot/client_init.py | 4 ++-- ytdlbot/config.py | 5 ++++- ytdlbot/downloader.py | 13 +++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index f1635e68..5f9f9019 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -11,13 +11,13 @@ from pyrogram import Client -from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN +from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 def create_app(session="ytdl", workers=PYRO_WORKERS): _app = Client(session, APP_ID, APP_HASH, bot_token=TOKEN, workers=workers, - ipv6=os.getenv("IPv6", False), + ipv6=IPv6, # proxy={"hostname": "host.docker.internal", "port": 1080} ) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 2bc0f4d2..5db51983 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -31,7 +31,7 @@ USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 ENABLE_VIP = os.getenv("VIP", False) -MAX_DURATION = int(os.getenv("MAX_DURATION", 0)) +MAX_DURATION = int(os.getenv("MAX_DURATION", 60)) AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") COFFEE_TOKEN = os.getenv("COFFEE_TOKEN") @@ -53,3 +53,6 @@ AUDIO_FORMAT = os.getenv("AUDIO_FORMAT", "m4a") ARCHIVE_ID = os.getenv("ARCHIVE_ID") + +IPv6 = os.getenv("IPv6", False) +ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index dff3a05e..9e7871db 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -25,7 +25,8 @@ from tqdm import tqdm from yt_dlp import DownloadError -from config import AUDIO_FORMAT, ENABLE_VIP, MAX_DURATION, TG_MAX_SIZE +from config import (AUDIO_FORMAT, ENABLE_FFMPEG, ENABLE_VIP, MAX_DURATION, + TG_MAX_SIZE, IPv6) from db import Redis from limit import VIP from utils import (adjust_formats, apply_log_formatter, current_time, @@ -135,10 +136,8 @@ def convert_to_mp4(resp: dict, bot_msg): mime = getattr(filetype.guess(path), "mime", "video/mp4") if mime in default_type: if not can_convert_mp4(path, bot_msg.chat.id): - logging.warning("Conversion abort for non VIP %s", bot_msg.chat.id) - bot_msg._client.send_message( - bot_msg.chat.id, - f"You're not VIP, so you can't convert video {MAX_DURATION}s to streaming formats.") + logging.warning("Conversion abort for %s", bot_msg.chat.id) + bot_msg._client.send_message(bot_msg.chat.id, "Can't convert your video to streaming format.") break edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") new_file_path = path.with_suffix(".mp4") @@ -170,6 +169,8 @@ def run_ffmpeg(cmd_list, bm): def can_convert_mp4(video_path, uid): + if not ENABLE_FFMPEG: + return False if not ENABLE_VIP: return True video_streams = ffmpeg.probe(video_path, select_streams="v") @@ -202,7 +203,7 @@ def ytdl_download(url, tempdir, bm) -> dict: adjust_formats(chat_id, url, formats) add_instagram_cookies(url, ydl_opts) - address = ["::", "0.0.0.0"] if os.getenv("IPv6") else [None] + address = ["::", "0.0.0.0"] if IPv6 else [None] for format_ in formats: ydl_opts["format"] = format_ From 87d6b0ee51592c88fb54b5450c7d4b6c7c90a9b7 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 12 Mar 2022 23:18:12 +0800 Subject: [PATCH 008/297] =?UTF-8?q?=E6=88=91=E8=A2=AB=E7=A5=AD=E5=A4=A9?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ytdlbot/downloader.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 9e7871db..aeac189f 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -23,7 +23,6 @@ import filetype import yt_dlp as ytdl from tqdm import tqdm -from yt_dlp import DownloadError from config import (AUDIO_FORMAT, ENABLE_FFMPEG, ENABLE_VIP, MAX_DURATION, TG_MAX_SIZE, IPv6) @@ -193,7 +192,8 @@ def ytdl_download(url, tempdir, bm) -> dict: 'progress_hooks': [lambda d: download_hook(d, bm)], 'outtmpl': output, 'restrictfilenames': False, - 'quiet': True + 'quiet': True, + # "proxy": "socks5://127.0.0.1:1080" } formats = [ "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", @@ -204,7 +204,6 @@ def ytdl_download(url, tempdir, bm) -> dict: add_instagram_cookies(url, ydl_opts) address = ["::", "0.0.0.0"] if IPv6 else [None] - for format_ in formats: ydl_opts["format"] = format_ for addr in address: @@ -217,12 +216,13 @@ def ytdl_download(url, tempdir, bm) -> dict: response["status"] = True response["error"] = "" break - except (ValueError, DownloadError) as e: + except Exception as e: logging.error("Download failed for %s ", url) response["status"] = False response["error"] = str(e) - except Exception as e: - logging.error("UNKNOWN EXCEPTION: %s", e) + + if response["status"]: + break logging.info("%s - %s", url, response) if response["status"] is False: From 97dbacad04e8e4a01efffe57276bcab2ffad6200 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 13 Mar 2022 13:12:18 +0800 Subject: [PATCH 009/297] better direct audio --- ytdlbot/config.py | 2 +- ytdlbot/downloader.py | 27 ++++++++++++++++++++++++--- ytdlbot/tasks.py | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 5db51983..85c737f2 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -51,7 +51,7 @@ MYSQL_USER = os.getenv("MYSQL_USER", "root") MYSQL_PASS = os.getenv("MYSQL_PASS", "root") -AUDIO_FORMAT = os.getenv("AUDIO_FORMAT", "m4a") +AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") ARCHIVE_ID = os.getenv("ARCHIVE_ID") IPv6 = os.getenv("IPv6", False) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index aeac189f..e901ce4c 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -257,11 +257,32 @@ def ytdl_download(url, tempdir, bm) -> dict: def convert_audio_format(resp: "dict", bm): + # 1. file is audio, default format + # 2. file is video, default format + # 3. non default format if resp["status"]: - # all_converted = [] - path: pathlib.PosixPath + path: "pathlib.Path" for path in resp["filepath"]: - if path.suffix != f".{AUDIO_FORMAT}": + streams = ffmpeg.probe(path)["streams"] + if (AUDIO_FORMAT is None and + len(streams) == 1 and + streams[0]["codec_type"] == "audio"): + logging.info("%s is audio, default format, no need to convert", path) + elif AUDIO_FORMAT is None and len(streams) >= 2: + logging.info("%s is video, default format, need to extract audio", path) + audio_stream = {"codec_name": "m4a"} + for stream in streams: + if stream["codec_type"] == "audio": + audio_stream = stream + break + ext = audio_stream["codec_name"] + new_path = path.with_suffix(f".{ext}") + run_ffmpeg(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) + path.unlink() + index = resp["filepath"].index(path) + resp["filepath"][index] = new_path + else: + logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) new_path = path.with_suffix(f".{AUDIO_FORMAT}") run_ffmpeg(["ffmpeg", "-y", "-i", path, new_path], bm) path.unlink() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 58ded731..83730609 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -362,7 +362,7 @@ def gen_video_markup(): [ [ # First row InlineKeyboardButton( # Generates a callback query when pressed - f"convert to audio({AUDIO_FORMAT})", + "convert to audio", callback_data="convert" ) ] From 2df3ee5c6dfd69eb2376321d28a660437d3ad820 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 13 Mar 2022 13:58:10 +0800 Subject: [PATCH 010/297] direct audio conversion --- ytdlbot/downloader.py | 9 +++++---- ytdlbot/limit.py | 5 +++-- ytdlbot/tasks.py | 23 ++++++----------------- ytdlbot/utils.py | 6 +++++- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index e901ce4c..e2e73c0c 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -184,8 +184,9 @@ def can_convert_mp4(video_path, uid): return True -def ytdl_download(url, tempdir, bm) -> dict: +def ytdl_download(url, tempdir, bm, **kwargs) -> dict: chat_id = bm.chat.id + hijack = kwargs.get("hijack") response = {"status": True, "error": "", "filepath": []} output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { @@ -193,14 +194,14 @@ def ytdl_download(url, tempdir, bm) -> dict: 'outtmpl': output, 'restrictfilenames': False, 'quiet': True, - # "proxy": "socks5://127.0.0.1:1080" + "proxy": os.getenv("YTDL_PROXY") } formats = [ "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", None ] - adjust_formats(chat_id, url, formats) + adjust_formats(chat_id, url, formats, hijack) add_instagram_cookies(url, ydl_opts) address = ["::", "0.0.0.0"] if IPv6 else [None] @@ -249,7 +250,7 @@ def ytdl_download(url, tempdir, bm) -> dict: if settings[2] == "video" or isinstance(settings[2], MagicMock): # only convert if send type is video convert_to_mp4(response, bm) - if settings[2] == "audio": + if settings[2] == "audio" or hijack == "bestaudio[ext=m4a]": convert_audio_format(response, bm) # disable it for now # split_large_video(response) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index dbf00952..87514d19 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -107,17 +107,18 @@ def unsubscribe_channel(self, user_id: "int", channel_id: "str"): @staticmethod def extract_canonical_link(url): # canonic link works for many websites. It will strip out unnecessary stuff + proxy = {"https": os.getenv("YTDL_PROXY")} if os.getenv("YTDL_PROXY") else None props = ["canonical", "alternate", "shortlinkUrl"] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"} # send head request first - r = requests.head(url, headers=headers) + r = requests.head(url, headers=headers, proxies=proxy) if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type"): # get content-type, if it's not text/html, there's no need to issue a GET request logging.warning("%s Content-type is not text/html, no need to GET for extract_canonical_link", url) return url - html_doc = requests.get(url, headers=headers, timeout=5).text + html_doc = requests.get(url, headers=headers, timeout=5, proxies=proxy).text soup = BeautifulSoup(html_doc, "html.parser") for prop in props: element = soup.find("link", rel=prop) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 83730609..7d540647 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -211,28 +211,17 @@ def direct_normal_download(bot_msg, client, url): def normal_audio(bot_msg, client): chat_id = bot_msg.chat.id - fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) + # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) status_msg = bot_msg.reply_text("Converting to audio...please wait patiently", quote=True) + orig_url: "str" = re.findall(r"http[s]://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: - logging.info("downloading to %s", tmp) - base_path = pathlib.Path(tmp) - video_path = base_path.joinpath(fn) - audio = base_path.joinpath(fn).with_suffix(f".{AUDIO_FORMAT}") - client.send_chat_action(chat_id, 'record_video_note') - status_msg.edit_text("Preparing your conversion....") - client.download_media(bot_msg, video_path) - logging.info("downloading complete %s", video_path) - # execute ffmpeg client.send_chat_action(chat_id, 'record_audio') - try: - run_ffmpeg(["ffmpeg", "-y", "-i", video_path, "-vn", "-acodec", "copy", audio], status_msg) - except subprocess.CalledProcessError: - # CPU consuming if re-encoding. - run_ffmpeg(["ffmpeg", "-y", "-i", video_path, audio], status_msg) - + # just try to download the audio using yt-dlp + resp = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") client.send_chat_action(chat_id, 'upload_audio') - client.send_audio(chat_id, audio) + for f in resp["filepath"]: + client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") Redis().update_metrics("audio_success") diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 90c20546..7a0677ca 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -76,10 +76,14 @@ def is_youtube(url: "str"): return True -def adjust_formats(user_id: "str", url: "str", formats: "list"): +def adjust_formats(user_id: "str", url: "str", formats: "list", hijack=None): # high: best quality, 720P, 1080P, 2K, 4K, 8K # medium: 480P # low: 360P+240P + if hijack: + formats.insert(0, hijack) + return + mapping = {"high": [], "medium": [480], "low": [240, 360]} settings = get_user_settings(user_id) if settings and is_youtube(url): From 9c199ab81ac14194b08cf1b961693bb9c1b0c294 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Thu, 17 Mar 2022 19:56:38 +0800 Subject: [PATCH 011/297] fix sub --- ytdlbot/tasks.py | 6 +++--- ytdlbot/ytdl_bot.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 7d540647..7ecc58e2 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -317,14 +317,14 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.P def gen_cap(bm, url, video_path): chat_id = bm.chat.id user = bm.chat - if user is None: - user_info = "" - else: + try: user_info = "@{}({})-{}".format( user.username or "N/A", user.first_name or "" + user.last_name or "", user.id ) + except Exception: + user_info = "" if isinstance(video_path, pathlib.Path): meta = get_metadata(video_path) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index e8d4802f..cacc02e2 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -347,7 +347,7 @@ def periodic_sub_check(): logging.warning("User is blocked or deleted. %s", e) vip.deactivate_user_subscription(uid) except Exception as e: - logging.error("Unknown error when sending message to user. %s", e) + logging.error("Unknown error when sending message to user. %s", traceback.format_exc()) finally: time.sleep(random.random() * 3) From a379c11542d12c5ca6b6a5e2dc661e1a4b805b11 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 25 Mar 2022 23:19:21 +0800 Subject: [PATCH 012/297] temp disable sub --- README.md | 2 ++ ytdlbot/ytdl_bot.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1054cc19..c10498f9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ YouTube Download Bot๐Ÿš€ Download videos from YouTube and other platforms through a Telegram Bot +**subscription was disaabled for now!** + ----- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index cacc02e2..60821862 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -358,8 +358,8 @@ def periodic_sub_check(): scheduler.add_job(Redis().reset_today, 'cron', hour=0, minute=0) scheduler.add_job(auto_restart, 'interval', seconds=5) scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) - # default quota allocation of 10,000 units per day, - scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) + # default quota allocation of 10,000 units per day, TODO disabled it for now! + # scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From 16fd2d17edad3d2b05e2b33deaf727612d595b30 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sat, 2 Apr 2022 20:32:23 +0800 Subject: [PATCH 013/297] re-enable sub --- README.md | 2 -- ytdlbot/ytdl_bot.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c10498f9..1054cc19 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ YouTube Download Bot๐Ÿš€ Download videos from YouTube and other platforms through a Telegram Bot -**subscription was disaabled for now!** - ----- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 60821862..cacc02e2 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -358,8 +358,8 @@ def periodic_sub_check(): scheduler.add_job(Redis().reset_today, 'cron', hour=0, minute=0) scheduler.add_job(auto_restart, 'interval', seconds=5) scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) - # default quota allocation of 10,000 units per day, TODO disabled it for now! - # scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) + # default quota allocation of 10,000 units per day, + scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From c7436b8bd1896dc4197d0e998625bafa732d969b Mon Sep 17 00:00:00 2001 From: BennyThink Date: Mon, 11 Apr 2022 20:10:21 +0800 Subject: [PATCH 014/297] fix #92 --- ytdlbot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 85c737f2..fe82fa91 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -40,7 +40,7 @@ OWNER = os.getenv("OWNER", "BennyThink") # limitation settings -AUTHORIZED_USER: "str" = os.getenv("AUTHORIZED", "") +AUTHORIZED_USER: "str" = os.getenv("AUTHORIZED_USER", "") # membership requires: the format could be username/chat_id of channel or group REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "") From c15348b14a5b8ce3c9278fac86e97c38ec311aea Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 15 Apr 2022 20:12:22 +0800 Subject: [PATCH 015/297] exception Handle in ping --- requirements.txt | 10 +++++----- ytdlbot/db.py | 5 ++++- ytdlbot/utils.py | 3 ++- ytdlbot/ytdl_bot.py | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1d36c5fa..28aadb76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,21 @@ -pyrogram==1.4.8 +pyrogram==1.4.15 tgcrypto==1.2.3 -yt-dlp==2022.3.8.2 +yt-dlp==2022.4.8 APScheduler==3.9.1 beautifultable==1.0.1 ffmpeg-python==0.2.0 PyMySQL==1.0.2 -celery==5.2.3 +celery==5.2.6 filetype==1.0.10 flower==1.0.0 psutil==5.9.0 influxdb==5.3.1 -beautifulsoup4==4.10.0 +beautifulsoup4==4.11.1 fakeredis==1.7.1 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.1.4 requests==2.27.1 -tqdm==4.63.0 +tqdm==4.64.0 requests-toolbelt==0.9.1 ffpb==0.4.1 \ No newline at end of file diff --git a/ytdlbot/db.py b/ytdlbot/db.py index 7d6f7910..15e2a018 100644 --- a/ytdlbot/db.py +++ b/ytdlbot/db.py @@ -235,7 +235,10 @@ def get_worker_data(): username = os.getenv("FLOWER_USERNAME", "benny") token = base64.b64encode(f"{username}:{password}".encode()).decode() headers = {"Authorization": f"Basic {token}"} - return requests.get("https://celery.dmesg.app/dashboard?json=1", headers=headers).json() + r = requests.get("https://celery.dmesg.app/dashboard?json=1", headers=headers) + if r.status_code != 200: + return dict(data=[]) + return r.json() def extract_dashboard_data(self): self.data = self.get_worker_data() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 7a0677ca..642d67f5 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -187,7 +187,8 @@ def updates_too_long_detector(self): "Code is updated", 'Retrying "messages.GetMessages"', "OSError: Connection lost", - "[Errno -3] Try again" + "[Errno -3] Try again", + "MISCONF", ] for indicator in indicators: if indicator in self.logs: diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index cacc02e2..a2cc969f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -161,7 +161,7 @@ def ping_handler(client: "Client", message: "types.Message"): else: bot_info = get_runtime("ytdlbot_ytdl_1", "YouTube-dl") if message.chat.username == OWNER: - stats = bot_text.ping_worker() + stats = bot_text.ping_worker()[:1000] client.send_document(chat_id, Redis().generate_file(), caption=f"{bot_info}\n\n{stats}") else: client.send_message(chat_id, f"{bot_info}") From c33e4eece18014af3551eb2dd48020acd1ba5d2b Mon Sep 17 00:00:00 2001 From: BennyThink Date: Tue, 19 Apr 2022 03:28:20 +0800 Subject: [PATCH 016/297] add search step 1 --- README.md | 10 +++++++--- requirements.txt | 13 +++++++------ ytdlbot/ytdl_bot.py | 24 ++++++++++++++++++++---- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1054cc19..2bda3417 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ You can choose to become 'VIP' if you really need large traffic. And also, you c 8. support sending as file or streaming as video 9. supports celery worker distribution - faster than before. 10. subscriptions to YouTube Channels -11. cache mechanism - download once for the same video. +11. cache mechanism - download once for the same video. ![](assets/2.jpeg) @@ -128,8 +128,8 @@ you can configure all the following environment variables: * GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. * AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. -* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. -**Inline button will be lost during the forwarding.** +* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. + **Inline button will be lost during the forwarding.** ## 3.2 Set up init data @@ -255,6 +255,10 @@ https://www.youtube.com/playlist?list=PL1Hdq7xjQCJxQnGc05gS4wzHWccvEJy0w https://dmesg.app/m3u8/prog_index.m3u8 +## test twitter +https://twitter.com/nitori_sayaka/status/1526199729864200192 +https://twitter.com/BennyThinks/status/1475836588542341124 + # Donation * [Buy me a coffee](https://www.buymeacoffee.com/bennythink) diff --git a/requirements.txt b/requirements.txt index 28aadb76..18efce93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,22 @@ -pyrogram==1.4.15 +pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.4.8 +yt-dlp==2022.5.18 APScheduler==3.9.1 -beautifultable==1.0.1 +beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 celery==5.2.6 -filetype==1.0.10 +filetype==1.0.13 flower==1.0.0 psutil==5.9.0 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.7.1 +fakeredis==1.7.5 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.1.4 requests==2.27.1 tqdm==4.64.0 requests-toolbelt==0.9.1 -ffpb==0.4.1 \ No newline at end of file +ffpb==0.4.1 +youtube-search-python==1.6.5 \ No newline at end of file diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index a2cc969f..5797687f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -15,7 +15,7 @@ import traceback import typing from io import BytesIO - +from youtubesearchpython import VideosSearch import pyrogram.errors from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types @@ -268,8 +268,24 @@ def download_handler(client: "Client", message: "types.Message"): logging.info("start %s", url) if not re.findall(r"^https?://", url.lower()): - red.update_metrics("bad_request") - message.reply_text("I think you should send me a link.", quote=True) + red.update_metrics("search_request") + # TODO + result = VideosSearch(url, limit=5).result().get("result", []) + text = "" + count = 1 + buttons = [] + for item in result: + text += f"{count}. {item['title']} - {item['link']}\n\n" + buttons.append( + InlineKeyboardButton( + f"{count}", + callback_data=f"search_{item['id']}" + ) + ) + count += 1 + + markup = InlineKeyboardMarkup([buttons]) + client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup) return if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)): @@ -359,7 +375,7 @@ def periodic_sub_check(): scheduler.add_job(auto_restart, 'interval', seconds=5) scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) # default quota allocation of 10,000 units per day, - scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) + scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 60) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From 80dd3bc224cdec62786c47ce873db1561f5d5028 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 27 May 2022 20:01:54 +0800 Subject: [PATCH 017/297] bump fix #121 --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 18efce93..a27a9741 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,18 +5,18 @@ APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 -celery==5.2.6 +celery==5.2.7 filetype==1.0.13 flower==1.0.0 -psutil==5.9.0 +psutil==5.9.1 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.7.5 +fakeredis==1.8.1 supervisor==4.2.4 tgbot-ping==1.0.4 -redis==4.1.4 -requests==2.27.1 +redis==4.3.3 +requests==2.28.0 tqdm==4.64.0 requests-toolbelt==0.9.1 ffpb==0.4.1 -youtube-search-python==1.6.5 \ No newline at end of file +youtube-search-python==1.6.5 From 86e5a0272999c1cd75e01f80c4776a052f4b46f5 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 24 Jun 2022 13:41:15 +0800 Subject: [PATCH 018/297] fix subscribe --- ytdlbot/limit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 87514d19..5d01a7b2 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -92,7 +92,7 @@ def subscribe_channel(self, user_id: "int", share_link: "str"): self.cur.execute("INSERT IGNORE INTO channel values" "(%(link)s,%(title)s,%(description)s,%(channel_id)s,%(playlist)s,%(last_video)s)", data) - self.cur.execute("INSERT INTO subscribe values(%s,%s)", (user_id, channel_id)) + self.cur.execute("INSERT INTO subscribe values(%s,%s, NULL)", (user_id, channel_id)) self.con.commit() logging.info("User %s subscribed channel %s", user_id, data["title"]) return "Subscribed to {}".format(data["title"]) From a0c48d505ea0f309c717bc0e62d2728cd1803770 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 24 Jun 2022 19:26:07 +0800 Subject: [PATCH 019/297] set bot command #124 --- README.md | 2 +- requirements.txt | 4 ++-- ytdlbot/constant.py | 6 ++++++ ytdlbot/ytdl_bot.py | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2bda3417..1e1cd376 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ docker-compose -f worker.yml up -d # Command -``` +```cmd start - Let's start about - What's this bot? ping - Bot running status diff --git a/requirements.txt b/requirements.txt index a27a9741..3cad5220 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.5.18 +yt-dlp==2022.6.22.1 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -19,4 +19,4 @@ requests==2.28.0 tqdm==4.64.0 requests-toolbelt==0.9.1 ffpb==0.4.1 -youtube-search-python==1.6.5 +youtube-search-python==1.6.6 diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index bc8ceecc..9ad45d25 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -8,6 +8,8 @@ __author__ = "Benny " import os +import pathlib +import re import time from config import (AFD_LINK, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, EX, @@ -149,3 +151,7 @@ def ping_worker(): text += f"{status}{hostname} **{active}** {load} {rev}\n" return text + + +with open(pathlib.Path(__file__).parent.parent.joinpath("README.md"), encoding="u8") as f: + COMMANDS = re.findall(r"```cmd([\s|\S]*)```", f.read())[0].strip() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 5797687f..f241548c 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -15,18 +15,19 @@ import traceback import typing from io import BytesIO -from youtubesearchpython import VideosSearch + import pyrogram.errors from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime +from youtubesearchpython import VideosSearch from client_init import create_app from config import (AUTHORIZED_USER, ENABLE_CELERY, ENABLE_VIP, OWNER, REQUIRED_MEMBERSHIP) -from constant import BotText +from constant import COMMANDS, BotText from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment from tasks import app as celery_app @@ -387,4 +388,13 @@ def periodic_sub_check(): Version: {get_revision()} """ print(banner) + + with app: + commands = [i for i in COMMANDS.split("\n") if i] + command_list = [] + for cmd in commands: + split = cmd.split("-") + command_list.append(pyrogram.types.BotCommand(split[0].strip(), split[1].strip())) + app.set_bot_commands(command_list) + app.run() From 01a6a8653975f666218ab1bc0d61546b45395cdb Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 26 Jun 2022 12:27:08 +0800 Subject: [PATCH 020/297] Revert "set bot command" This reverts commit a0c48d505ea0f309c717bc0e62d2728cd1803770. --- README.md | 2 +- requirements.txt | 2 +- ytdlbot/constant.py | 6 ------ ytdlbot/ytdl_bot.py | 14 ++------------ 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1e1cd376..2bda3417 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ docker-compose -f worker.yml up -d # Command -```cmd +``` start - Let's start about - What's this bot? ping - Bot running status diff --git a/requirements.txt b/requirements.txt index 3cad5220..c750248d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,4 +19,4 @@ requests==2.28.0 tqdm==4.64.0 requests-toolbelt==0.9.1 ffpb==0.4.1 -youtube-search-python==1.6.6 +youtube-search-python==1.6.6 \ No newline at end of file diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 9ad45d25..bc8ceecc 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -8,8 +8,6 @@ __author__ = "Benny " import os -import pathlib -import re import time from config import (AFD_LINK, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, EX, @@ -151,7 +149,3 @@ def ping_worker(): text += f"{status}{hostname} **{active}** {load} {rev}\n" return text - - -with open(pathlib.Path(__file__).parent.parent.joinpath("README.md"), encoding="u8") as f: - COMMANDS = re.findall(r"```cmd([\s|\S]*)```", f.read())[0].strip() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index f241548c..5797687f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -15,19 +15,18 @@ import traceback import typing from io import BytesIO - +from youtubesearchpython import VideosSearch import pyrogram.errors from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime -from youtubesearchpython import VideosSearch from client_init import create_app from config import (AUTHORIZED_USER, ENABLE_CELERY, ENABLE_VIP, OWNER, REQUIRED_MEMBERSHIP) -from constant import COMMANDS, BotText +from constant import BotText from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment from tasks import app as celery_app @@ -388,13 +387,4 @@ def periodic_sub_check(): Version: {get_revision()} """ print(banner) - - with app: - commands = [i for i in COMMANDS.split("\n") if i] - command_list = [] - for cmd in commands: - split = cmd.split("-") - command_list.append(pyrogram.types.BotCommand(split[0].strip(), split[1].strip())) - app.set_bot_commands(command_list) - app.run() From 903dd58e1b11080f160896d31e3f4cc2fb6b17e0 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 26 Jun 2022 19:16:09 +0800 Subject: [PATCH 021/297] fix re to support http audio extraction #126 --- ytdlbot/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 7ecc58e2..3395e580 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -213,7 +213,7 @@ def normal_audio(bot_msg, client): chat_id = bot_msg.chat.id # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) status_msg = bot_msg.reply_text("Converting to audio...please wait patiently", quote=True) - orig_url: "str" = re.findall(r"http[s]://.*", bot_msg.caption)[0] + orig_url: "str" = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: client.send_chat_action(chat_id, 'record_audio') # just try to download the audio using yt-dlp From 11a71756724178c3adcd9e8607efd8910dd83694 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 10 Jul 2022 21:25:24 +0800 Subject: [PATCH 022/297] add queue support --- requirements.txt | 8 +++---- ytdlbot/config.py | 2 ++ ytdlbot/tasks.py | 57 +++++++++++++++++++++++++++++++++++++-------- ytdlbot/ytdl_bot.py | 3 ++- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/requirements.txt b/requirements.txt index c750248d..c46a1452 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,13 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.6.22.1 +yt-dlp==2022.6.29 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 celery==5.2.7 filetype==1.0.13 -flower==1.0.0 +flower==1.1.0 psutil==5.9.1 influxdb==5.3.1 beautifulsoup4==4.11.1 @@ -15,8 +15,8 @@ fakeredis==1.8.1 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 -requests==2.28.0 +requests==2.28.1 tqdm==4.64.0 requests-toolbelt==0.9.1 ffpb==0.4.1 -youtube-search-python==1.6.6 \ No newline at end of file +youtube-search-python==1.6.6 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index fe82fa91..e8d84adf 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -46,7 +46,9 @@ # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) +ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", True) BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4") + MYSQL_HOST = os.getenv("MYSQL_HOST") MYSQL_USER = os.getenv("MYSQL_USER", "root") MYSQL_PASS = os.getenv("MYSQL_PASS", "root") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 3395e580..b5f1e555 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -10,6 +10,7 @@ import logging import os import pathlib +import random import re import subprocess import tempfile @@ -31,7 +32,7 @@ from client_init import create_app from config import (ARCHIVE_ID, AUDIO_FORMAT, BROKER, ENABLE_CELERY, - ENABLE_VIP, TG_MAX_SIZE, WORKERS) + ENABLE_QUEUE, ENABLE_VIP, TG_MAX_SIZE, WORKERS) from constant import BotText from db import Redis from downloader import (edit_text, run_ffmpeg, sizeof_fmt, tqdm_progress, @@ -135,7 +136,8 @@ def ytdl_download_entrance(bot_msg, client, url): return mode = get_user_settings(str(chat_id))[-1] if ENABLE_CELERY and mode in [None, "Celery"]: - ytdl_download_task.delay(chat_id, bot_msg.message_id, url) + async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) + # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) else: ytdl_normal_download(bot_msg, client, url) @@ -151,7 +153,8 @@ def direct_download_entrance(bot_msg, client, url): def audio_entrance(bot_msg, client): if ENABLE_CELERY: - audio_task.delay(bot_msg.chat.id, bot_msg.message_id) + async_task(audio_task, bot_msg.chat.id, bot_msg.message_id) + # audio_task.delay(bot_msg.chat.id, bot_msg.message_id) else: normal_audio(bot_msg, client) @@ -284,12 +287,24 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.P chat_id = ARCHIVE_ID if settings[2] == "document": logging.info("Sending as document") - res_msg = client.send_document(chat_id, vp_or_fid, - caption=cap, - progress=upload_hook, progress_args=(bot_msg,), - reply_markup=markup, - thumb=meta["thumb"] - ) + try: + # send as document could be sent as video even if it's a document + res_msg = client.send_document(chat_id, vp_or_fid, + caption=cap, + progress=upload_hook, progress_args=(bot_msg,), + reply_markup=markup, + thumb=meta["thumb"], + force_document=True + ) + except ValueError: + logging.error("Retry to send as video") + res_msg = client.send_video(chat_id, vp_or_fid, + supports_streaming=True, + caption=cap, + progress=upload_hook, progress_args=(bot_msg,), + reply_markup=markup, + **meta + ) elif settings[2] == "audio": logging.info("Sending as audio") res_msg = client.send_audio(chat_id, vp_or_fid, @@ -305,6 +320,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.P reply_markup=markup, **meta ) + unique = get_unique_clink(url, bot_msg.chat.id) obj = res_msg.document or res_msg.video or res_msg.audio red.add_send_cache(unique, getattr(obj, "file_id", None)) @@ -387,12 +403,33 @@ def hot_patch(*args): psutil.Process().kill() +def async_task(task_name, *args): + if not ENABLE_QUEUE: + task_name.delay(*args) + return + + t0 = time.time() + inspect = app.control.inspect() + worker_stats = inspect.stats() + route_queues = [] + for worker_name, stats in worker_stats.items(): + route = worker_name.split('@')[1] + concurrency = stats['pool']['max-concurrency'] + route_queues.extend([route] * concurrency) + destination = random.choice(route_queues) + logging.info("Selecting worker %s from %s in %.2fs", destination, route_queues, time.time() - t0) + task_name.apply_async(args=args, queue=destination) + + def run_celery(): + worker_name = os.getenv("WORKER_NAME", "") argv = [ "-A", "tasks", 'worker', '--loglevel=info', "--pool=threads", f"--concurrency={WORKERS}", - "-n", os.getenv("WORKER_NAME", "") + "-n", worker_name ] + if ENABLE_QUEUE: + argv.extend(["-Q", worker_name]) app.worker_main(argv) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 5797687f..9620e676 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -15,13 +15,14 @@ import traceback import typing from io import BytesIO -from youtubesearchpython import VideosSearch + import pyrogram.errors from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime +from youtubesearchpython import VideosSearch from client_init import create_app from config import (AUTHORIZED_USER, ENABLE_CELERY, ENABLE_VIP, OWNER, From 8391cbd21bd1a8b7a61e28da516fbc830dcf68b0 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 10 Jul 2022 22:21:54 +0800 Subject: [PATCH 023/297] enhance queue algorithm --- ytdlbot/tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index b5f1e555..aebeaf86 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -8,6 +8,7 @@ __author__ = "Benny " import logging +import math import os import pathlib import random @@ -412,10 +413,11 @@ def async_task(task_name, *args): inspect = app.control.inspect() worker_stats = inspect.stats() route_queues = [] + padding = math.ceil(sum([i['pool']['max-concurrency'] for i in worker_stats.values()]) / len(worker_stats)) for worker_name, stats in worker_stats.items(): route = worker_name.split('@')[1] concurrency = stats['pool']['max-concurrency'] - route_queues.extend([route] * concurrency) + route_queues.extend([route] * (concurrency + padding)) destination = random.choice(route_queues) logging.info("Selecting worker %s from %s in %.2fs", destination, route_queues, time.time() - t0) task_name.apply_async(args=args, queue=destination) From 2d7056899b575506918c1a85f68d43d97b192477 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Fri, 22 Jul 2022 22:40:35 +0800 Subject: [PATCH 024/297] bump --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index c46a1452..f334ccd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.6.29 +yt-dlp==2022.7.18 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 celery==5.2.7 -filetype==1.0.13 +filetype==1.1.0 flower==1.1.0 psutil==5.9.1 influxdb==5.3.1 From 6a4a0ff450a3d39319c80bea44ae598dc72d9630 Mon Sep 17 00:00:00 2001 From: BennyThink Date: Thu, 28 Jul 2022 21:31:01 +0800 Subject: [PATCH 025/297] disable search now --- requirements.txt | 2 +- ytdlbot/ytdl_bot.py | 41 ++++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/requirements.txt b/requirements.txt index f334ccd4..b117d1e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.1.0 psutil==5.9.1 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.8.1 +fakeredis==1.8.2 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 9620e676..7ce887a4 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -269,25 +269,28 @@ def download_handler(client: "Client", message: "types.Message"): logging.info("start %s", url) if not re.findall(r"^https?://", url.lower()): - red.update_metrics("search_request") - # TODO - result = VideosSearch(url, limit=5).result().get("result", []) - text = "" - count = 1 - buttons = [] - for item in result: - text += f"{count}. {item['title']} - {item['link']}\n\n" - buttons.append( - InlineKeyboardButton( - f"{count}", - callback_data=f"search_{item['id']}" - ) - ) - count += 1 - - markup = InlineKeyboardMarkup([buttons]) - client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup) + red.update_metrics("bad_request") + message.reply_text("I think you should send me a link.", quote=True) return + # TODO + # red.update_metrics("search_request") + # result = VideosSearch(url, limit=5).result().get("result", []) + # text = "" + # count = 1 + # buttons = [] + # for item in result: + # text += f"{count}. {item['title']} - {item['link']}\n\n" + # buttons.append( + # InlineKeyboardButton( + # f"{count}", + # callback_data=f"search_{item['id']}" + # ) + # ) + # count += 1 + # + # markup = InlineKeyboardMarkup([buttons]) + # client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup) + # return if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)): message.reply_text("Channel download is disabled now. Please send me individual video link.", quote=True) @@ -376,7 +379,7 @@ def periodic_sub_check(): scheduler.add_job(auto_restart, 'interval', seconds=5) scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) # default quota allocation of 10,000 units per day, - scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 60) + scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From 32ac23b70c20a66917d2c2ae3d0dcb1154fdcb4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 06:32:13 +0000 Subject: [PATCH 026/297] Bump fakeredis from 1.8.2 to 1.9.0 Bumps [fakeredis](https://github.com/cunla/fakeredis-py) from 1.8.2 to 1.9.0. - [Release notes](https://github.com/cunla/fakeredis-py/releases) - [Commits](https://github.com/cunla/fakeredis-py/compare/v1.8.2...v1.9.0) --- updated-dependencies: - dependency-name: fakeredis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b117d1e0..ea42d9c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.1.0 psutil==5.9.1 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.8.2 +fakeredis==1.9.0 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 From 6750c7555b12b1c91d86a1f05aff7cf69b3a8c3f Mon Sep 17 00:00:00 2001 From: BennyThink Date: Sun, 14 Aug 2022 10:39:56 +0800 Subject: [PATCH 027/297] bump --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index ea42d9c3..9c0bed66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,13 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.7.18 +yt-dlp==2022.8.8 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 celery==5.2.7 filetype==1.1.0 -flower==1.1.0 +flower==1.2.0 psutil==5.9.1 influxdb==5.3.1 beautifulsoup4==4.11.1 From 6bc54a5f6849de4143cecd1e02622c6ae8ab7b69 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 26 Aug 2022 23:11:39 +0800 Subject: [PATCH 028/297] heroku free, dependencies --- README.md | 2 ++ requirements.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2bda3417..39cf129f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Download videos from YouTube and other platforms through a Telegram Bot [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) Can't deploy? Fork to your personal account and deploy it there! +**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no longer be available.** +[Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) # Usage diff --git a/requirements.txt b/requirements.txt index 9c0bed66..b414a41c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.8.8 +yt-dlp==2022.8.19 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From e726eadfa348adca2341a04605a3d68064ea1dd4 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 27 Aug 2022 20:19:03 +0800 Subject: [PATCH 029/297] 3.10 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 918b8dd5..b9d57c88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM python:3.9-alpine as builder +FROM python:3.10-alpine as builder RUN apk update && apk add --no-cache tzdata alpine-sdk libffi-dev ca-certificates ADD requirements.txt /tmp/ RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt -FROM python:3.9-alpine +FROM python:3.10-alpine WORKDIR /ytdlbot/ytdlbot ENV TZ=Asia/Shanghai From c1385878ce4ecf1bcc8958b367611c4319445124 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 5 Sep 2022 18:22:21 +0800 Subject: [PATCH 030/297] Add FAQ --- FAQ.md | 25 +++++++++++++++++++++++++ README.md | 7 ++++++- requirements.txt | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 FAQ.md diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 00000000..0039e28b --- /dev/null +++ b/FAQ.md @@ -0,0 +1,25 @@ +# Project status + +This project is in zombie mode now. + +It is not maintained anymore. No features will be added, no bugs will be fixed. + +Existing code and bot will continue to work without any guarantees. + +# Can you support downloading from XXX? + +Please reach out to [yt-dlp](https://github.com/yt-dlp/yt-dlp) + +# My video is not downloading, slow, or not downloading at all + +Just wait. It is a free service, and it is not guaranteed to work. + +See [Grafana Dashboard](https://grafana.dmesg.app/d/9yXGmc1nk/youtube-download-celery?orgId=2) for current server status + +# Why is the bot not responding? + +If `/start` command is not responding, please contact me [@BennyThink](https://t.me/BennyThink) + +# Can't redeem VIP + +If you are not able to redeem VIP, please contact me [@BennyThink](https://t.me/BennyThink) diff --git a/README.md b/README.md index 39cf129f..4cafceed 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,15 @@ YouTube Download Bot๐Ÿš€ Download videos from YouTube and other platforms through a Telegram Bot ----- +**READ [FAQ](FAQ.md) FIRST IF YOU ENCOUNTER ANY ISSUES.** +----- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) Can't deploy? Fork to your personal account and deploy it there! -**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no longer be available.** + +**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no +longer be available.** [Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) # Usage @@ -258,6 +262,7 @@ https://www.youtube.com/playlist?list=PL1Hdq7xjQCJxQnGc05gS4wzHWccvEJy0w https://dmesg.app/m3u8/prog_index.m3u8 ## test twitter + https://twitter.com/nitori_sayaka/status/1526199729864200192 https://twitter.com/BennyThinks/status/1475836588542341124 diff --git a/requirements.txt b/requirements.txt index b414a41c..7588b76b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.8.19 +yt-dlp==2022.9.1 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 2371235c3583da6b1d73a33584f328987663210f Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 10 Sep 2022 19:02:50 +0800 Subject: [PATCH 031/297] bump --- .github/workflows/codeql-analysis.yml | 6 +++--- requirements.txt | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b6c6ea29..05a8e31f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # โ„น๏ธ Command-line programs to run using the OS shell. # ๐Ÿ“š https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 \ No newline at end of file + uses: github/codeql-action/analyze@v2 diff --git a/requirements.txt b/requirements.txt index 7588b76b..248cd6a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.3 -yt-dlp==2022.9.1 +yt-dlp==2022.10.4 APScheduler==3.9.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -8,15 +8,15 @@ PyMySQL==1.0.2 celery==5.2.7 filetype==1.1.0 flower==1.2.0 -psutil==5.9.1 +psutil==5.9.2 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.9.0 +fakeredis==1.9.3 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 requests==2.28.1 -tqdm==4.64.0 -requests-toolbelt==0.9.1 +tqdm==4.64.1 +requests-toolbelt==0.10.0 ffpb==0.4.1 youtube-search-python==1.6.6 From 3794dd9292fac4ff622c16da1ce71be5c738ae14 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 26 Oct 2022 20:17:33 +0800 Subject: [PATCH 032/297] pin and bump --- docker-compose.yml | 6 +++--- requirements.txt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e6012c1f..226aa2d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,13 +9,13 @@ services: entrypoint: [ "socat", "tcp-listen:2375,fork,reuseaddr","unix-connect:/var/run/docker.sock" ] redis: - image: redis:alpine + image: redis:7-alpine restart: always logging: driver: none mysql: - image: ubuntu/mysql:8.0-20.04_beta + image: ubuntu/mysql:8.0-22.04_beta restart: always volumes: - ./db_data:/var/lib/mysql @@ -48,4 +48,4 @@ services: volumes: - ./data/flower:/ytdlbot/ytdlbot/flower ports: - - "5555:5555" \ No newline at end of file + - "5555:5555" diff --git a/requirements.txt b/requirements.txt index 248cd6a1..4474df1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ pyrogram==1.4.16 -tgcrypto==1.2.3 +tgcrypto==1.2.4 yt-dlp==2022.10.4 APScheduler==3.9.1 beautifultable==1.1.0 @@ -8,15 +8,15 @@ PyMySQL==1.0.2 celery==5.2.7 filetype==1.1.0 flower==1.2.0 -psutil==5.9.2 +psutil==5.9.3 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.9.3 +fakeredis==1.10.0 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 requests==2.28.1 tqdm==4.64.1 -requests-toolbelt==0.10.0 +requests-toolbelt==0.10.1 ffpb==0.4.1 youtube-search-python==1.6.6 From d44f57c45c1bcf51e0cd3340f14cdd004948a60b Mon Sep 17 00:00:00 2001 From: Qunas101 <40661883+Qunas101@users.noreply.github.com> Date: Sun, 30 Oct 2022 12:23:47 +0200 Subject: [PATCH 033/297] Now VIP can be disabled, Previously "False" counted as True because string to boolean conversion (#155) --- ytdlbot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index e8d84adf..c47bf7c1 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -30,7 +30,7 @@ MULTIPLY = os.getenv("MULTIPLY", 5) # VIP1 is 5*5-25G, VIP2 is 50G USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 -ENABLE_VIP = os.getenv("VIP", False) +ENABLE_VIP: "bool" = True if os.getenv("VIP", False).lower == "true" else False MAX_DURATION = int(os.getenv("MAX_DURATION", 60)) AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") From 8fa3cb4aa11594c039910c7debb3cba7c99e3131 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 4 Nov 2022 16:59:55 +0800 Subject: [PATCH 034/297] Revert "Now VIP can be disabled, Previously "False" counted as True because string to boolean conversion (#155)" This reverts commit d44f57c45c1bcf51e0cd3340f14cdd004948a60b. --- ytdlbot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index c47bf7c1..e8d84adf 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -30,7 +30,7 @@ MULTIPLY = os.getenv("MULTIPLY", 5) # VIP1 is 5*5-25G, VIP2 is 50G USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 -ENABLE_VIP: "bool" = True if os.getenv("VIP", False).lower == "true" else False +ENABLE_VIP = os.getenv("VIP", False) MAX_DURATION = int(os.getenv("MAX_DURATION", 60)) AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") From db1605efa63bb9a9cb2d651fac613af650bc2228 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 8 Nov 2022 18:38:27 +0800 Subject: [PATCH 035/297] update some config --- requirements.txt | 4 ++-- ytdlbot/config.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4474df1e..871fb03a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,9 +6,9 @@ beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 celery==5.2.7 -filetype==1.1.0 +filetype==1.2.0 flower==1.2.0 -psutil==5.9.3 +psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 fakeredis==1.10.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index e8d84adf..de406b15 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -46,7 +46,7 @@ # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) -ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", True) +ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", False) BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4") MYSQL_HOST = os.getenv("MYSQL_HOST") From 7b5a0f7a4607c888a7d43a701bd7127c8a40eccc Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 10 Nov 2022 20:27:49 +0800 Subject: [PATCH 036/297] add token bucket --- README.md | 4 +++- requirements.txt | 3 ++- ytdlbot/config.py | 4 +++- ytdlbot/constant.py | 8 ++++++-- ytdlbot/utils.py | 2 +- ytdlbot/ytdl_bot.py | 25 ++++++++++++++++++++----- 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4cafceed..517c1621 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,10 @@ Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/support I don't have unlimited servers and bandwidth, so I have to make some restrictions. -* 10 GiB one-way traffic per 24 hours for each user +* 5 GiB one-way traffic per 24 hours for each user * maximum 5 minutes streaming conversion support * maximum 3 subscriptions +* limited request in certain time range You can choose to become 'VIP' if you really need large traffic. And also, you could always deploy your own bot. @@ -128,6 +129,7 @@ you can configure all the following environment variables: above `AUTHORIZED_USER` * ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. +* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream * MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode * MYSQL_USER * MYSQL_PASS diff --git a/requirements.txt b/requirements.txt index 871fb03a..df17a3c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.10.0 +fakeredis==1.10.1 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 @@ -20,3 +20,4 @@ tqdm==4.64.1 requests-toolbelt==0.10.1 ffpb==0.4.1 youtube-search-python==1.6.6 +token-bucket==0.3.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index de406b15..7d314398 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -19,7 +19,7 @@ REDIS = os.getenv("REDIS") # quota settings -QUOTA = int(os.getenv("QUOTA", 10 * 1024 * 1024 * 1024)) # 10G +QUOTA = int(os.getenv("QUOTA", 5 * 1024 * 1024 * 1024)) # 5G if os.uname().sysname == "Darwin": QUOTA = 10 * 1024 * 1024 # 10M @@ -58,3 +58,5 @@ IPv6 = os.getenv("IPv6", False) ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) +RATE = float(os.getenv("RATE", 60 * 5)) +BURST = int(os.getenv("BURST", 3)) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index bc8ceecc..517084cc 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -10,8 +10,8 @@ import os import time -from config import (AFD_LINK, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, EX, - MULTIPLY, REQUIRED_MEMBERSHIP, USD2CNY) +from config import (AFD_LINK, BURST, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, + EX, MULTIPLY, RATE, REQUIRED_MEMBERSHIP, USD2CNY) from db import InfluxDB from downloader import sizeof_fmt from limit import QUOTA, VIP @@ -33,6 +33,8 @@ class BotText: 4. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information. 5. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot + +6. Request limit is applied for everyone, excluding VIP users. """ if ENABLE_VIP else "Help text" about = "YouTube-DL by @BennyThink. Open source on GitHub: https://github.com/tgbot-collection/ytdlbot" @@ -149,3 +151,5 @@ def ping_worker(): text += f"{status}{hostname} **{active}** {load} {rev}\n" return text + + too_fast = f"You have reached rate limit. Current rate limit is 1 request per {RATE} seconds, {BURST - 1} bursts." diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 642d67f5..37de1abc 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -205,7 +205,7 @@ def next_salt_detector(self): def idle_detector(self): mtime = os.stat("/var/log/ytdl.log").st_mtime cur_ts = time.time() - if cur_ts - mtime > 300: + if cur_ts - mtime > 1800: logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) return True diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 7ce887a4..1bac9b35 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -22,11 +22,12 @@ from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime +from token_bucket import Limiter, MemoryStorage from youtubesearchpython import VideosSearch from client_init import create_app -from config import (AUTHORIZED_USER, ENABLE_CELERY, ENABLE_VIP, OWNER, - REQUIRED_MEMBERSHIP) +from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG, + ENABLE_VIP, OWNER, RATE, REQUIRED_MEMBERSHIP) from constant import BotText from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment @@ -44,6 +45,11 @@ logging.info("Authorized users are %s", AUTHORIZED_USER) +# rate, capacity +mem = MemoryStorage() +# 5 minutes, 2 bursts +lim = Limiter(1 / RATE, BURST, mem) + def private_use(func): def wrapper(client: "Client", message: "types.Message"): @@ -145,7 +151,7 @@ def patch_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["uncache"])) -def patch_handler(client: "Client", message: "types.Message"): +def uncache_handler(client: "Client", message: "types.Message"): username = message.from_user.username link = message.text.split()[1] if username == OWNER: @@ -169,7 +175,7 @@ def ping_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["about"])) -def help_handler(client: "Client", message: "types.Message"): +def about_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") client.send_message(chat_id, bot_text.about) @@ -296,6 +302,11 @@ def download_handler(client: "Client", message: "types.Message"): message.reply_text("Channel download is disabled now. Please send me individual video link.", quote=True) red.update_metrics("reject_channel") return + # non vip user, consume too many token + if (not VIP().check_vip(chat_id)) and (not lim.consume(str(chat_id).encode(), 1)): + red.update_metrics("rate_limit") + message.reply_text(bot_text.too_fast, quote=True) + return red.update_metrics("video_request") text = bot_text.get_receive_link_text() @@ -338,9 +349,13 @@ def download_resolution_callback(client: "Client", callback_query: types.Callbac @app.on_callback_query(filters.regex(r"convert")) def audio_callback(client: "Client", callback_query: types.CallbackQuery): + if not ENABLE_FFMPEG: + callback_query.answer("Audio conversion is disabled now.") + callback_query.message.reply_text("Audio conversion is disabled now.") + return + callback_query.answer(f"Converting to audio...please wait patiently") Redis().update_metrics("audio_request") - vmsg = callback_query.message audio_entrance(vmsg, client) From 876d8f629ea5662a6431e9f8a2883525cc42a908 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 22 Nov 2022 21:39:21 +0800 Subject: [PATCH 037/297] check filesize before hand --- requirements.txt | 8 ++++---- ytdlbot/downloader.py | 23 ++++++++++++++++++++--- ytdlbot/tasks.py | 9 ++++----- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index df17a3c6..155b1a74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyrogram==1.4.16 -tgcrypto==1.2.4 -yt-dlp==2022.10.4 -APScheduler==3.9.1 +tgcrypto==1.2.5 +yt-dlp==2022.11.11 +APScheduler==3.9.1.post1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 @@ -11,7 +11,7 @@ flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==1.10.1 +fakeredis==2.0.0 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index e2e73c0c..6cbcbe29 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -203,6 +203,13 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: ] adjust_formats(chat_id, url, formats, hijack) add_instagram_cookies(url, ydl_opts) + # check quota before download + if ENABLE_VIP: + # check quota after download + remain, _, ttl = VIP().check_remaining_quota(chat_id) + result, err_msg = check_quota(detect_filesize(url), chat_id) + if not result: + return {"status": False, "error": err_msg, "filepath": []} address = ["::", "0.0.0.0"] if IPv6 else [None] for format_ in formats: @@ -233,6 +240,7 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: p = pathlib.Path(tempdir, i) file_size = os.stat(p).st_size if ENABLE_VIP: + # check quota after download remain, _, ttl = VIP().check_remaining_quota(chat_id) result, err_msg = check_quota(file_size, chat_id) else: @@ -252,8 +260,8 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: convert_to_mp4(response, bm) if settings[2] == "audio" or hijack == "bestaudio[ext=m4a]": convert_audio_format(response, bm) - # disable it for now - # split_large_video(response) + # enable it for now + split_large_video(response) return response @@ -297,7 +305,7 @@ def add_instagram_cookies(url: "str", opt: "dict"): def run_splitter(video_path: "str"): - subprocess.check_output(f"sh split-video.sh {video_path} {TG_MAX_SIZE} ".split()) + subprocess.check_output(f"sh split-video.sh {video_path} {TG_MAX_SIZE * 0.95} ".split()) os.remove(video_path) @@ -313,3 +321,12 @@ def split_large_video(response: "dict"): if split and original_video: response["filepath"] = [i.as_posix() for i in pathlib.Path(original_video).parent.glob("*")] + + +def detect_filesize(url: "str") -> "int": + # find the largest file size + with ytdl.YoutubeDL() as ydl: + info_dict = ydl.extract_info(url, download=False) + max_size = max([i.get("filesize", 0) for i in info_dict["formats"] if i.get("filesize")]) + logging.info("%s max size is %s", url, max_size) + return max_size diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index aebeaf86..07f52278 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -263,11 +263,10 @@ def ytdl_normal_download(bot_msg, client, url): # normally there's only one video in that path... st_size = os.stat(video_path).st_size if st_size > TG_MAX_SIZE: - t = f"Your video({sizeof_fmt(st_size)}) is too large for Telegram. I'll upload it to transfer.sh" - bot_msg.edit_text(t) - client.send_chat_action(chat_id, 'upload_document') - client.send_message(chat_id, upload_transfer_sh(bot_msg, video_paths)) - return + bot_msg.edit_text(f"Your video({sizeof_fmt(st_size)}) is too large for Telegram.") + # client.send_chat_action(chat_id, 'upload_document') + # client.send_message(chat_id, upload_transfer_sh(bot_msg, video_paths)) + continue upload_processor(client, bot_msg, url, video_path) bot_msg.edit_text('Download success!โœ…') else: From b166729478e198076b5b44fdf37dff3eaa70fc25 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 23 Nov 2022 10:09:54 +0800 Subject: [PATCH 038/297] fix maxsize --- ytdlbot/downloader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 6cbcbe29..693ff0fc 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -327,6 +327,9 @@ def detect_filesize(url: "str") -> "int": # find the largest file size with ytdl.YoutubeDL() as ydl: info_dict = ydl.extract_info(url, download=False) - max_size = max([i.get("filesize", 0) for i in info_dict["formats"] if i.get("filesize")]) + max_size = 0 + max_size_list = [i.get("filesize", 0) for i in info_dict["formats"] if i.get("filesize")] + if max_size_list: + max_size = max(max_size_list) logging.info("%s max size is %s", url, max_size) return max_size From 1b245471dca2ed9609890c20f7d81f7dda285160 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 3 Dec 2022 20:50:57 +0100 Subject: [PATCH 039/297] bump --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 155b1a74..93169e58 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==2.0.0 +fakeredis==2.1.0 supervisor==4.2.4 tgbot-ping==1.0.4 redis==4.3.3 From c8c04b4321067806c2f2a9dc7fc2c1a3c81794be Mon Sep 17 00:00:00 2001 From: dlcvietnam Date: Fri, 25 Nov 2022 14:09:56 +0700 Subject: [PATCH 040/297] Add K8S Deloyment and Readme --- README.md | 159 ++++++++++++++++++++++++++++++++++++++++- k8s/01.redis.yml | 53 ++++++++++++++ k8s/02.mariadb.yml | 80 +++++++++++++++++++++ k8s/03.configmap.yml | 17 +++++ k8s/04.ytdl-master.yml | 65 +++++++++++++++++ k8s/05.ytdl-worker.yml | 47 ++++++++++++ k8s/06.flower.yml | 101 ++++++++++++++++++++++++++ 7 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 k8s/01.redis.yml create mode 100644 k8s/02.mariadb.yml create mode 100644 k8s/03.configmap.yml create mode 100644 k8s/04.ytdl-master.yml create mode 100644 k8s/05.ytdl-worker.yml create mode 100644 k8s/06.flower.yml diff --git a/README.md b/README.md index 517c1621..3a8d4095 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ You can also limit CPU and RAM usage by adding an `deploy' key: Be sure to use `--compatibility` when deploying. + ## 4. run ### 4.1. standalone mode @@ -231,6 +232,162 @@ docker-compose -f worker.yml up -d **โš ๏ธ Bear in mind don't publish redis directly on the internet! You can use WireGuard to wrap it up.** +## Kubernetes + +Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications + +# Complete deployment guide for k8s deloyment + +* contains every functionality +* compatible with amd64, arm64 and armv7l + +## First. Get all file in k8s folder + +Download `k8s` file to a directory on your k8s server and go to this folder + +## 1. Create Redis deloyment + +```shell +kubectl apply -f 01.redis.yml +``` + +This command will create ytdl namespace, redis pod and redis service + +## 2. Creat MariaDB deloyment + +```shell +kubectl apply -f 02.mariadb.yml +``` +This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your storageClassName before apply. + +## 3. Set environment variables + +Create configMap for env +### 3.1 Edit configmap.yml + +```shell +vim 03.configmap.yml +``` + +you can configure all the following environment variables: + +* PYRO_WORKERS: number of workers for pyrogram, default is 100 +* WORKERS: workers count for celery +* APP_ID: **REQUIRED**, get it from https://core.telegram.org/ +* APP_HASH: **REQUIRED** +* TOKEN: **REQUIRED** +* REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ + +* OWNER: owner username +* QUOTA: quota in bytes +* EX: quota expire time +* MULTIPLY: vip quota comparing to normal quota +* USD2CNY: exchange rate +* VIP: VIP mode, default: disable +* AFD_LINK +* COFFEE_LINK +* COFFEE_TOKEN +* AFD_TOKEN +* AFD_USER_ID + +* AUTHORIZED_USER: users that could use this bot, user_id, separated with `,` +* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot. Could be use with + above `AUTHORIZED_USER` + +* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. +* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream +* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode +* MYSQL_USER +* MYSQL_PASS +* GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. +* AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For + example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. +* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. + **Inline button will be lost during the forwarding.** + +### 3.2 Apply configMap for environment variables + +```shell +kubectl apply -f 03.configmap.yml +``` + +## 4. Run Master Celery + +```shell +kubectl apply -f 04.ytdl-master.yml +``` +This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, cookies folder and flower database. Please replace longhorn with your storageClassName before apply + +### 4.1 Setup instagram cookies +Required if you want to support instagram. + +You can use this extension +[Get cookies.txt](https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid) +to get instagram cookies + +Get pod running ytdl master: +```shell +kubectl get pods --namespace ytdl +``` +Name should be ytdl-xxxxxxxx + +Access to pod +```shell +kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh +``` +(replace ytdl-xxx by your pod name) + +Go to ytdl-pvc mounted folder +```shell +cd /ytdlbot/ytdlbot/data/ +vim instagram.com_cookies.txt +# paste your cookies +``` +## 5. Run Worker Celery + +```shell +kubectl apply -f 05.ytdl-worker.yml +``` + +## 6. Run Flower image (OPTIONAL) + + +### 6.1 Setup flower db + +Get pod running ytdl master: +```shell +kubectl get pods --namespace ytdl +``` +Name should be ytdl-xxxxxxxx + +Access to pod +```shell +kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh +``` +(replace ytdl-xxx by your pod name) + +Go to ytdl-pvc mounted folder +```shel +cd /var/lib/vnstat/ +``` + +Create flower database file +```shell +{} ~ python3 +Python 3.9.9 (main, Nov 21 2021, 03:22:47) +[Clang 12.0.0 (clang-1200.0.32.29)] on darwin +Type "help", "copyright", "credits" or "license" for more information. +>>> import dbm;dbm.open("flower","n");exit() +``` +### 6.2 Config Flower Ingress +This step need config ingress from line 51 with your ingress service for access from internet +YML file should be adjusted depending on your load balancing, ingress and network system +### 6.2 Apply Flower deloyment + +```shell +kubectl apply -f 06.flower.yml +``` + # Command ``` @@ -275,4 +432,4 @@ https://twitter.com/BennyThinks/status/1475836588542341124 # License -Apache License 2.0 +Apache License 2.0 \ No newline at end of file diff --git a/k8s/01.redis.yml b/k8s/01.redis.yml new file mode 100644 index 00000000..da52fc3c --- /dev/null +++ b/k8s/01.redis.yml @@ -0,0 +1,53 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: ytdl + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + ytdl: redis + name: redis + namespace: ytdl +spec: + replicas: 1 + selector: + matchLabels: + ytdl: redis + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + ytdl: redis + spec: + containers: + - image: redis:7-alpine + name: redis + ports: + - containerPort: 6379 + resources: {} + restartPolicy: Always +status: {} + +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + ytdl: redis + name: redis + namespace: ytdl +spec: + ports: + - name: "6379" + port: 6379 + targetPort: 6379 + selector: + ytdl: redis +status: + loadBalancer: {} \ No newline at end of file diff --git a/k8s/02.mariadb.yml b/k8s/02.mariadb.yml new file mode 100644 index 00000000..c89dbc75 --- /dev/null +++ b/k8s/02.mariadb.yml @@ -0,0 +1,80 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + ytdl: mariadb-pvc + name: mariadb-pvc + namespace: ytdl +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 10Gi +status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + creationTimestamp: null + labels: + ytdl: mariadb + name: mariadb + namespace: ytdl +spec: + replicas: 1 + selector: + matchLabels: + ytdl: mariadb + strategy: + type: Recreate + template: + metadata: + creationTimestamp: null + labels: + ytdl: mariadb + spec: + containers: + - env: + - name: MYSQL_ROOT_PASSWORD + value: ro0tP4sSworD + - name: MYSQL_DATABASE + value: ytdl + image: mariadb:latest + name: mariadb + ports: + - containerPort: 3306 + resources: {} + volumeMounts: + - mountPath: /var/lib/mysql + name: "mariadb-persistent-storage" + restartPolicy: Always + volumes: + - name: mariadb-persistent-storage + persistentVolumeClaim: + claimName: mariadb-pvc +status: {} + +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + ytdl: mariadb + name: mariadb-svc + namespace: ytdl +spec: + ports: + - name: "3306" + port: 3306 + targetPort: 3306 + selector: + ytdl: mariadb +status: + loadBalancer: {} + diff --git a/k8s/03.configmap.yml b/k8s/03.configmap.yml new file mode 100644 index 00000000..90ec84a8 --- /dev/null +++ b/k8s/03.configmap.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: ytdlenv + namespace: ytdl + annotations: +data: + APP_HASH: + APP_ID: + TOKEN: + ARCHIVE_ID: + ENABLE_CELERY: 'True' + ENABLE_FFMPEG: 'True' + MYSQL_HOST: mariadb-svc + MYSQL_PASS: ro0tP4sSworD + MYSQL_USER: root + REDIS: redis \ No newline at end of file diff --git a/k8s/04.ytdl-master.yml b/k8s/04.ytdl-master.yml new file mode 100644 index 00000000..f17579f6 --- /dev/null +++ b/k8s/04.ytdl-master.yml @@ -0,0 +1,65 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ytdl-pvc + namespace: ytdl + creationTimestamp: null + labels: + ytdl: ytdl-pvc +spec: + accessModes: + - ReadWriteMany + storageClassName: longhorn + resources: + requests: + storage: 10Gi +status: {} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ytdl + namespace: ytdl + creationTimestamp: null + labels: + ytdl: ytdl +spec: + replicas: 1 + selector: + matchLabels: + ytdl: ytdl + template: + metadata: + creationTimestamp: null + labels: + ytdl: ytdl + spec: + volumes: + - name: ytdl-pvc + persistentVolumeClaim: + claimName: ytdl-pvc + containers: + - name: ytdl + image: bennythink/ytdlbot + envFrom: + - configMapRef: + name: ytdlenv + resources: {} + volumeMounts: + - name: ytdl-pvc + mountPath: /var/lib/vnstat/ + subPath: vnstat/ + - name: ytdl-pvc + mountPath: /ytdlbot/ytdlbot/data/ + subPath: data/ + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: Always + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + schedulerName: default-scheduler +status: {} diff --git a/k8s/05.ytdl-worker.yml b/k8s/05.ytdl-worker.yml new file mode 100644 index 00000000..ca154655 --- /dev/null +++ b/k8s/05.ytdl-worker.yml @@ -0,0 +1,47 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + ytdl: ytdl-worker + name: ytdl-worker + namespace: ytdl +spec: + replicas: 4 + selector: + matchLabels: + ytdl: ytdl-worker + template: + metadata: + creationTimestamp: null + labels: + ytdl: ytdl-worker + spec: + volumes: + - name: ytdl-pvc + persistentVolumeClaim: + claimName: ytdl-pvc + containers: + - name: ytdl-worker + image: bennythink/ytdlbot + args: + - /usr/local/bin/supervisord + - '-c' + - /ytdlbot/conf/supervisor_worker.conf + envFrom: + - configMapRef: + name: ytdlenv + resources: {} + volumeMounts: + - name: ytdl-pvc + mountPath: /ytdlbot/ytdlbot/data/ + subPath: data/ + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: Always + restartPolicy: Always + terminationGracePeriodSeconds: 30 + dnsPolicy: ClusterFirst + securityContext: {} + schedulerName: default-scheduler +status: {} diff --git a/k8s/06.flower.yml b/k8s/06.flower.yml new file mode 100644 index 00000000..e7c01c73 --- /dev/null +++ b/k8s/06.flower.yml @@ -0,0 +1,101 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + ytdl: flower + name: flower + namespace: ytdl +spec: + replicas: 1 + selector: + matchLabels: + ytdl: flower + strategy: + type: Recreate + template: + metadata: + creationTimestamp: null + labels: + ytdl: flower + spec: + containers: + - envFrom: + - configMapRef: + name: ytdlenv + args: + - /usr/local/bin/celery + - -A + - flower_tasks + - flower + - --basic_auth=bennythink:123456 + - --address=0.0.0.0 + - --persistent + - --purge_offline_workers=3600 + image: bennythink/ytdlbot + name: flower + ports: + - containerPort: 5555 + resources: {} + volumeMounts: + - name: ytdl-pvc + mountPath: /ytdlbot/ytdlbot/flower + subPath: vnstat/flower + restartPolicy: Always + volumes: + - name: ytdl-pvc + persistentVolumeClaim: + claimName: ytdl-pvc +status: {} + +# THIS IS OPTION IF YOU WANT PUBLIC FLOWER PAGE TO INTERNET. +# should be adjusted depending on your load balancing system machine +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + ytdl: flower + name: flower-svc + namespace: ytdl +spec: + type: NodePort + ports: + - name: "5555" + protocol: TCP + port: 5555 + targetPort: 5555 + selector: + ytdl: flower +status: + loadBalancer: {} + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: nginx-flower-ingress + namespace: ytdl + annotations: + # cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/rewrite-target: / + # nginx.ingress.kubernetes.io/whitelist-source-range: 14.161.27.151 limit by ipaddresss + +spec: + ingressClassName: nginx + tls: + - hosts: + - your-domain + secretName: flower-tls + rules: + - host: your-domain + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: flower-svc + port: + number: 5555 \ No newline at end of file From 9b9da44bac2becb3c7c718d372f225f2b5b427a6 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 24 Dec 2022 16:05:41 +0100 Subject: [PATCH 041/297] reject list as well --- ytdlbot/ytdl_bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 1bac9b35..e0e0c264 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -298,8 +298,8 @@ def download_handler(client: "Client", message: "types.Message"): # client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup) # return - if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)): - message.reply_text("Channel download is disabled now. Please send me individual video link.", quote=True) + if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url: + message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True) red.update_metrics("reject_channel") return # non vip user, consume too many token From c17cad65a8ec1e180eab5eb1f0e05bee21c9e047 Mon Sep 17 00:00:00 2001 From: cpanel10x <96235876+dlcvietnam@users.noreply.github.com> Date: Sat, 10 Dec 2022 00:52:50 +0700 Subject: [PATCH 042/297] Fix error cache #173 --- ytdlbot/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 07f52278..e760cbc2 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -357,7 +357,7 @@ def gen_cap(bm, url, video_path): ) remain = bot_text.remaining_quota_caption(chat_id) worker = get_dl_source() - cap = f"{user_info}\n`{file_name}`\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" \ + cap = f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" \ f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" return cap, meta From f4d9974cb3a7b57789a8890bf1327ef6ee8d195f Mon Sep 17 00:00:00 2001 From: cpanel10x <96235876+dlcvietnam@users.noreply.github.com> Date: Tue, 6 Dec 2022 08:53:03 +0700 Subject: [PATCH 043/297] Update README.md --- README.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3a8d4095..46d3fde9 100644 --- a/README.md +++ b/README.md @@ -380,9 +380,29 @@ Type "help", "copyright", "credits" or "license" for more information. >>> import dbm;dbm.open("flower","n");exit() ``` ### 6.2 Config Flower Ingress -This step need config ingress from line 51 with your ingress service for access from internet +This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from internet. YML file should be adjusted depending on your load balancing, ingress and network system -### 6.2 Apply Flower deloyment + +For active SSL +```yml +cert-manager.io/cluster-issuer: letsencrypt-prod +``` + +Replace nginx by your ingress service +```yml +ingressClassName: nginx +``` + +Add your domain, example +```yml +tls: + - hosts: + - flower.benny.com + secretName: flower-tls + rules: + - host: flower.benny.com +``` +### 6.3 Apply Flower deloyment ```shell kubectl apply -f 06.flower.yml @@ -432,4 +452,4 @@ https://twitter.com/BennyThinks/status/1475836588542341124 # License -Apache License 2.0 \ No newline at end of file +Apache License 2.0 From 67ad5a6bd9fe17f717b3bf2ddfc9e61f566f4a11 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 27 Dec 2022 21:57:33 +0100 Subject: [PATCH 044/297] create celery app, not in-memory --- .gitignore | 5 +++-- ytdlbot/client_init.py | 2 +- ytdlbot/session/readme.txt | 1 + ytdlbot/tasks.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 ytdlbot/session/readme.txt diff --git a/.gitignore b/.gitignore index ea7f521a..4c68f546 100644 --- a/.gitignore +++ b/.gitignore @@ -155,8 +155,9 @@ db_data/* env/* .ash_history .DS_Store -ytdlbot/ytdl.session +ytdlbot/session/ytdl.session +ytdlbot/session/celery.session data/* upgrade_worker.sh ytdl.session -reinforcement/* \ No newline at end of file +reinforcement/* diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 5f9f9019..fe7fa53c 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -14,7 +14,7 @@ from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 -def create_app(session="ytdl", workers=PYRO_WORKERS): +def create_app(session="session/ytdl", workers=PYRO_WORKERS): _app = Client(session, APP_ID, APP_HASH, bot_token=TOKEN, workers=workers, ipv6=IPv6, diff --git a/ytdlbot/session/readme.txt b/ytdlbot/session/readme.txt new file mode 100644 index 00000000..b345f25d --- /dev/null +++ b/ytdlbot/session/readme.txt @@ -0,0 +1 @@ +dir for session files. diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index e760cbc2..f9bbef9e 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -51,7 +51,7 @@ # app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') app = Celery('tasks', broker=BROKER) -celery_client = create_app(":memory:") +celery_client = create_app("session/celery") def get_messages(chat_id, message_id): From 98d9edbd617af21b79586b0f4abf595afaf4252d Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 28 Dec 2022 18:07:40 +0100 Subject: [PATCH 045/297] startup scripts --- {tools => scripts}/migrate_to_mysql.py | 0 scripts/start.sh | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) rename {tools => scripts}/migrate_to_mysql.py (100%) create mode 100644 scripts/start.sh diff --git a/tools/migrate_to_mysql.py b/scripts/migrate_to_mysql.py similarity index 100% rename from tools/migrate_to_mysql.py rename to scripts/migrate_to_mysql.py diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 00000000..acb78ec9 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,17 @@ +docker run -d --restart unless-stopped --name ytdl \ + --net host \ + -e TOKEN=12345 \ + -e APP_ID=123123 \ + -e APP_HASH=4990 \ + -e ENABLE_CELERY=True \ + -e REDIS=192.168.6.1 \ + -e MYSQL_HOST=192.168.6.1 \ + -e WORKERS=4 \ + -e VIP=True \ + -e CUSTOM_TEXT=#StandWithUkraine \ + -e WORKER_NAME=Pi \ + -v "$(pwd)"/session:/ytdlbot/ytdlbot/session \ + -v "$(pwd)"/ramdisk:/tmp/ \ + --log-driver none \ + bennythink/ytdlbot \ + /usr/local/bin/supervisord -c "/ytdlbot/conf/supervisor_worker.conf" From 3d3393dc8df4bc72ad84ec31cef1ebef86014555 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 31 Dec 2022 17:32:38 +0100 Subject: [PATCH 046/297] purge tasks --- requirements.txt | 4 ++-- ytdlbot/tasks.py | 9 ++++++--- ytdlbot/ytdl_bot.py | 10 ++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 93169e58..9df4231b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,8 @@ flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==2.1.0 -supervisor==4.2.4 +fakeredis==2.4.0 +supervisor==4.2.5 tgbot-ping==1.0.4 redis==4.3.3 requests==2.28.1 diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index f9bbef9e..73801051 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -27,16 +27,16 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel -from pyrogram import Client, idle +from pyrogram import idle from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor from client_init import create_app -from config import (ARCHIVE_ID, AUDIO_FORMAT, BROKER, ENABLE_CELERY, +from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE, ENABLE_VIP, TG_MAX_SIZE, WORKERS) from constant import BotText from db import Redis -from downloader import (edit_text, run_ffmpeg, sizeof_fmt, tqdm_progress, +from downloader import (edit_text, sizeof_fmt, tqdm_progress, upload_hook, ytdl_download) from limit import VIP from utils import (apply_log_formatter, auto_restart, customize_logger, @@ -433,6 +433,9 @@ def run_celery(): argv.extend(["-Q", worker_name]) app.worker_main(argv) +def purge_tasks(): + count = app.control.purge() + return f"purged {count} tasks." if __name__ == '__main__': celery_client.start() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index e0e0c264..34b03329 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -23,7 +23,6 @@ from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime from token_bucket import Limiter, MemoryStorage -from youtubesearchpython import VideosSearch from client_init import create_app from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG, @@ -32,7 +31,7 @@ from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment from tasks import app as celery_app -from tasks import (audio_entrance, direct_download_entrance, hot_patch, +from tasks import (audio_entrance, direct_download_entrance, hot_patch, purge_tasks, ytdl_download_entrance) from utils import (auto_restart, customize_logger, get_revision, get_user_settings, set_user_settings) @@ -159,6 +158,13 @@ def uncache_handler(client: "Client", message: "types.Message"): message.reply_text(f"{count} cache(s) deleted.", quote=True) +@app.on_message(filters.command(["purge"])) +def purge_handler(client: "Client", message: "types.Message"): + username = message.from_user.username + if username == OWNER: + message.reply_text(purge_tasks(), quote=True) + + @app.on_message(filters.command(["ping"])) def ping_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id From 4727c06d483aa09542025e32a1a8aae204457866 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 4 Jan 2023 18:03:54 +0100 Subject: [PATCH 047/297] auto clean --- ytdlbot/utils.py | 7 ++++++- ytdlbot/ytdl_bot.py | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 37de1abc..84ff863e 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -21,7 +21,6 @@ import ffmpeg import psutil -from config import ENABLE_CELERY from db import MySQL from flower_tasks import app @@ -228,5 +227,11 @@ def auto_restart(): psutil.Process().kill() +def clean_tempfile(): + for item in pathlib.Path(tempfile.gettempdir()).glob("ytdl-*"): + if time.time() - item.stat().st_ctime > 3600: + shutil.rmtree(item, ignore_errors=True) + + if __name__ == '__main__': auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 34b03329..32a9132f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -33,7 +33,7 @@ from tasks import app as celery_app from tasks import (audio_entrance, direct_download_entrance, hot_patch, purge_tasks, ytdl_download_entrance) -from utils import (auto_restart, customize_logger, get_revision, +from utils import (auto_restart, customize_logger, get_revision, clean_tempfile, get_user_settings, set_user_settings) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) @@ -395,12 +395,13 @@ def periodic_sub_check(): if __name__ == '__main__': MySQL() - scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={'max_instances': 5}) + scheduler = BackgroundScheduler(timezone="Europe/Stockholm", job_defaults={'max_instances': 5}) scheduler.add_job(Redis().reset_today, 'cron', hour=0, minute=0) - scheduler.add_job(auto_restart, 'interval', seconds=5) + scheduler.add_job(auto_restart, 'interval', seconds=60) + scheduler.add_job(clean_tempfile, 'interval', seconds=60) scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) - # default quota allocation of 10,000 units per day, - scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 30) + # default quota allocation of 10,000 units per day + scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 60) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From 4c8e17cf214dbf86308e1775ce8d5f53c4994437 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 4 Jan 2023 18:20:25 +0100 Subject: [PATCH 048/297] update tz --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b9d57c88..8dde3a93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt FROM python:3.10-alpine WORKDIR /ytdlbot/ytdlbot -ENV TZ=Asia/Shanghai +ENV TZ=Europe/Stockholm COPY apk.txt /tmp/ RUN apk update && xargs apk add < /tmp/apk.txt From d5c925dc2037cee1b93380adfc3d0c613b375cd4 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 4 Jan 2023 18:25:17 +0100 Subject: [PATCH 049/297] add vnstat for worker --- conf/supervisor_worker.conf | 5 ++++- requirements.txt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/conf/supervisor_worker.conf b/conf/supervisor_worker.conf index 545bde71..dbad141c 100644 --- a/conf/supervisor_worker.conf +++ b/conf/supervisor_worker.conf @@ -5,6 +5,9 @@ logfile_maxbytes=0 user=root +[program:vnstat] +command=vnstatd -n +autorestart=true [program:worker] directory=/ytdlbot/ytdlbot/ @@ -27,4 +30,4 @@ priority=999 redirect_stderr=true stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 \ No newline at end of file +stdout_logfile_maxbytes=0 diff --git a/requirements.txt b/requirements.txt index 9df4231b..f2be5122 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2022.11.11 +yt-dlp==2023.1.2 APScheduler==3.9.1.post1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 3cbfe3f1f191b6f6b3bf58be0ebab1bcb03fb7e5 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 6 Jan 2023 17:06:26 +0100 Subject: [PATCH 050/297] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ README.md | 53 ++++++++++++++++++++++++++++++++++++-------- assets/CNY.png | Bin 0 -> 57380 bytes assets/SEK.png | Bin 0 -> 58480 bytes assets/USD.png | Bin 0 -> 58368 bytes 5 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 assets/CNY.png create mode 100644 assets/SEK.png create mode 100644 assets/USD.png diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..1aef0940 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: BennyThink +custom: https://buy.stripe.com/bIYbMa9JletbevCaEE diff --git a/README.md b/README.md index 46d3fde9..e09c06d6 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,6 @@ You can also limit CPU and RAM usage by adding an `deploy' key: Be sure to use `--compatibility` when deploying. - ## 4. run ### 4.1. standalone mode @@ -234,7 +233,8 @@ docker-compose -f worker.yml up -d ## Kubernetes -Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications +Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of +containerized applications # Complete deployment guide for k8s deloyment @@ -258,11 +258,14 @@ This command will create ytdl namespace, redis pod and redis service ```shell kubectl apply -f 02.mariadb.yml ``` -This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your storageClassName before apply. + +This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your +storageClassName before apply. ## 3. Set environment variables Create configMap for env + ### 3.1 Edit configmap.yml ```shell @@ -316,9 +319,12 @@ kubectl apply -f 03.configmap.yml ```shell kubectl apply -f 04.ytdl-master.yml ``` -This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, cookies folder and flower database. Please replace longhorn with your storageClassName before apply + +This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, +cookies folder and flower database. Please replace longhorn with your storageClassName before apply ### 4.1 Setup instagram cookies + Required if you want to support instagram. You can use this extension @@ -326,23 +332,29 @@ You can use this extension to get instagram cookies Get pod running ytdl master: + ```shell kubectl get pods --namespace ytdl ``` + Name should be ytdl-xxxxxxxx Access to pod + ```shell kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh ``` + (replace ytdl-xxx by your pod name) Go to ytdl-pvc mounted folder + ```shell cd /ytdlbot/ytdlbot/data/ vim instagram.com_cookies.txt # paste your cookies ``` + ## 5. Run Worker Celery ```shell @@ -351,27 +363,32 @@ kubectl apply -f 05.ytdl-worker.yml ## 6. Run Flower image (OPTIONAL) - ### 6.1 Setup flower db Get pod running ytdl master: + ```shell kubectl get pods --namespace ytdl ``` + Name should be ytdl-xxxxxxxx Access to pod + ```shell kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh ``` + (replace ytdl-xxx by your pod name) Go to ytdl-pvc mounted folder + ```shel cd /var/lib/vnstat/ ``` Create flower database file + ```shell {} ~ python3 Python 3.9.9 (main, Nov 21 2021, 03:22:47) @@ -379,29 +396,36 @@ Python 3.9.9 (main, Nov 21 2021, 03:22:47) Type "help", "copyright", "credits" or "license" for more information. >>> import dbm;dbm.open("flower","n");exit() ``` + ### 6.2 Config Flower Ingress -This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from internet. + +This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from +internet. YML file should be adjusted depending on your load balancing, ingress and network system For active SSL + ```yml cert-manager.io/cluster-issuer: letsencrypt-prod ``` Replace nginx by your ingress service + ```yml ingressClassName: nginx ``` Add your domain, example + ```yml tls: - - hosts: - - flower.benny.com - secretName: flower-tls + - hosts: + - flower.benny.com + secretName: flower-tls rules: - host: flower.benny.com ``` + ### 6.3 Apply Flower deloyment ```shell @@ -449,6 +473,17 @@ https://twitter.com/BennyThinks/status/1475836588542341124 * [Buy me a coffee](https://www.buymeacoffee.com/bennythink) * [Afdian](https://afdian.net/@BennyThink) +* [GitHub Sponsor](https://github.com/sponsors/BennyThink) + +## Stripe + +You can choose to donate via Stripe. Please click the button below to donate via Stripe. +Choose the currency and payment method that suits you. + +| USD(Card, Apple Pay and Google Pay) | SEK(Card, Apple Pay and Google Pay) | CNY(Card, Apple Pay, Google Pay and Alipay) | +|--------------------------------------------------|--------------------------------------------------|--------------------------------------------------| +| [USD](https://buy.stripe.com/cN203sdZB98RevC3cd) | [SEK](https://buy.stripe.com/bIYbMa9JletbevCaEE) | [CNY](https://buy.stripe.com/dR67vU4p13Ox73a6oq) | +| ![](assets/USD.png) | ![](assets/SEK.png) | ![](assets/CNY.png) | # License diff --git a/assets/CNY.png b/assets/CNY.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf5838020330e6b9c028bed3c10e470fd11d10d GIT binary patch literal 57380 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 z�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 GLK6VIy-mab literal 0 HcmV?d00001 diff --git a/assets/SEK.png b/assets/SEK.png new file mode 100644 index 0000000000000000000000000000000000000000..9e99f652b987c442462a85eabbccb029e2f8bef3 GIT binary patch literal 58480 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 literal 0 HcmV?d00001 diff --git a/assets/USD.png b/assets/USD.png new file mode 100644 index 0000000000000000000000000000000000000000..108fee1c474db96119513a805f269fb8e179dbbb GIT binary patch literal 58368 zcmeFac{tQ<|1eyJDQQwt7(#_ekrk__96TBp2MZoegE#?^*+DnIo|hpj{fMlT=|~o`8hw^`PuW=)>4M;fbZC{WeZGI zMNxOl76@j`7Fw?DQ1Cap;|y)!KU;6=DxcYs*U0g0%NFh}s)`EwF6JZU?i04whV8Ru z>5Jd_4+XlgY1eGoag;Umev16g#}`!k?;h2RiXKx)KhKz<&2u+VN742WoG$YY&7QPU z<*4|~3s?4;Go@;TW3suS2YpyRGi|#|IURUkBVlYR&CAnmFt#FgYHYS=xNvr1Z28pc zcP^{(>6|Z9W@wqF*^!nn&a4kBgZ>qbFoB9Qox77|EqDGG)%og2U?*#bs%} zeHS^RR-|`Wvc{dWX0F46gA-16@eH&-?oi5id@H?*JPlK*4=oS(R{HJ`LaxXziCRfn zPY$vi_&BqQbsX==VJFA2Yt=H4TzQ&fmz4 zzY=CT$%`_JBZXXuyaRHRE@ji7$e6kJu+6MKSr_4d=^;+KPJE4=ESc4N6(dkshC-&Bk z3!*O@b&>RHe009NUz2z$5{_?LdPf%Y8bbCh4OLz4&d_Rg+2FFj@gBCFBZTZfyF}M6 zw=7sWInkI^R==~`f>CbyLoM0GgQF%tPpwh%w?$9rIcjGQ>>@v_?OS^9Y&eqqnjkY% zC&eYp$%vGz!5cmd7x{&>Tj_7yC*)RrniB|-n+}j@>S?(FaV8@qRtb_El^(W-DujQ- zRts&gUz1n=@%Gh`SeeF?n2?IW!7qR>grgt%jH~@h`i~4)GG&79e8s>53$mo|0W5ZK zRQGemkzckPi6U_9b$*DQ%GVrw+1*{bCTJV1cOr&x*E??V24r?g?!sj2$Q{_Plu)@; zOS{XDeE7>s6`vIw{)?ABfwk{brT9zo(VN4+phkWl*2GUlb}*JpcUQLHYFH}_=VaF>Yr@oW@s_@U;2{eD}f{i8GDkK+< z^V0K?ew;B(4q;gL?+=|TW-yD}92eDpkwCC6fS!Y~_Ppz4rcv29{g02JV~+V!6_cGU z6009r$p-1Z^NYX043rLL54O0+?dw9m2brDGuClM|2&_9uIesO}o+H6aXGaCkFSrCS zwQT38F_NRSo=vcShApK;$8d=Pp-p%ZeO~of;)D{8(bQJeQn}t}b#k?SrDU81&(7Gl zgtp-D9?qflF#a8SJ|@Yf=_9=!%f)dvBwJ~RslBz7xnjP&ev99s)>a^4<{HjRJ1)nV z3$@Br0v+=?ZgmleY~~eDiw(?ekdmNC<45lE=+87-YJUXlwg3`7T}!5z9Jx0u@|zej z@$+CLIb_Eu>3QB=56)JMP(hYkW~pJVEX8=9{g*4=qVcBqOUQk9*Mj5QD+klf9fv30 zpVtT(e)KOOYmz!C-)23lOnRqmoNGPbfSmy#Blgl>#?$>ZkJcZ6AE0AJw{z@Dn0gH~ z(^3U=?1NvzN?sLfD{lr;hEX`N z=wIXrpxRW0G%iCK^pjoSg^o>!aP+OVQ3g$fcMhR{N5z)icqjbIkc713+_UWW<12#U zAu^NS$4_zAvSX)r{-&hmX;_#fIckPEy@!&r6NsL*;DU4Cj?}P*n6tLD|9(H8gAwE{W3%u$Q5diDn{rhlouQGNdmY2I5bDg1Kkq(DS&o0{IE6H2efKZhZQ+?9 z6%AL;t-Xa`R=DuB=2P$Nq&RJt4KOlie|v~R33q9C)uJnxDaF!r?Y2OuZM{}LB&<3e zR73toUIMt%F60FD-I+T@MXU45fXJ@OM`q1me@7OE4NI1grVJV_6&J(051$8$GZ^V; z%-t9E8^>%7CtO~tIuh^p^(>QG(NJ;h9JcJI|rt z`6!pZY=soE`}*^$G^_kSNvXy>q%_Lve5vHxyGC~AB2 z`8T3-RD7EFijH5A@~fJoz5GLh{suuFNmEOnEA^`du$6{bEJasXM@Rk6yBsxh+l*Ej zyBUK-WK}r+4G4@}oSt*viVxJxoy%?M?-`{6k(4AqO1<3Y6Xw$uUg9RkOt?+R!apE%dG$-Ke3LsB?7al3r9k?; zr-_YCx6u3VLv866@>f#*C=a@%w{Wm1jdo`jfi}(G`Bk0Irp)TbROlS!*2fb=u78l`c{q?*GQ}; z6vrpe7mvZ^JC;W_P)81xg@UEI)k@qSivXroN`uy zeUws8)=G-RybULf;FTF=zBd5T9EGOJ27FZJ|ze31l+1`9x(Gv?3a;5uctZ3h){V!5Zs-|6OPme#)_r21(zcF(stefq**Wx%{Tz1|W)!QgP zn7mo0@xoZUrF&Mj$YsH3eCD+$*ccVHDe*nwA)DYx56pm{>zC+0Fh1ww~T&)M%bDCiY1>wv;g`n`_^Ma^+Wr z)uK0g2A{__zfi#2Ay#V5AE_X*dA}xjUOx;ds~<&Io{qb30fq(SU2?(x zyZ-AuRZhxQW*1k>ZL(W?f_@TOo(U_l^wO%<*&|!doKzicO)^*fEaTP4>fPUD$~8XS zt2$tlw;7)w_hX%xKC^m%XYA#WTjqLEl>f5y1EKsr3C-nsX{n~~GcqpLW`m?}=fy2pR=^PIc9gkAlRZ#Sj>cKc@N zGk7PgR@1F&X1()~pYk$Lr1<>YriHVN!N}!3UY^-Dh~&*|aY+_Cz8FuQ3y>Q%l7w|n z6@90wyOlbYxoI0OG@HU^yzj)y&7W%u^jwBrj4UZlpNavV!}R{6@?HOv~3y!o{N z+uE&0nvf~^6l6+wIkt4c)ecD^wtq1S-?K?Fz>nbsE6;@n`{}7qGfU`M=$OQ6B%16j zvqXqT6UsNU6t^z{={8kJwp$J|+4E9Yb*z`L{7l#t<3ZTA5Q5XDvozhz(E5AkK5(51 zmRoUMT&dM?vLTr-t22@rR#R48R1MCuDQ~`)7*=@ca8qFMO5yT~1z)K+rgGWYls#cd zYI*q4tw(Ig&EFc(R#4dE4|q@KkId%ljCB$`d%3(Z9;9va?MJes-~8giCekm}F>=-7 zmYyxIAl~-C@d>lCew1s-Z^(oc_IGo?5RGr*RKonbIpSTV2?dWFw%hM#$$}Wo9 z1{YV#h|RNEk2e#RqgO{d=iW8Vta``jNK|b06|e^M9s@Wzsg+l&g_Wy}wm4{G!s-iD z{!9F3FfI3f9B!f>x%hqc6PALO>+~jEb!un*#kmvQGq>31iuRaZEvxjdyNZL3sjs}d zOyFQ68-<4(djseuHPVpsj*)NeV>`S3mec(x+g6@JZO@+@ zGvD++zECAKtz-hhTdjoSZL$`Z^1P~!n6l?D>U-ZIq@8oRx#{>)?unXwcPEefzMl3F zrjI==lHeO5w+_usbQ$p>aC(Omz>(?F;5E9__c>(uM+Ldkb3geU8?<+>)O)dQZsEx6 zbTX}z6={hL!#;kccvDp3DLak(k};-K6yyDMij$+V-)LTx)~SVPtN)X)FBQ_RP~@b8 z<(7FD7uLQ-&ZRO28?7FJ;~Siwv2XGyKEklYE8ew~y9wFYuID80AwjU`1dBCE{CGjk z=I`jR-qXS%WJd2usBOy?@9Kn7+1}xD@$z#Ln}FlSn(cO}m8zM`>GQtH;_lhb=R9k~ zUpWM8uWefBC~f}Oun8e5mmbl24ax;m*z(&6HoD!;HbYITcZbQZwn_Y(T?eB?yvD~r z5Ns}vNR)kBkesUoKU!=){3#=_)>-qta{+)Txs}#w&GDv&&60NMJT0fYBBa>5eAbe* z%geU=mW*>pH)SZKG7)Kw&?xZPt$a6i2-_U=Rzgkegt3~$iGj+Yx!j#u9NNwbg?&x30Qqc7p>3uK7KvVv+74oBK(Rog$p@t&fBLP%#n-7|^oNW7r*a_bsLjI}&5m6eUFn8OfM|y zdj!4;wn@HDLZy5l5HY5w1GZH+zn(3PFG|C<5UfcvQd`rH+QqIz(q!ezk26&*KU=L?V4we zeeP*Bz@u@5Ym%W`Rk4F{jxJA0uJS*_iX<)380j6~s>88!>!IV1pG-gH9*GmamTOG{ z2*AgMyi)CQHq`p-*5tuTLY!jwvJU|C`_vym|7+_1-;L4a3WCn<9Lx6lbE;(Z0rzvU zS@b8Zp6K%LQp~&hPOL{|zv7MI5pjcpM*bT8M*RxKYO@KUDWuo-mPDwkndx3 z;@p^xA&L~H70uv4B$#a(y=I;Sg&^usPUWWIVjGJ`U^7hTzD4eDKTRGEyrR)?`Dows zUKMb+E&~aesnNB7$z}r4Ck1+Y8=bpr;+*buBDS<8Kimni_wq~rtGUOEA5;h$TaE10 zinVT|HQCGDSKgooGwr*X$|DLhrB_sNUASX76TdD|hQlYB(>tBtD9 zaz$qf^04Yd_gDti8aT7QWO$Sw&A<+4Y3P^(WfR(ZDG_SikcWE^R)c#hK`A(E;9vd< znIkY7)_>8;BTgqU^OVLwOe>4g%QQ;r_ z-;C5sqy3+nG8eQ(cAe{s$Psmd9%&Rq`{vRIjQ3imLi5I7>V`!@^Jp!)6BLOfZZG*; zNt)99OgH(C-tCwFtX7lacKh8afftT7=4}X!YR|-9rkNdQr#T|*jOY;Qa$5H`Y5_22 z5dv@3Ax4le_|8`D?2c&%on}COK69sxqt7Jf9w1W9bWMi?^F;$h5Pq2+j~4omwo^%A z=Ga1TG)UtYhMB@t{#UspaPd(_w3#r=U3i)QwsTh+6;ri2?wh`OtZx?zIO7H2j1s6& zCcF7#L4EOXas z3*linKE>AMa$f65l3OsqlJ75ALWQpJmbFKw$6jG6{fLk3OV!cMLIQb?HSKKGkE5&f zD^xx0BhXX|aNopr*mR0qs0(1t}`}+1Fkqt$qktI%Zog#pHgw@`7F`FVHe`r;}OrZa#i1ktv+#p!pxv zg*+S|t>t3P9B}T56RicHZ#J$%N?)KS!z+0R8*9zXpe7Y2vPTLWA^hsap)_co?-epD zkbe6NKqL^;vcH=~RJ>@XJ>RAgjZ=EYX)Br}sXZMUDQr-QQVzRj(ZznO@8t&s<|e(k zHxgoWSiAG5*oEN^sA&e=Wx3RbzreWiX^#O!eK|?LbHq5nnRuE?g=`nY+7ASSy*B&L zgQeRMtQqR44TWO_Sa~p`m%Hfj>3cv2xb1t?uWo+=nZ@Mb9+icNzcspJM(KFd&~-Q- z^`xO6C>>f=_BGW^6(A(cbSyPwDDrtd50RaD+f?dNiDF#lMY3vp-X*)%$(-OcY&Ku# zv3T$pgg#d=qzXX_&|yTvfcZj(t!k+TU@7Wh4mbZC_6LoB807yKa@Gf8KBf_^If^Kw zN1qsyU7~n0di_<2kW}^vVbxPXbP>XD9^cm_!mDv_ahFW?=s}DWp4sn&nFdMzV#ydm zYTG$74?HXv^-aveT4V}M`R&#h?z&k#tq$~P>DBz$!bs>C$3<)tiiA>32A0>|ZPgD1 zTcb0+b-15Pa*KM~>6Tb`+WkU-^AkOU#`MXEaow`)HmW^pQeAjD7@8M-z0d`yPz9>3Tl>St9j^h4XqvNsRnt91=G`GXw4CYjaKmW=dCGBLbe= ze(l?vObaB819>yWNpYreyKfdWICbH#ulg{(a*csfo~eK$s6a{h46|4=$QyDnuUz3^gY43L7?^@}r{dg`f4Ni9gTEaixm(Qinr zO?A6HG>}(@_~8mU*xgRL*SVl0&0nTij^B|>|3VnJu5GP1aJLKnvO2!w_3gCFyCaH! z`XXaCCt9^l504inF3Ja=EH+ z|D_%dwe65iaSQIGLv#(0Mv_qCGD1XlePQQ;+@Fr6JNr&VYgVTQiM#OO9hG1E^(#f_Tu0~xs>>eC73}RywOsQCt7mS(l$xaFlPNyW@Gom#^)Tg1u zdNWt_4|DxP5}$GiN1X?hTUd z5Y~a?uPufSokFqvLH~xUzL`J&p#Oi${o7M6^I%xEP9~~Ib-$Z+x~z>jh@9`0@n{+P zbkzB%6(cWwx28|@D>H!~@ zL1}I}Lq`4C&bvJxi0{I-d$ahY!+9_gTTxKw)SL~qa47WNLmv>GPRB}}+Llp&+VPYq zvJJC9H+HptPJYi;loE5Q5vLNwDEPUNJj_&B=A9)wZQbp^72!t*`GXK%Lk7tF0u@%?V=`(ldlC+@KB=ku5%#tjNeLjLdryEfsa#20-D>fQmaEh`Tmd z-FTtk10F8z0v5ITw-WOY3@4qqoV^6sB0g9>S!XbIw$=VZ2K5t|~eA!3%j zyEX8>5Rb&{$g)RHJyb_ih$*^ zDn5es2PCJam|k02FFl6lJ(Eo1!_t9|_TMS5(S_p?8Kr5o3wzd?WO-q-J5UVs zg=xx_{>CGa+>R-!a5sYEgBV6;cuxE+wlN=%1#z_y5c_Led7g4_g?0NrZNH!fDTX?> zhs(>+tZ`5t5*Z)!kg7~%g|RkF?+G?~MxX$|)Cd4{6C6%W`rIr{>zjh(y`mpKq;2B{ zVt0GN7rj7dtME~X7hAIqwJ8}I_c$7Adx)Rw5ut{9H6c*>L8d8{q1OP)j7l9oadmR7 z7$&G%1VMjHMIKVjJ%WgACgdvgAb8)0tZSO#cH9@s6s#2Pag+L`7_fC$>vT`)|0WCp zkjW(_GW4rjNx)=SoE>rOw~&YKv~gU&VL5XF4$0>0M_eo z^i)^qg}Cp4oBXVM^g>~HD6@U7D!6q5DDqG zl7Z8km-%YB?T0N2|)mg;M!OmBDW)dNAz(xJ3XZ`ub! zj{yWcJ)bNKRWF^QnTY{fbarxCDemTsX@f@#$d2A%KL=?yjIMi~fz^7YLd|-leHYE6 zy}tf!IyU*s&OJzovbGJg9;96*5SH=;TYCJ^n^z12P{qwa1B!KGao z_dsC0u)}6amPfGEGTn9U;HsBg+XBaPp1x#!opj=P>CPM=T%!dOJ>M9`QK*8%bdR|=Joevz(8x-O$c>eCLsqZ zq*EjcPLA7JuY8_~JE;CmwXh2}#Ub+X6kYRN0PV#e6nk*_mL(HF8a7o1q~Y{(oWV0& zNtn*lE__6A-N4CMS>^|_HO!Rpz|&)57}icRXd6I;lkuFbTj9nxLiXUVm<*&21H7?4 z_4v+16}qvj59fSneK$yH`o7N1ISY$p6ugkI)ghi|X zAo+?}7$6f9s^X^b+An$zVvqlzC9s@W{=qg&G&ue?-cx9jP*UXTu4~UT>UW)s?Ux@= zuStFWNF=k9HQe?_o6`Bi@-xp6?^a(Sb<1_8<=ZgI6@Uvj-baO`3ioJN>(Me5v-f;z4*iqL|w)!^@Hh>+Vf%d0;5+ovIbcs zBlAA=o%0#7K+U9NDxcK4qCl+boden9tQB8V1cpVviy+v_oeUMey9tHSAby9w1?zS| zkaEfzItw$>20^@!qf6-np(p~X8F-o>obxL2=Q%ZIP9JlE;+59Usgn%yf#&G~mCM_3 z@0l0|cFvfacj$zc)O~?n3@R5YXj9_?>r>qnsBIll)eE^A``S0lube_8@ER=By+o0Od|qIU*2#P7ck;sV11N&AYfO=q zj%EYM?MS`|dV)m@!lGFnwY6{Kd=-8YHmG#usWH1u(AqfmKj5yEUZz!)hgIF-BzOg+)3a8exI^dvg^ zr{af;i3}wsLWSvW|C9~5bI0XEaX0wdIfuz&;Js9<7Q;k{vDY*c5X{%G;Tctuqugnd z$G9)gGXZuL!VS@F73kR@E_%)h5gPbtDCnOcypr(jR?vzN%~dYF!Xuh8VfIW6S)4O9 zMi89-G|T5KZ0VLxD5zXu0~HUSnYSH9o)iL%p-L2^k{3;i%-(7>VAB?V4uMEtFC~KJ z<%(Uu(yE{UmAopBqfOu$JwW5j6Ec#3_Tl4vEae2aF)?CJ7xK)+Ecr ziVz@*r}_C=0ax^iRWNS@`!%C|qGbTqPX1G@0X8#-w2s&-O++YID__$h-r?Y{cB>^4 zrrWHXoZ$G5?H{%P&Ae;J8ZSu%30J=YRQL{0vfRNb?UF`VVRTPe_f2kbI|4PKP51 z;lGoD>mCDzNniL2ipjcbrQ!9ujs9BSA}^rBQEd}81^T(8Kv4w`wekU}N1V}yr1ME7 zcR$#Ic)xcTdqu$jdB`GwZVxC+QInu%{jO-JfugZ7t|vN<8pO_C%RA8EtoNIjUMFd_ z%XfaVY>I2U)wow*M!Q63zVR|X)mfm2>JzZc(i@=NWA?!D$2Cc~jt8JT;u4C~dew>< z*8L3*B8np~TcR6Xeb>(GJGHa;yHG0Cp-IPD@ZE(5f9mAR0!wyGVzS@7=NDG?Df!OZ z2C4SVDpRasW;i6Yo~)$pdi^-ucFkZ8j@k~FyoZj-0H&BgT~#jMSy0~!f;!eLm)wI# zt+P1!donzX-=6xg2A(90Lv))MW@o#&4nT%(sGo`EJz(lslSX{M(ej^FY*0Je_wDtI zWu6U~Ed73miF&HpiMTD-?q?XQ4oJm)e36iHYu(0wH}ak!V%nuhBoI{;IBgMZ@N?i^ zg$!wX6L;=G2xfYu@9mm-^aJn%{|dMm#_829n(%{9EO{fwZ(Zr~3TxI}^ZZ2$&q8W3 zHhT>~kqezGo_O^-GqBGag&u49oK$ShNtuk(y3uuB7gQWkoi0nKjd%_!g`2TBdM7Y_ zS_mCjBs){uAL7JQS^C!bcNjlU$N-v$>`gXTY zVc1*e)6Z<=RKKm)2tV#f!dl>8_-A3WANopnqfUwifIX~OG>*u#9RgY0d95QaMvweJ zw$vYF|9^+Kd4 zH%h}**~<{6Ue?U0#B^YjXqT8(r?ToDdxy(`0=EfAR{vt-5xfP_k`b8Mp*2ui3|0mS z=3%qhT`W|&GXnRDtE>$w1*{vc2>9R;@+plQ`eo-Dp6P=KH+ zqct~!sPahM1?`jz7&I=AXDBq?P3U-E%MR0-I~+n}@#*6MTU%0tT+bMdP4084XA){*BCwpDxAbHtzs_o_{RI!T#a%g)4IAn(WF798!I4x zro=tOR8)-BOTMRbdQYO;?M{n}O>d0A1D~9Rh1wbyjG(LwJ%wPfZd2cezIFm}yQ0~X znSXx*2yUxKnjN=!(e%qr)LVO@T0wB7Z;C!cFjR=O%n>Ab>_09RPo1c*DhBW70LbbnVM46X#r`2W5!NJ&pTvs_cj$|3F)}AOCnILse8AA z5)PTP=~xgngG-{|*GZAQ^?8xvU0<5t6B>9wE`h95kbEmI>R%D71yR<7fSQo29mHnK zMN>E&|B^PQG9SDk_e&Hl&K5vxV4iUxV4$WV?3$5!%;-()>wl2HiL3u0pIT$y zZj$)Cp6%Qtc-drmGHdnYEc&BD274z{nJr)(sU{cWbmj{TUMZ%deBU_jEGpF$*DQVs znr8mi7riv6TsmF}B4OPRj%+d5>s~5-Ovbq7mS|t9V2W+|bMrJ*Zt&NW51VhrsGr;5 z)p9zA2^1+Z>wWnF+u43KAT}LpS}hG89d6vo>C-0-(4g?rDcbfX+YfFnmfvL#>(<>@ zQe348Db~Msyl@xrASzDuT^sVfa5szoPG4%@n?jSfm^(Z>Z5x6PS zcjtxlSY991*W3-FreoTgEjG5GZ{lyQR4m_mu+P0+xFHzeqi6h68dg1qX0{18H1NFR zQKa3KeY$}H`+%Xd$Kxj*6>`oHX&+`INi&On;c&^U zS%{bUaMqX4>H zP}pD%B5j0(0b0L8A6Th_4nGu^9hRdEBpol9nA4xXSiQJGJ46T{_hOU)Dpf`!UuoK*b40Zsm&b5Ow)$}$s4(dh1x=Uw z$_W2#Ec(!EndU?x-g4*V=8+7DUd5tJw;0A+kF+wcTOKR!-aM(9b0$Ef+II+c6nPZT z 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 literal 0 HcmV?d00001 From 7a7b7f31724981d6ecffa4b7da996252b1bd98db Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 7 Jan 2023 12:11:26 +0100 Subject: [PATCH 051/297] telegram payment --- README.md | 6 ++- ytdlbot/config.py | 6 ++- ytdlbot/constant.py | 55 +++++++++------------ ytdlbot/limit.py | 28 ++++++++--- ytdlbot/tasks.py | 10 ++-- ytdlbot/ytdl_bot.py | 118 ++++++++++++++++++++++++++++++++------------ 6 files changed, 147 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index e09c06d6..35994e9d 100644 --- a/README.md +++ b/README.md @@ -441,13 +441,15 @@ ping - Bot running status help - Help ytdl - Download video in group vip - Join VIP -terms - View Terms of Service settings - Set your preference direct - Download file directly sub - Subscribe to YouTube Channel unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. -uncache - Delete cache for this link +uncache - Delete cache for this link, owner only. +purge - Delete all tasks , owner only. +topup - Top up quota +tgvip - Using Telegram payment to pay for VIP ``` # Test data diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 7d314398..0fe89e88 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -27,7 +27,7 @@ # TG_MAX_SIZE = 10 * 1024 * 1024 EX = os.getenv("EX", 24 * 3600) -MULTIPLY = os.getenv("MULTIPLY", 5) # VIP1 is 5*5-25G, VIP2 is 50G +MULTIPLY = os.getenv("MULTIPLY", 10) # VIP1 is 5*5-25G, VIP2 is 50G USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 ENABLE_VIP = os.getenv("VIP", False) @@ -58,5 +58,7 @@ IPv6 = os.getenv("IPv6", False) ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) -RATE = float(os.getenv("RATE", 60 * 5)) +# 0.01 means basically no limit +RATE = float(os.getenv("RATE", 0.01)) BURST = int(os.getenv("BURST", 3)) +PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 517084cc..4d8e603f 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -22,62 +22,53 @@ class BotText: start = "Welcome to YouTube Download bot. Type /help for more information." help = f""" -1. This bot should works at all times. If it doesn't, try to send the link again or DM @BennyThink +1. This bot should works at all times. If it doesn't, wait for a few minutes, try to send the link again. -2. At this time of writing, this bot consumes hundreds of GigaBytes of network traffic per day. +2. At this time of writing, this bot consumes more than 100GB of network traffic per day. In order to avoid being abused, every one can use this bot within **{sizeof_fmt(QUOTA)} of quota for every {int(EX / 3600)} hours.** -3. Free users can't receive streaming formats of one video whose duration is longer than 300 seconds. +3. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information. -4. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information. +4. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot -5. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot - -6. Request limit is applied for everyone, excluding VIP users. +5. Request limit is applied for everyone, excluding VIP users. """ if ENABLE_VIP else "Help text" about = "YouTube-DL by @BennyThink. Open source on GitHub: https://github.com/tgbot-collection/ytdlbot" - terms = f""" + vip = f""" +**Terms:** 1. You can use this service, free of charge, {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours. - -2. The above traffic, is counted for one-way. -For example, if you download a video of 1GB, your current quota will be 9GB instead of 8GB. - +2. The above traffic, is counted one-way. For example, if you download a video of 1GB, your will use 1GB instead of 2GB. 3. Streaming support is limited due to high costs of conversion. - 4. I won't gather any personal information, which means I don't know how many and what videos did you download. - -5. Please try not to abuse this service. - -6. It's a open source project, you can always deploy your own bot. - -7. For VIPs, please refer to /vip command - """ if ENABLE_VIP else "Please contact the actual owner of this bot" - - vip = f""" -**Terms:** -1. No refund, I'll keep it running as long as I can. -2. I'll record your unique ID after a successful payment, usually it's payment ID or email address. -3. VIPs identity won't expire. +5. No rate limit for VIP users. +6. Possible to refund, but you'll have to bear with process fee. +7. I'll record your unique ID after a successful payment, usually it's payment ID or email address. +8. VIP identity won't expire. +9. Please try not to abuse this service. It's a open source project, you can always deploy your own bot. **Pay Tier:** 1. Everyone: {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours -2. VIP1: ${MULTIPLY} or ยฅ{MULTIPLY * USD2CNY}, {sizeof_fmt(QUOTA * 5)} per {int(EX / 3600)} hours -3. VIP2: ${MULTIPLY * 2} or ยฅ{MULTIPLY * USD2CNY * 2}, {sizeof_fmt(QUOTA * 5 * 2)} per {int(EX / 3600)} hours +2. VIP1: ${MULTIPLY} or ยฅ{MULTIPLY * USD2CNY}, {sizeof_fmt(QUOTA * 2)} per {int(EX / 3600)} hours +3. VIP2: ${MULTIPLY * 2} or ยฅ{MULTIPLY * USD2CNY * 2}, {sizeof_fmt(QUOTA * 2 * 2)} per {int(EX / 3600)} hours 4. VIP4....VIPn. -5. Unlimited streaming conversion support. -Note: If you pay $9, you'll become VIP1 instead of VIP2. + +**Temporary top up** +Just want more traffic for a short period of time? Don't worry, you can use /topup command to top up your quota. +It's valid permanently, until you use it up. **Payment method:** 1. (afdian) Mainland China: {AFD_LINK} 2. (buy me a coffee) Other countries or regions: {COFFEE_LINK} -__I live in a place where I don't have access to Telegram Payments. So...__ +3. Telegram Payment(stripe), please directly using /tgvip command. **After payment:** + 1. afdian: with your order number `/vip 123456` 2. buy me a coffee: with your email `/vip someone@else.com` +3. Telegram Payment: automatically activated """ if ENABLE_VIP else "VIP is not enabled." vip_pay = "Processing your payments...If it's not responding after one minute, please contact @BennyThink." @@ -95,6 +86,8 @@ class BotText: Sending format: **{1}** """ custom_text = os.getenv("CUSTOM_TEXT", "") + topup_description = f"US$1 will give you {sizeof_fmt(QUOTA)} traffic permanently" + topup_title = "Pay US$1 for more traffic!" def remaining_quota_caption(self, chat_id): if not ENABLE_VIP: diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 5d01a7b2..ed37ef9e 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -34,26 +34,42 @@ def check_vip(self, user_id: "int") -> "tuple": data = self.cur.fetchone() return data - def add_vip(self, user_data: "dict") -> ("bool", "str"): + def __add_vip(self, user_data: "dict"): sql = "INSERT INTO vip VALUES (%s,%s,%s,%s,%s,%s);" + self.cur.execute(sql, list(user_data.values())) + self.con.commit() + # also remove redis cache + self.r.delete(user_data["user_id"]) + + def add_vip(self, user_data: "dict") -> "str": # first select self.cur.execute("SELECT * FROM vip WHERE payment_id=%s", (user_data["payment_id"],)) is_exist = self.cur.fetchone() if is_exist: return "Failed. {} is being used by user {}".format(user_data["payment_id"], is_exist[0]) - self.cur.execute(sql, list(user_data.values())) - self.con.commit() - # also remove redis cache - self.r.delete(user_data["user_id"]) + self.__add_vip(user_data) return "Success! You are VIP{} now!".format(user_data["level"]) + def direct_add_vip(self, user_data: "dict") -> ("bool", "str"): + self.__add_vip(user_data) + return "Success payment from Telegram! You are VIP{} now!".format(user_data["level"]) + def remove_vip(self, user_id: "int"): raise NotImplementedError() def get_user_quota(self, user_id: "int") -> int: # even VIP have certain quota q = self.check_vip(user_id) - return q[-1] if q else QUOTA + topup = self.r.hget("topup", user_id) + if q: + return q[-1] + elif topup: + return int(topup) + QUOTA + else: + return QUOTA + + def set_topup(self, user_id: "int"): + self.r.hset("topup", user_id, QUOTA) def check_remaining_quota(self, user_id: "int"): user_quota = self.get_user_quota(user_id) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 73801051..3c7974c4 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -32,12 +32,12 @@ from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor from client_init import create_app -from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY, - ENABLE_QUEUE, ENABLE_VIP, TG_MAX_SIZE, WORKERS) +from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE, + ENABLE_VIP, TG_MAX_SIZE, WORKERS) from constant import BotText from db import Redis -from downloader import (edit_text, sizeof_fmt, tqdm_progress, - upload_hook, ytdl_download) +from downloader import (edit_text, sizeof_fmt, tqdm_progress, upload_hook, + ytdl_download) from limit import VIP from utils import (apply_log_formatter, auto_restart, customize_logger, get_metadata, get_revision, get_user_settings) @@ -433,10 +433,12 @@ def run_celery(): argv.extend(["-Q", worker_name]) app.worker_main(argv) + def purge_tasks(): count = app.control.purge() return f"purged {count} tasks." + if __name__ == '__main__': celery_client.start() print("Bootstrapping Celery worker now.....") diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 32a9132f..066f5ef8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -20,21 +20,24 @@ from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant +from pyrogram.raw import functions +from pyrogram.raw import types as raw_types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime from token_bucket import Limiter, MemoryStorage from client_init import create_app from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG, - ENABLE_VIP, OWNER, RATE, REQUIRED_MEMBERSHIP) + ENABLE_VIP, MULTIPLY, OWNER, PROVIDER_TOKEN, QUOTA, RATE, + REQUIRED_MEMBERSHIP) from constant import BotText from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment from tasks import app as celery_app -from tasks import (audio_entrance, direct_download_entrance, hot_patch, purge_tasks, - ytdl_download_entrance) -from utils import (auto_restart, customize_logger, get_revision, clean_tempfile, - get_user_settings, set_user_settings) +from tasks import (audio_entrance, direct_download_entrance, hot_patch, + purge_tasks, ytdl_download_entrance) +from utils import (auto_restart, clean_tempfile, customize_logger, + get_revision, get_user_settings, set_user_settings) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) logging.getLogger('apscheduler.executors.default').propagate = False @@ -187,13 +190,6 @@ def about_handler(client: "Client", message: "types.Message"): client.send_message(chat_id, bot_text.about) -@app.on_message(filters.command(["terms"])) -def terms_handler(client: "Client", message: "types.Message"): - chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") - client.send_message(chat_id, bot_text.terms) - - @app.on_message(filters.command(["sub_count"])) def sub_count_handler(client: "Client", message: "types.Message"): username = message.from_user.username @@ -268,6 +264,53 @@ def vip_handler(client: "Client", message: "types.Message"): bm.edit_text(msg) +def generate_invoice(amount: "int", title: "str", description: "str", payload: "bytes"): + invoice = raw_types.input_media_invoice.InputMediaInvoice( + invoice=( + raw_types.invoice.Invoice(currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)])), + title=title, + description=description, + provider=PROVIDER_TOKEN, + provider_data=raw_types.DataJSON(data="{}"), + payload=payload, + ) + return invoice + + +# payment related +@app.on_message(filters.command(["topup"])) +def topup_handler(client: "Client", message: "types.Message"): + chat_id = message.chat.id + client.send_chat_action(chat_id, "typing") + invoice = generate_invoice(100, bot_text.topup_title, bot_text.topup_description, + f"{message.chat.id}-topup".encode()) + + app.send( + functions.messages.SendMedia( + peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), + media=invoice, + random_id=app.rnd_id(), + message="Please use your card to pay for more traffic" + ) + ) + + +@app.on_message(filters.command(["tgvip"])) +def tgvip_handler(client: "Client", message: "types.Message"): + chat_id = message.chat.id + client.send_chat_action(chat_id, "typing") + invoice = generate_invoice(1000, f"VIP1", f"pay USD${MULTIPLY} for VIP1", f"{message.chat.id}-vip1".encode()) + + app.send( + functions.messages.SendMedia( + peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), + media=invoice, + random_id=app.rnd_id(), + message="Please use your card to pay for more traffic" + ) + ) + + @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: "Client", message: "types.Message"): @@ -284,25 +327,6 @@ def download_handler(client: "Client", message: "types.Message"): red.update_metrics("bad_request") message.reply_text("I think you should send me a link.", quote=True) return - # TODO - # red.update_metrics("search_request") - # result = VideosSearch(url, limit=5).result().get("result", []) - # text = "" - # count = 1 - # buttons = [] - # for item in result: - # text += f"{count}. {item['title']} - {item['link']}\n\n" - # buttons.append( - # InlineKeyboardButton( - # f"{count}", - # callback_data=f"search_{item['id']}" - # ) - # ) - # count += 1 - # - # markup = InlineKeyboardMarkup([buttons]) - # client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup) - # return if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url: message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True) @@ -393,6 +417,38 @@ def periodic_sub_check(): time.sleep(random.random() * 3) +@app.on_raw_update() +def raw_update(client: "Client", update, users, chats): + action = getattr(getattr(update, "message", None), "action", None) + if update.QUALNAME == 'types.UpdateBotPrecheckoutQuery': + client.send( + functions.messages.SetBotPrecheckoutResults( + query_id=update.query_id, + success=True, + ) + ) + elif action and action.QUALNAME == 'types.MessageActionPaymentSentMe': + logging.info("Payment received. %s", action) + uid = update.message.peer_id.user_id + vip = VIP() + amount = f"{action.total_amount / 100} {action.currency}" + if "vip" in action.payload.decode(): + ud = { + "user_id": uid, + "username": users.get(uid).username, + "payment_amount": 10, + "payment_id": 0, + "level": 1, + "quota": QUOTA * 2 + } + vip.add_vip(ud) + client.send_message(uid, f"Thank you {uid}. VIP payment received: {amount}") + + else: + vip.set_topup(uid) + client.send_message(uid, f"Thank you {uid}. Top up payment received: {amount}") + + if __name__ == '__main__': MySQL() scheduler = BackgroundScheduler(timezone="Europe/Stockholm", job_defaults={'max_instances': 5}) From c8c0482791618068d1283267515ca98566b5b042 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 7 Jan 2023 13:01:26 +0100 Subject: [PATCH 052/297] fix tgvip --- ytdlbot/ytdl_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 066f5ef8..f715b4b8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -441,7 +441,7 @@ def raw_update(client: "Client", update, users, chats): "level": 1, "quota": QUOTA * 2 } - vip.add_vip(ud) + vip.direct_add_vip(ud) client.send_message(uid, f"Thank you {uid}. VIP payment received: {amount}") else: From d874416005248bf6c2d4dd0a4ac83ad64c05104c Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 7 Jan 2023 14:19:20 +0100 Subject: [PATCH 053/297] show receipt --- ytdlbot/ytdl_bot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index f715b4b8..71cb593c 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -264,7 +264,7 @@ def vip_handler(client: "Client", message: "types.Message"): bm.edit_text(msg) -def generate_invoice(amount: "int", title: "str", description: "str", payload: "bytes"): +def generate_invoice(amount: "int", title: "str", description: "str", payload: "str"): invoice = raw_types.input_media_invoice.InputMediaInvoice( invoice=( raw_types.invoice.Invoice(currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)])), @@ -272,7 +272,8 @@ def generate_invoice(amount: "int", title: "str", description: "str", payload: " description=description, provider=PROVIDER_TOKEN, provider_data=raw_types.DataJSON(data="{}"), - payload=payload, + payload=payload.encode(), + start_param=payload ) return invoice @@ -283,7 +284,7 @@ def topup_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") invoice = generate_invoice(100, bot_text.topup_title, bot_text.topup_description, - f"{message.chat.id}-topup".encode()) + f"{message.chat.id}-topup") app.send( functions.messages.SendMedia( From d9eb0490c5862d3f5590efb0d549e1e318d4233b Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 20 Jan 2023 20:01:22 +0100 Subject: [PATCH 054/297] instagram cookie fix #189 --- ytdlbot/downloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 693ff0fc..a829d8c5 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -301,7 +301,7 @@ def convert_audio_format(resp: "dict", bm): def add_instagram_cookies(url: "str", opt: "dict"): if url.startswith("https://www.instagram.com"): - opt["cookiefi22"] = pathlib.Path(__file__).parent.joinpath("instagram.com_cookies.txt").as_posix() + opt["cookiefile"] = pathlib.Path(__file__).parent.joinpath("instagram.com_cookies.txt").as_posix() def run_splitter(video_path: "str"): From d0e32bba605ab90936393ae49e50f5a62c8508fb Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 20 Jan 2023 20:22:45 +0100 Subject: [PATCH 055/297] REQUIRED_MEMBERSHIP fix banned user is still in the list fix #187 --- ytdlbot/ytdl_bot.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 71cb593c..0adcd87c 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -75,8 +75,10 @@ def wrapper(client: "Client", message: "types.Message"): # membership check if REQUIRED_MEMBERSHIP: try: - app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id) - logging.info("user %s check passed for group/channel %s.", chat_id, REQUIRED_MEMBERSHIP) + if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status != "member": + raise UserNotParticipant() + else: + logging.info("user %s check passed for group/channel %s.", chat_id, REQUIRED_MEMBERSHIP) except UserNotParticipant: logging.warning("user %s is not a member of group/channel %s", chat_id, REQUIRED_MEMBERSHIP) message.reply_text(bot_text.membership_require, quote=True) From 48b8b605c58f0d864158c18456e118b48f4bd1d9 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 20 Jan 2023 20:41:34 +0100 Subject: [PATCH 056/297] fix sub edge case #146 --- ytdlbot/limit.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index ed37ef9e..3e9f04c0 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -150,7 +150,10 @@ def extract_canonical_link(url): def get_channel_info(self, url: "str"): api_key = os.getenv("GOOGLE_API_KEY") canonical_link = self.extract_canonical_link(url) - channel_id = canonical_link.split("https://www.youtube.com/channel/")[1] + try: + channel_id = canonical_link.split("https://www.youtube.com/channel/")[1] + except IndexError: + channel_id = canonical_link.split("https://youtube.com/channel/")[1] channel_api = f"https://www.googleapis.com/youtube/v3/channels?part=snippet,contentDetails&" \ f"id={channel_id}&key={api_key}" data = requests.get(channel_api).json() From ecf39ec973832a8b1760bd0272c3cdad4e41d345 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 20 Jan 2023 20:43:12 +0100 Subject: [PATCH 057/297] upgrade dep --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f2be5122..6eba2a7d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.1.2 +yt-dlp==2023.1.6 APScheduler==3.9.1.post1 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -15,7 +15,7 @@ fakeredis==2.4.0 supervisor==4.2.5 tgbot-ping==1.0.4 redis==4.3.3 -requests==2.28.1 +requests==2.28.2 tqdm==4.64.1 requests-toolbelt==0.10.1 ffpb==0.4.1 From 9d932e8523f8f63b25c1d30048b14dd4b6f5d79d Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 22 Jan 2023 10:19:34 +0100 Subject: [PATCH 058/297] EX conversion to int fix #190 --- ytdlbot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 0fe89e88..027730dc 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -26,7 +26,7 @@ TG_MAX_SIZE = 2 * 1024 * 1024 * 1024 * 0.99 # TG_MAX_SIZE = 10 * 1024 * 1024 -EX = os.getenv("EX", 24 * 3600) +EX = int(os.getenv("EX", 24 * 3600)) MULTIPLY = os.getenv("MULTIPLY", 10) # VIP1 is 5*5-25G, VIP2 is 50G USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 From 880a86805d99d942b54f30ef17a50fe5d19be0f6 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 28 Jan 2023 14:59:12 +0100 Subject: [PATCH 059/297] upgrade tgbot_ping --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6eba2a7d..62d6fd5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,9 +11,9 @@ flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 beautifulsoup4==4.11.1 -fakeredis==2.4.0 +fakeredis==2.6.0 supervisor==4.2.5 -tgbot-ping==1.0.4 +tgbot-ping==1.0.5 redis==4.3.3 requests==2.28.2 tqdm==4.64.1 From dfbd69d8fcdf32972fb1c2fb535c9cebcaf6bdec Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 28 Jan 2023 15:42:59 +0100 Subject: [PATCH 060/297] fix timezone issue in ping --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 62d6fd5e..ea388858 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ influxdb==5.3.1 beautifulsoup4==4.11.1 fakeredis==2.6.0 supervisor==4.2.5 -tgbot-ping==1.0.5 +tgbot-ping==1.0.6 redis==4.3.3 requests==2.28.2 tqdm==4.64.1 From 2d582891af7926f3f0fb51ad73440b0db410b5a4 Mon Sep 17 00:00:00 2001 From: Bartixxx32 Date: Sun, 5 Feb 2023 14:48:41 +0100 Subject: [PATCH 061/297] Env toggle to enable / disable Playlist download (#200) * Env toggle to enable / disable Playlist download * i think this else is obsolete --- README.md | 1 + ytdlbot/config.py | 1 + ytdlbot/ytdl_bot.py | 11 ++++++----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 35994e9d..84b448cc 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ you can configure all the following environment variables: * AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. * ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. +* PLAYLIST_SUPPORT: `True` or `False`, Ability to enable or disable downloads of playlist / channels by bot. Default: `False`. **Inline button will be lost during the forwarding.** ## 3.2 Set up init data diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 027730dc..f82322d8 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -62,3 +62,4 @@ RATE = float(os.getenv("RATE", 0.01)) BURST = int(os.getenv("BURST", 3)) PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" +PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 0adcd87c..f1298a97 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -29,7 +29,7 @@ from client_init import create_app from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, MULTIPLY, OWNER, PROVIDER_TOKEN, QUOTA, RATE, - REQUIRED_MEMBERSHIP) + REQUIRED_MEMBERSHIP, PLAYLIST_SUPPORT) from constant import BotText from db import InfluxDB, MySQL, Redis from limit import VIP, verify_payment @@ -331,10 +331,11 @@ def download_handler(client: "Client", message: "types.Message"): message.reply_text("I think you should send me a link.", quote=True) return - if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url: - message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True) - red.update_metrics("reject_channel") - return + if not PLAYLIST_SUPPORT: + if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url: + message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True) + red.update_metrics("reject_channel") + return # non vip user, consume too many token if (not VIP().check_vip(chat_id)) and (not lim.consume(str(chat_id).encode(), 1)): red.update_metrics("rate_limit") From 1f72259974f63c7a4de36ef9cb41a0ef8b90f52a Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 14 Feb 2023 02:00:39 +0530 Subject: [PATCH 062/297] Changed video quality choices (#205) * Changed video quality choices --- ytdlbot/constant.py | 2 +- ytdlbot/utils.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 4d8e603f..92d0a021 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -77,7 +77,7 @@ class BotText: settings = """ Select sending format and video quality. **Only applies to YouTube** -High quality is recommended; Medium quality is aimed as 480P while low quality is aimed as 360P and 240P. +High quality is recommended; Medium quality is aimed as 720P while low quality is aimed as 480P. Remember if you choose to send as document, there will be no streaming. diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 84ff863e..eea2fc67 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -76,14 +76,14 @@ def is_youtube(url: "str"): def adjust_formats(user_id: "str", url: "str", formats: "list", hijack=None): - # high: best quality, 720P, 1080P, 2K, 4K, 8K - # medium: 480P - # low: 360P+240P + # high: best quality 1080P, 2K, 4K, 8K + # medium: 720P + # low: 480P if hijack: formats.insert(0, hijack) return - mapping = {"high": [], "medium": [480], "low": [240, 360]} + mapping = {"high": [], "medium": [720], "low": [480]} settings = get_user_settings(user_id) if settings and is_youtube(url): for m in mapping.get(settings[1], []): From 9d24c34297a4a4c607939514ba9ff28c6d45c1c2 Mon Sep 17 00:00:00 2001 From: Jiawen Geng Date: Fri, 17 Feb 2023 22:00:39 +0800 Subject: [PATCH 063/297] fix: youtube-dl download --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ea388858..b0a3a609 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.1.6 +yt-dlp==2023.2.17 APScheduler==3.9.1.post1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From f7ea35bbee1d204e5794da96b0f2a7454b09baae Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 2 Mar 2023 21:54:28 +0100 Subject: [PATCH 064/297] use yt-dlp daily build for now https://github.com/tgbot-collection/ytdlbot/pull/211 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b0a3a609..e0519954 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.2.17 +git+https://github.com/yt-dlp/yt-dlp APScheduler==3.9.1.post1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 0f3dd86cd40bf749b16a86bbdcad321057b04f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elberte=20Pl=C3=ADnio?= Date: Sat, 4 Mar 2023 15:08:02 -0300 Subject: [PATCH 065/297] feat: add aria2 (#211) --- README.md | 7 ++++--- apk.txt | 2 +- ytdlbot/downloader.py | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 84b448cc..3469ac5d 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,10 @@ Use the button above! It should work like a magic but with limited functionaliti 1. clone code 2. install ffmpeg 3. install Python 3.6+ -4. pip3 install -r requirements.txt -5. set environment variables `TOKEN`, `APP_ID` and `APP_HASH`, and more if you like. -6. `python3 ytdl_bot.py` +4. install aria2 and add to PATH +5. pip3 install -r requirements.txt +6. set environment variables `TOKEN`, `APP_ID` and `APP_HASH`, and more if you like. +7. `python3 ytdl_bot.py` ## Docker diff --git a/apk.txt b/apk.txt index fd936fe0..76b90295 100644 --- a/apk.txt +++ b/apk.txt @@ -1 +1 @@ -ffmpeg vnstat git \ No newline at end of file +ffmpeg vnstat git aria2 \ No newline at end of file diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index a829d8c5..0d0a668e 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -190,6 +190,8 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: response = {"status": True, "error": "", "filepath": []} output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { + 'external_downloader': 'aria2c', + 'external_downloader_args': ['--min-split-size=1M', '--max-connection-per-server=16', '--max-concurrent-downloads=16', '--split=16'], 'progress_hooks': [lambda d: download_hook(d, bm)], 'outtmpl': output, 'restrictfilenames': False, From 9a9de982a75c93a3102286814d1fea6bb64ca797 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 4 Mar 2023 19:11:09 +0100 Subject: [PATCH 066/297] make aria2 optional --- requirements.txt | 10 +++++----- ytdlbot/config.py | 1 + ytdlbot/downloader.py | 11 ++++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index e0519954..480c5458 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -git+https://github.com/yt-dlp/yt-dlp -APScheduler==3.9.1.post1 +yt-dlp==2023.3.3 +APScheduler==3.10.0 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 @@ -10,10 +10,10 @@ filetype==1.2.0 flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 -beautifulsoup4==4.11.1 -fakeredis==2.6.0 +beautifulsoup4==4.11.2 +fakeredis==2.9.2 supervisor==4.2.5 -tgbot-ping==1.0.6 +tgbot-ping==1.0.7 redis==4.3.3 requests==2.28.2 tqdm==4.64.1 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index f82322d8..9183b117 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -63,3 +63,4 @@ BURST = int(os.getenv("BURST", 3)) PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) +ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 0d0a668e..9b33c1fe 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -24,7 +24,7 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import (AUDIO_FORMAT, ENABLE_FFMPEG, ENABLE_VIP, MAX_DURATION, +from config import (AUDIO_FORMAT, ENABLE_FFMPEG, ENABLE_VIP, MAX_DURATION, ENABLE_ARIA2, TG_MAX_SIZE, IPv6) from db import Redis from limit import VIP @@ -190,14 +190,19 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: response = {"status": True, "error": "", "filepath": []} output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { - 'external_downloader': 'aria2c', - 'external_downloader_args': ['--min-split-size=1M', '--max-connection-per-server=16', '--max-concurrent-downloads=16', '--split=16'], 'progress_hooks': [lambda d: download_hook(d, bm)], 'outtmpl': output, 'restrictfilenames': False, 'quiet': True, "proxy": os.getenv("YTDL_PROXY") } + if ENABLE_ARIA2: + ydl_opts["external_downloader"] = "aria2c" + ydl_opts["external_downloader_args"] = ['--min-split-size=1M', + '--max-connection-per-server=16', + '--max-concurrent-downloads=16', + '--split=16' + ] formats = [ "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", From fe4f859aa1d3275e2c2f810eb5de0d86e2667728 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 19 Mar 2023 15:52:51 +0100 Subject: [PATCH 067/297] Revert "create celery app, not in-memory" This reverts commit 67ad5a6bd9fe17f717b3bf2ddfc9e61f566f4a11. --- .gitignore | 5 ++--- ytdlbot/client_init.py | 2 +- ytdlbot/session/readme.txt | 1 - ytdlbot/tasks.py | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 ytdlbot/session/readme.txt diff --git a/.gitignore b/.gitignore index 4c68f546..ea7f521a 100644 --- a/.gitignore +++ b/.gitignore @@ -155,9 +155,8 @@ db_data/* env/* .ash_history .DS_Store -ytdlbot/session/ytdl.session -ytdlbot/session/celery.session +ytdlbot/ytdl.session data/* upgrade_worker.sh ytdl.session -reinforcement/* +reinforcement/* \ No newline at end of file diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index fe7fa53c..5f9f9019 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -14,7 +14,7 @@ from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 -def create_app(session="session/ytdl", workers=PYRO_WORKERS): +def create_app(session="ytdl", workers=PYRO_WORKERS): _app = Client(session, APP_ID, APP_HASH, bot_token=TOKEN, workers=workers, ipv6=IPv6, diff --git a/ytdlbot/session/readme.txt b/ytdlbot/session/readme.txt deleted file mode 100644 index b345f25d..00000000 --- a/ytdlbot/session/readme.txt +++ /dev/null @@ -1 +0,0 @@ -dir for session files. diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 3c7974c4..82448a22 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -51,7 +51,7 @@ # app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') app = Celery('tasks', broker=BROKER) -celery_client = create_app("session/celery") +celery_client = create_app(":memory:") def get_messages(chat_id, message_id): From 218b1d5ec969e90aee30680659f05bd0fb5a901f Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 6 Apr 2023 22:33:24 +0200 Subject: [PATCH 068/297] token mode instead of vip mode --- .gitignore | 8 +- FAQ.md | 8 +- README.md | 303 +++++------------------------ k8s.md | 200 +++++++++++++++++++ requirements.txt | 11 +- scripts/migrate_to_mysql.py | 1 + scripts/start.sh | 4 - ytdlbot/broadcast.py | 62 ------ ytdlbot/channel.py | 177 +++++++++++++++++ ytdlbot/config.py | 29 ++- ytdlbot/constant.py | 126 +++++------- ytdlbot/{db.py => database.py} | 132 ++++++++----- ytdlbot/downloader.py | 149 ++++---------- ytdlbot/fakemysql.py | 58 ------ ytdlbot/limit.py | 343 ++++++++------------------------- ytdlbot/migration.sql | 3 - ytdlbot/tasks.py | 231 +++++++++++----------- ytdlbot/utils.py | 55 ++---- ytdlbot/ytdl_bot.py | 302 ++++++++++++++--------------- 19 files changed, 1001 insertions(+), 1201 deletions(-) create mode 100644 k8s.md delete mode 100644 ytdlbot/broadcast.py create mode 100644 ytdlbot/channel.py rename ytdlbot/{db.py => database.py} (71%) delete mode 100644 ytdlbot/fakemysql.py delete mode 100644 ytdlbot/migration.sql diff --git a/.gitignore b/.gitignore index ea7f521a..11572941 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,10 @@ ytdlbot/ytdl.session data/* upgrade_worker.sh ytdl.session -reinforcement/* \ No newline at end of file +reinforcement/* +/ytdlbot/session/celery.session +/.idea/prettier.xml +/.idea/watcherTasks.xml +/ytdlbot/session/ytdl.session-journal +/ytdlbot/unknown_errors.txt +/ytdlbot/ytdl.session-journal diff --git a/FAQ.md b/FAQ.md index 0039e28b..4a0f7b54 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,10 +1,11 @@ # Project status -This project is in zombie mode now. +This project is currently inactive and is no longer being maintained. -It is not maintained anymore. No features will be added, no bugs will be fixed. +No new features will be added, and no bugs will be fixed. -Existing code and bot will continue to work without any guarantees. +While the existing code and bot will continue to work, there are no guarantees regarding their reliability or +functionality. # Can you support downloading from XXX? @@ -14,7 +15,6 @@ Please reach out to [yt-dlp](https://github.com/yt-dlp/yt-dlp) Just wait. It is a free service, and it is not guaranteed to work. -See [Grafana Dashboard](https://grafana.dmesg.app/d/9yXGmc1nk/youtube-download-celery?orgId=2) for current server status # Why is the bot not responding? diff --git a/README.md b/README.md index 3469ac5d..7c04526f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ YouTube Download Bot๐Ÿš€ -Download videos from YouTube and other platforms through a Telegram Bot +This Telegram bot allows you to download videos from YouTube and other supported platforms. ----- **READ [FAQ](FAQ.md) FIRST IF YOU ENCOUNTER ANY ISSUES.** @@ -12,7 +12,7 @@ Download videos from YouTube and other platforms through a Telegram Bot ----- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) -Can't deploy? Fork to your personal account and deploy it there! +If you are having trouble deploying, you can fork the project to your personal account and deploy it from there. **Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no longer be available.** @@ -27,14 +27,13 @@ Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/support # Limitations of my bot -I don't have unlimited servers and bandwidth, so I have to make some restrictions. +Due to limitations on servers and bandwidth, there are some restrictions on this service. -* 5 GiB one-way traffic per 24 hours for each user -* maximum 5 minutes streaming conversion support -* maximum 3 subscriptions -* limited request in certain time range +* Each user is limited to 5 free downloads per 24-hour period +* there is a maximum of three subscriptions allowed for YouTube channels. -You can choose to become 'VIP' if you really need large traffic. And also, you could always deploy your own bot. +If you require more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your +own bot. # Features @@ -45,7 +44,7 @@ You can choose to become 'VIP' if you really need large traffic. And also, you c 3. support progress bar 4. audio conversion 5. playlist support -6. VIP support +6. payment support 7. support different video resolutions 8. support sending as file or streaming as video 9. supports celery worker distribution - faster than before. @@ -56,25 +55,23 @@ You can choose to become 'VIP' if you really need large traffic. And also, you c # How to deploy? -You can deploy this bot on any platform that supports Python. - -## Heroku - -Use the button above! It should work like a magic but with limited functionalities. +This bot can be deployed on any platform that supports Python. ## Run natively on your machine -1. clone code -2. install ffmpeg -3. install Python 3.6+ -4. install aria2 and add to PATH -5. pip3 install -r requirements.txt -6. set environment variables `TOKEN`, `APP_ID` and `APP_HASH`, and more if you like. -7. `python3 ytdl_bot.py` +To deploy this bot, follow these steps: + +1. Clone the code from the repository. +2. Install FFmpeg. +3. Install Python 3.6 or a later version. +4. Install Aria2 and add it to the PATH. +5. Install the required packages by running `pip3 install -r requirements.txt`. +6. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. +7. Run `python3 ytdl_bot.py`. ## Docker -Some functions, such as VIP, ping will be disabled. +This bot has a simple one-line code and some functions, such as VIP and ping, are disabled. ```shell docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot @@ -104,42 +101,35 @@ mkdir env vim env/ytdl.env ``` -you can configure all the following environment variables: +You can configure all the following environment variables: -* PYRO_WORKERS: number of workers for pyrogram, default is 100 * WORKERS: workers count for celery +* PYRO_WORKERS: number of workers for pyrogram, default is 100 * APP_ID: **REQUIRED**, get it from https://core.telegram.org/ * APP_HASH: **REQUIRED** * TOKEN: **REQUIRED** * REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ - +* EXPIRE: token expire time, default: 1 day +* ENABLE_VIP: enable VIP mode * OWNER: owner username -* QUOTA: quota in bytes -* EX: quota expire time -* MULTIPLY: vip quota comparing to normal quota -* USD2CNY: exchange rate -* VIP: VIP mode, default: disable -* AFD_LINK -* COFFEE_LINK -* COFFEE_TOKEN -* AFD_TOKEN -* AFD_USER_ID - -* AUTHORIZED_USER: users that could use this bot, user_id, separated with `,` -* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot. Could be use with - above `AUTHORIZED_USER` - -* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. -* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream -* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode -* MYSQL_USER -* MYSQL_PASS +* AUTHORIZED_USER: only authorized users can use the bot +* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot +* ENABLE_CELERY: celery mode, default: disable +* ENABLE_QUEUE: celery queue +* BROKER: celery broker, should be redis://redis:6379/0 +* MYSQL_HOST:MySQL host +* MYSQL_USER: MySQL username +* MYSQL_PASS: MySQL password +* AUDIO_FORMAT: default audio format +* ARCHIVE_ID: forward all downloads to this group/channel +* IPv6 = os.getenv("IPv6", False) +* ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) +* PROVIDER_TOKEN: stripe token on Telegram payment +* PLAYLIST_SUPPORT: download playlist support +* ENABLE_ARIA2: enable aria2c download +* FREE_DOWNLOAD: free download count per day +* TOKEN_PRICE: token price per 1 USD * GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. -* AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For - example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. -* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. -* PLAYLIST_SUPPORT: `True` or `False`, Ability to enable or disable downloads of playlist / channels by bot. Default: `False`. - **Inline button will be lost during the forwarding.** ## 3.2 Set up init data @@ -187,7 +177,7 @@ vim data/instagram.com_cookies.txt In `flower` service section, you may want to change your basic authentication username password and publish port. -You can also limit CPU and RAM usage by adding an `deploy' key: +You can also limit CPU and RAM usage by adding a `deploy' key: ```docker deploy: @@ -231,208 +221,12 @@ On the other machine: docker-compose -f worker.yml up -d ``` -**โš ๏ธ Bear in mind don't publish redis directly on the internet! You can use WireGuard to wrap it up.** - -## Kubernetes - -Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of -containerized applications - -# Complete deployment guide for k8s deloyment - -* contains every functionality -* compatible with amd64, arm64 and armv7l - -## First. Get all file in k8s folder - -Download `k8s` file to a directory on your k8s server and go to this folder - -## 1. Create Redis deloyment - -```shell -kubectl apply -f 01.redis.yml -``` - -This command will create ytdl namespace, redis pod and redis service - -## 2. Creat MariaDB deloyment - -```shell -kubectl apply -f 02.mariadb.yml -``` - -This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your -storageClassName before apply. - -## 3. Set environment variables - -Create configMap for env - -### 3.1 Edit configmap.yml - -```shell -vim 03.configmap.yml -``` - -you can configure all the following environment variables: - -* PYRO_WORKERS: number of workers for pyrogram, default is 100 -* WORKERS: workers count for celery -* APP_ID: **REQUIRED**, get it from https://core.telegram.org/ -* APP_HASH: **REQUIRED** -* TOKEN: **REQUIRED** -* REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ - -* OWNER: owner username -* QUOTA: quota in bytes -* EX: quota expire time -* MULTIPLY: vip quota comparing to normal quota -* USD2CNY: exchange rate -* VIP: VIP mode, default: disable -* AFD_LINK -* COFFEE_LINK -* COFFEE_TOKEN -* AFD_TOKEN -* AFD_USER_ID - -* AUTHORIZED_USER: users that could use this bot, user_id, separated with `,` -* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot. Could be use with - above `AUTHORIZED_USER` - -* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. -* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream -* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode -* MYSQL_USER -* MYSQL_PASS -* GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. -* AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For - example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. -* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. - **Inline button will be lost during the forwarding.** - -### 3.2 Apply configMap for environment variables - -```shell -kubectl apply -f 03.configmap.yml -``` - -## 4. Run Master Celery - -```shell -kubectl apply -f 04.ytdl-master.yml -``` - -This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, -cookies folder and flower database. Please replace longhorn with your storageClassName before apply - -### 4.1 Setup instagram cookies - -Required if you want to support instagram. - -You can use this extension -[Get cookies.txt](https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid) -to get instagram cookies - -Get pod running ytdl master: - -```shell -kubectl get pods --namespace ytdl -``` - -Name should be ytdl-xxxxxxxx - -Access to pod - -```shell -kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh -``` - -(replace ytdl-xxx by your pod name) - -Go to ytdl-pvc mounted folder - -```shell -cd /ytdlbot/ytdlbot/data/ -vim instagram.com_cookies.txt -# paste your cookies -``` +**โš ๏ธ Please bear in mind that you should not publish Redis directly on the internet. +Instead, you can use WireGuard to wrap it up for added security.** -## 5. Run Worker Celery - -```shell -kubectl apply -f 05.ytdl-worker.yml -``` - -## 6. Run Flower image (OPTIONAL) - -### 6.1 Setup flower db - -Get pod running ytdl master: - -```shell -kubectl get pods --namespace ytdl -``` - -Name should be ytdl-xxxxxxxx - -Access to pod - -```shell -kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh -``` +## kubernetes -(replace ytdl-xxx by your pod name) - -Go to ytdl-pvc mounted folder - -```shel -cd /var/lib/vnstat/ -``` - -Create flower database file - -```shell -{} ~ python3 -Python 3.9.9 (main, Nov 21 2021, 03:22:47) -[Clang 12.0.0 (clang-1200.0.32.29)] on darwin -Type "help", "copyright", "credits" or "license" for more information. ->>> import dbm;dbm.open("flower","n");exit() -``` - -### 6.2 Config Flower Ingress - -This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from -internet. -YML file should be adjusted depending on your load balancing, ingress and network system - -For active SSL - -```yml -cert-manager.io/cluster-issuer: letsencrypt-prod -``` - -Replace nginx by your ingress service - -```yml -ingressClassName: nginx -``` - -Add your domain, example - -```yml -tls: - - hosts: - - flower.benny.com - secretName: flower-tls - rules: - - host: flower.benny.com -``` - -### 6.3 Apply Flower deloyment - -```shell -kubectl apply -f 06.flower.yml -``` +refer guide here [kubernetes](k8s.md) # Command @@ -450,8 +244,6 @@ unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. uncache - Delete cache for this link, owner only. purge - Delete all tasks , owner only. -topup - Top up quota -tgvip - Using Telegram payment to pay for VIP ``` # Test data @@ -481,8 +273,9 @@ https://twitter.com/BennyThinks/status/1475836588542341124 ## Stripe -You can choose to donate via Stripe. Please click the button below to donate via Stripe. -Choose the currency and payment method that suits you. +You can choose to donate via Stripe by clicking the button below. + +Select the currency and payment method that suits you. | USD(Card, Apple Pay and Google Pay) | SEK(Card, Apple Pay and Google Pay) | CNY(Card, Apple Pay, Google Pay and Alipay) | |--------------------------------------------------|--------------------------------------------------|--------------------------------------------------| diff --git a/k8s.md b/k8s.md new file mode 100644 index 00000000..61b0146b --- /dev/null +++ b/k8s.md @@ -0,0 +1,200 @@ +## Kubernetes + +Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of +containerized applications + +# Complete deployment guide for k8s deloyment + +* contains every functionality +* compatible with amd64, arm64 and armv7l + +## First. Get all file in k8s folder + +Download `k8s` file to a directory on your k8s server and go to this folder + +## 1. Create Redis deloyment + +```shell +kubectl apply -f 01.redis.yml +``` + +This command will create ytdl namespace, redis pod and redis service + +## 2. Creat MariaDB deloyment + +```shell +kubectl apply -f 02.mariadb.yml +``` + +This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your +storageClassName before apply. + +## 3. Set environment variables + +Create configMap for env + +### 3.1 Edit configmap.yml + +```shell +vim 03.configmap.yml +``` + +you can configure all the following environment variables: + +* PYRO_WORKERS: number of workers for pyrogram, default is 100 +* WORKERS: workers count for celery +* APP_ID: **REQUIRED**, get it from https://core.telegram.org/ +* APP_HASH: **REQUIRED** +* TOKEN: **REQUIRED** +* REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ + +* OWNER: owner username +* QUOTA: quota in bytes +* EX: quota expire time +* MULTIPLY: vip quota comparing to normal quota +* USD2CNY: exchange rate +* VIP: VIP mode, default: disable +* AFD_LINK +* COFFEE_LINK +* COFFEE_TOKEN +* AFD_TOKEN +* AFD_USER_ID + +* AUTHORIZED_USER: users that could use this bot, user_id, separated with `,` +* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot. Could be use with + above `AUTHORIZED_USER` + +* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. +* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream +* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode +* MYSQL_USER +* MYSQL_PASS +* GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. +* AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For + example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. +* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. + **Inline button will be lost during the forwarding.** + +### 3.2 Apply configMap for environment variables + +```shell +kubectl apply -f 03.configmap.yml +``` + +## 4. Run Master Celery + +```shell +kubectl apply -f 04.ytdl-master.yml +``` + +This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, +cookies folder and flower database. Please replace longhorn with your storageClassName before apply + +### 4.1 Setup instagram cookies + +Required if you want to support instagram. + +You can use this extension +[Get cookies.txt](https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid) +to get instagram cookies + +Get pod running ytdl master: + +```shell +kubectl get pods --namespace ytdl +``` + +Name should be ytdl-xxxxxxxx + +Access to pod + +```shell +kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh +``` + +(replace ytdl-xxx by your pod name) + +Go to ytdl-pvc mounted folder + +```shell +cd /ytdlbot/ytdlbot/data/ +vim instagram.com_cookies.txt +# paste your cookies +``` + +## 5. Run Worker Celery + +```shell +kubectl apply -f 05.ytdl-worker.yml +``` + +## 6. Run Flower image (OPTIONAL) + +### 6.1 Setup flower db + +Get pod running ytdl master: + +```shell +kubectl get pods --namespace ytdl +``` + +Name should be ytdl-xxxxxxxx + +Access to pod + +```shell +kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh +``` + +(replace ytdl-xxx by your pod name) + +Go to ytdl-pvc mounted folder + +```shel +cd /var/lib/vnstat/ +``` + +Create flower database file + +```shell +{} ~ python3 +Python 3.9.9 (main, Nov 21 2021, 03:22:47) +[Clang 12.0.0 (clang-1200.0.32.29)] on darwin +Type "help", "copyright", "credits" or "license" for more information. +>>> import dbm;dbm.open("flower","n");exit() +``` + +### 6.2 Config Flower Ingress + +This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from +internet. +YML file should be adjusted depending on your load balancing, ingress and network system + +For active SSL + +```yml +cert-manager.io/cluster-issuer: letsencrypt-prod +``` + +Replace nginx by your ingress service + +```yml +ingressClassName: nginx +``` + +Add your domain, example + +```yml +tls: + - hosts: + - flower.benny.com + secretName: flower-tls + rules: + - host: flower.benny.com +``` + +### 6.3 Apply Flower deloyment + +```shell +kubectl apply -f 06.flower.yml +``` diff --git a/requirements.txt b/requirements.txt index 480c5458..be225b9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.3.3 -APScheduler==3.10.0 +yt-dlp==2023.3.4 +APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.2 @@ -10,14 +10,15 @@ filetype==1.2.0 flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 -beautifulsoup4==4.11.2 -fakeredis==2.9.2 +beautifulsoup4==4.12.1 +fakeredis==2.10.3 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.3.3 requests==2.28.2 -tqdm==4.64.1 +tqdm==4.65.0 requests-toolbelt==0.10.1 ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 +coloredlogs==15.0.1 diff --git a/scripts/migrate_to_mysql.py b/scripts/migrate_to_mysql.py index 30b5f4f4..436fb78a 100644 --- a/scripts/migrate_to_mysql.py +++ b/scripts/migrate_to_mysql.py @@ -8,6 +8,7 @@ __author__ = "Benny " import sqlite3 + import pymysql mysql_con = pymysql.connect(host='localhost', user='root', passwd='root', db='vip', charset='utf8mb4') diff --git a/scripts/start.sh b/scripts/start.sh index acb78ec9..242f207f 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -9,9 +9,5 @@ docker run -d --restart unless-stopped --name ytdl \ -e WORKERS=4 \ -e VIP=True \ -e CUSTOM_TEXT=#StandWithUkraine \ - -e WORKER_NAME=Pi \ - -v "$(pwd)"/session:/ytdlbot/ytdlbot/session \ - -v "$(pwd)"/ramdisk:/tmp/ \ - --log-driver none \ bennythink/ytdlbot \ /usr/local/bin/supervisord -c "/ytdlbot/conf/supervisor_worker.conf" diff --git a/ytdlbot/broadcast.py b/ytdlbot/broadcast.py deleted file mode 100644 index 91d9a4b8..00000000 --- a/ytdlbot/broadcast.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - broadcast.py -# 8/25/21 16:11 -# - -__author__ = "Benny " - -import argparse -import contextlib -import logging -import random -import sys -import tempfile -import time - -from tqdm import tqdm - -from db import Redis -from ytdl_bot import create_app - -parser = argparse.ArgumentParser(description='Broadcast to users') -parser.add_argument('-m', help='message', required=True) -parser.add_argument('-p', help='picture', default=None) -parser.add_argument('--notify', help='notify all users?', action="store_false") -parser.add_argument('-u', help='user_id', type=int) -logging.basicConfig(level=logging.INFO) -args = parser.parse_args() - -r = Redis().r -keys = r.keys("*") -user_ids = set() -for key in keys: - if key.isdigit(): - user_ids.add(key) - -metrics = r.hgetall("metrics") - -for key in metrics: - if key.isdigit(): - user_ids.add(key) - -if args.u: - user_ids = [args.u] - -if "YES" != input("Are you sure you want to send broadcast message to %s users?\n>" % len(user_ids)): - logging.info("Abort") - sys.exit(1) - -with tempfile.NamedTemporaryFile() as tmp: - app = create_app(tmp.name, 1) - app.start() - for user_id in tqdm(user_ids): - time.sleep(random.random() * 5) - if args.p: - with contextlib.suppress(Exception): - app.send_photo(user_id, args.p, caption=args.m, disable_notification=args.notify) - else: - with contextlib.suppress(Exception): - app.send_message(user_id, args.m, disable_notification=args.notify) - app.stop() diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py new file mode 100644 index 00000000..56e79a51 --- /dev/null +++ b/ytdlbot/channel.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# coding: utf-8 +import http +import logging +import os +import re + +import requests +from bs4 import BeautifulSoup + +from config import ENABLE_VIP +from limit import Payment + + +class Channel(Payment): + def subscribe_channel(self, user_id: "int", share_link: "str"): + if not re.findall(r"youtube\.com|youtu\.be", share_link): + raise ValueError("Is this a valid YouTube Channel link?") + if ENABLE_VIP: + self.cur.execute("select count(user_id) from subscribe where user_id=%s", (user_id,)) + usage = int(self.cur.fetchone()[0]) + if usage >= 5: + # TODO: 5 tokens for one more subscription? + logging.warning("User %s has subscribed %s channels", user_id, usage) + return "You have subscribed too many channels. Maximum 5 channels." + + data = self.get_channel_info(share_link) + channel_id = data["channel_id"] + + self.cur.execute("select user_id from subscribe where user_id=%s and channel_id=%s", (user_id, channel_id)) + if self.cur.fetchall(): + raise ValueError("You have already subscribed this channel.") + + self.cur.execute( + "INSERT IGNORE INTO channel values" + "(%(link)s,%(title)s,%(description)s,%(channel_id)s,%(playlist)s,%(last_video)s)", + data, + ) + self.cur.execute("INSERT INTO subscribe values(%s,%s, NULL)", (user_id, channel_id)) + self.con.commit() + logging.info("User %s subscribed channel %s", user_id, data["title"]) + return "Subscribed to {}".format(data["title"]) + + def unsubscribe_channel(self, user_id: "int", channel_id: "str"): + affected_rows = self.cur.execute( + "DELETE FROM subscribe WHERE user_id=%s AND channel_id=%s", (user_id, channel_id) + ) + self.con.commit() + logging.info("User %s tried to unsubscribe channel %s", user_id, channel_id) + return affected_rows + + @staticmethod + def extract_canonical_link(url): + # canonic link works for many websites. It will strip out unnecessary stuff + props = ["canonical", "alternate", "shortlinkUrl"] + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36" + } + cookie = {"CONSENT": "YES+cb.20210328-17-p0.en+FX+999"} + # send head request first + r = requests.head(url, headers=headers, allow_redirects=True, cookies=cookie) + if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type"): + # get content-type, if it's not text/html, there's no need to issue a GET request + logging.warning("%s Content-type is not text/html, no need to GET for extract_canonical_link", url) + return url + + html_doc = requests.get(url, headers=headers, cookies=cookie, timeout=5).text + soup = BeautifulSoup(html_doc, "html.parser") + for prop in props: + element = soup.find("link", rel=prop) + try: + href = element["href"] + if href not in ["null", "", None]: + return href + except Exception: + logging.warning("Canonical exception %s", url) + + return url + + def get_channel_info(self, url: "str"): + api_key = os.getenv("GOOGLE_API_KEY") + canonical_link = self.extract_canonical_link(url) + try: + channel_id = canonical_link.split("youtube.com/channel/")[1] + except IndexError: + channel_id = canonical_link.split("/")[-1] + channel_api = ( + f"https://www.googleapis.com/youtube/v3/channels?part=snippet,contentDetails&id={channel_id}&key={api_key}" + ) + + data = requests.get(channel_api).json() + snippet = data["items"][0]["snippet"] + title = snippet["title"] + description = snippet["description"] + playlist = data["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"] + + return { + "link": url, + "title": title, + "description": description, + "channel_id": channel_id, + "playlist": playlist, + "last_video": self.get_latest_video(playlist), + } + + @staticmethod + def get_latest_video(playlist_id: "str"): + api_key = os.getenv("GOOGLE_API_KEY") + video_api = ( + f"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=1&" + f"playlistId={playlist_id}&key={api_key}" + ) + data = requests.get(video_api).json() + video_id = data["items"][0]["snippet"]["resourceId"]["videoId"] + logging.info(f"Latest video %s from %s", video_id, data["items"][0]["snippet"]["channelTitle"]) + return f"https://www.youtube.com/watch?v={video_id}" + + def has_newer_update(self, channel_id: "str"): + self.cur.execute("SELECT playlist,latest_video FROM channel WHERE channel_id=%s", (channel_id,)) + data = self.cur.fetchone() + playlist_id = data[0] + old_video = data[1] + newest_video = self.get_latest_video(playlist_id) + if old_video != newest_video: + logging.info("Newer update found for %s %s", channel_id, newest_video) + self.cur.execute("UPDATE channel SET latest_video=%s WHERE channel_id=%s", (newest_video, channel_id)) + self.con.commit() + return newest_video + + def get_user_subscription(self, user_id: "int"): + self.cur.execute( + """ + select title, link, channel.channel_id from channel, subscribe + where subscribe.user_id = %s and channel.channel_id = subscribe.channel_id + """, + (user_id,), + ) + data = self.cur.fetchall() + text = "" + for item in data: + text += "[{}]({}) `{}\n`".format(*item) + return text + + def group_subscriber(self): + # {"channel_id": [user_id, user_id, ...]} + self.cur.execute("select * from subscribe where is_valid=1") + data = self.cur.fetchall() + group = {} + for item in data: + group.setdefault(item[1], []).append(item[0]) + logging.info("Checking periodic subscriber...") + return group + + def deactivate_user_subscription(self, user_id: "int"): + self.cur.execute("UPDATE subscribe set is_valid=0 WHERE user_id=%s", (user_id,)) + self.con.commit() + + def sub_count(self): + sql = """ + select user_id, channel.title, channel.link + from subscribe, channel where subscribe.channel_id = channel.channel_id + """ + self.cur.execute(sql) + data = self.cur.fetchall() + text = f"Total {len(data)} subscriptions found.\n\n" + for item in data: + text += "{} ==> [{}]({})\n".format(*item) + return text + + def del_cache(self, user_link: "str"): + unique = self.extract_canonical_link(user_link) + caches = self.r.hgetall("cache") + count = 0 + for key in caches: + if key.startswith(unique): + count += self.del_send_cache(key) + return count diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 9183b117..eebd7f4f 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -12,26 +12,18 @@ # general settings WORKERS: "int" = int(os.getenv("WORKERS", 100)) PYRO_WORKERS: "int" = int(os.getenv("PYRO_WORKERS", 100)) -APP_ID: "int" = int(os.getenv("APP_ID", 111)) -APP_HASH = os.getenv("APP_HASH", "111") -TOKEN = os.getenv("TOKEN", "3703WLI") +APP_ID: "int" = int(os.getenv("APP_ID", 198214)) +APP_HASH = os.getenv("APP_HASH", "1234b90") +TOKEN = os.getenv("TOKEN", "1234") -REDIS = os.getenv("REDIS") - -# quota settings -QUOTA = int(os.getenv("QUOTA", 5 * 1024 * 1024 * 1024)) # 5G -if os.uname().sysname == "Darwin": - QUOTA = 10 * 1024 * 1024 # 10M +REDIS = os.getenv("REDIS", "redis") TG_MAX_SIZE = 2 * 1024 * 1024 * 1024 * 0.99 # TG_MAX_SIZE = 10 * 1024 * 1024 -EX = int(os.getenv("EX", 24 * 3600)) -MULTIPLY = os.getenv("MULTIPLY", 10) # VIP1 is 5*5-25G, VIP2 is 50G -USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ยฅ30 +EXPIRE = 24 * 3600 ENABLE_VIP = os.getenv("VIP", False) -MAX_DURATION = int(os.getenv("MAX_DURATION", 60)) AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") COFFEE_TOKEN = os.getenv("COFFEE_TOKEN") @@ -49,7 +41,7 @@ ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", False) BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4") -MYSQL_HOST = os.getenv("MYSQL_HOST") +MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql") MYSQL_USER = os.getenv("MYSQL_USER", "root") MYSQL_PASS = os.getenv("MYSQL_PASS", "root") @@ -58,9 +50,12 @@ IPv6 = os.getenv("IPv6", False) ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) -# 0.01 means basically no limit -RATE = float(os.getenv("RATE", 0.01)) -BURST = int(os.getenv("BURST", 3)) + +# Stripe setting PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" + PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) + +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 5) +TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 92d0a021..a9702871 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -8,13 +8,16 @@ __author__ = "Benny " import os -import time -from config import (AFD_LINK, BURST, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, - EX, MULTIPLY, RATE, REQUIRED_MEMBERSHIP, USD2CNY) -from db import InfluxDB -from downloader import sizeof_fmt -from limit import QUOTA, VIP +from config import ( + AFD_LINK, + COFFEE_LINK, + ENABLE_CELERY, + FREE_DOWNLOAD, + REQUIRED_MEMBERSHIP, + TOKEN_PRICE, +) +from database import InfluxDB from utils import get_func_queue @@ -22,96 +25,64 @@ class BotText: start = "Welcome to YouTube Download bot. Type /help for more information." help = f""" -1. This bot should works at all times. If it doesn't, wait for a few minutes, try to send the link again. +1. This bot should work at all times. If it doesn't, please wait for a few minutes and try sending the link again. -2. At this time of writing, this bot consumes more than 100GB of network traffic per day. -In order to avoid being abused, -every one can use this bot within **{sizeof_fmt(QUOTA)} of quota for every {int(EX / 3600)} hours.** +2. At the time of writing, this bot consumes more than 100GB of network traffic per day. +To prevent abuse, each user is limited to 5 downloads per 24 hours. -3. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information. +3. You have the option to buy more tokens. Type /buy for more information. -4. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot +4. The source code for this bot will always remain open and can be found here: https://github.com/tgbot-collection/ytdlbot + """ -5. Request limit is applied for everyone, excluding VIP users. - """ if ENABLE_VIP else "Help text" + about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" - about = "YouTube-DL by @BennyThink. Open source on GitHub: https://github.com/tgbot-collection/ytdlbot" - - vip = f""" + buy = f""" **Terms:** -1. You can use this service, free of charge, {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours. -2. The above traffic, is counted one-way. For example, if you download a video of 1GB, your will use 1GB instead of 2GB. -3. Streaming support is limited due to high costs of conversion. -4. I won't gather any personal information, which means I don't know how many and what videos did you download. -5. No rate limit for VIP users. -6. Possible to refund, but you'll have to bear with process fee. -7. I'll record your unique ID after a successful payment, usually it's payment ID or email address. -8. VIP identity won't expire. -9. Please try not to abuse this service. It's a open source project, you can always deploy your own bot. - -**Pay Tier:** -1. Everyone: {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours -2. VIP1: ${MULTIPLY} or ยฅ{MULTIPLY * USD2CNY}, {sizeof_fmt(QUOTA * 2)} per {int(EX / 3600)} hours -3. VIP2: ${MULTIPLY * 2} or ยฅ{MULTIPLY * USD2CNY * 2}, {sizeof_fmt(QUOTA * 2 * 2)} per {int(EX / 3600)} hours -4. VIP4....VIPn. - -**Temporary top up** -Just want more traffic for a short period of time? Don't worry, you can use /topup command to top up your quota. -It's valid permanently, until you use it up. - -**Payment method:** -1. (afdian) Mainland China: {AFD_LINK} -2. (buy me a coffee) Other countries or regions: {COFFEE_LINK} -3. Telegram Payment(stripe), please directly using /tgvip command. +1. You can use this service free of charge for up to {FREE_DOWNLOAD} downloads within a 24-hour period, regardless of whether the download is successful or not. + +2. You can purchase additional download tokens, which will be valid indefinitely. + +3. I will not gather any personal information, so I won't know how many or which videos you have downloaded. + +4. Refunds are possible, but you will be responsible for the processing fee charged by the payment provider (Stripe, Buy Me a Coffee, etc.). + +5. I will record your unique ID after a successful payment, which is usually your payment ID or email address. + + +**Download token price:** +1. Everyone: {FREE_DOWNLOAD} tokens per 24 hours, free of charge. +2. 1 USD == {TOKEN_PRICE} tokens, valid indefinitely. +3. 7 CNY == {TOKEN_PRICE} tokens, valid indefinitely. + +**Payment option:** +1. AFDIAN(AliPay, WeChat Pay and PayPal): {AFD_LINK} +2. Buy me a coffee: {COFFEE_LINK} +3. Telegram Payment(Stripe), see following invoice. **After payment:** -1. afdian: with your order number `/vip 123456` -2. buy me a coffee: with your email `/vip someone@else.com` -3. Telegram Payment: automatically activated - """ if ENABLE_VIP else "VIP is not enabled." - vip_pay = "Processing your payments...If it's not responding after one minute, please contact @BennyThink." +1. Afdian: Provide your order number with the /redeem command (e.g., `/redeem 123456`). +2. Buy Me a Coffee: Provide your email with the /redeem command (e.g., `/redeem some@one.com`). **Use different email each time.** +3. Telegram Payment: Your payment will be automatically activated. +Want to buy more token at once? Let's say 100? Here you go! `/buy 123` + """ private = "This bot is for private use" membership_require = f"You need to join this group or channel to use this bot\n\nhttps://t.me/{REQUIRED_MEMBERSHIP}" settings = """ -Select sending format and video quality. **Only applies to YouTube** -High quality is recommended; Medium quality is aimed as 720P while low quality is aimed as 480P. - -Remember if you choose to send as document, there will be no streaming. +Please choose the desired format and video quality for your video. Note that these settings only **apply to YouTube videos**. + +High quality is recommended. Medium quality is 720P, while low quality is 480P. + +Please keep in mind that if you choose to send the video as a document, it will not be possible to stream it. Your current settings: Video quality: **{0}** Sending format: **{1}** """ custom_text = os.getenv("CUSTOM_TEXT", "") - topup_description = f"US$1 will give you {sizeof_fmt(QUOTA)} traffic permanently" - topup_title = "Pay US$1 for more traffic!" - - def remaining_quota_caption(self, chat_id): - if not ENABLE_VIP: - return "" - used, total, ttl = self.return_remaining_quota(chat_id) - refresh_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ttl + time.time())) - caption = f"Remaining quota: **{sizeof_fmt(used)}/{sizeof_fmt(total)}**, " \ - f"refresh at {refresh_time}\n" - return caption - - @staticmethod - def return_remaining_quota(chat_id): - used, total, ttl = VIP().check_remaining_quota(chat_id) - return used, total, ttl - - @staticmethod - def get_vip_greeting(chat_id): - if not ENABLE_VIP: - return "" - v = VIP().check_vip(chat_id) - if v: - return f"Hello {v[1]}, VIP{v[-2]}โ˜บ๏ธ\n\n" - else: - return "" @staticmethod def get_receive_link_text(): @@ -126,6 +97,7 @@ def get_receive_link_text(): @staticmethod def ping_worker(): from tasks import app as celery_app + workers = InfluxDB().extract_dashboard_data() # [{'celery@BennyใฎMBP': 'abc'}, {'celery@BennyใฎMBP': 'abc'}] response = celery_app.control.broadcast("ping_revision", reply=True) @@ -144,5 +116,3 @@ def ping_worker(): text += f"{status}{hostname} **{active}** {load} {rev}\n" return text - - too_fast = f"You have reached rate limit. Current rate limit is 1 request per {RATE} seconds, {BURST - 1} bursts." diff --git a/ytdlbot/db.py b/ytdlbot/database.py similarity index 71% rename from ytdlbot/db.py rename to ytdlbot/database.py index 15e2a018..1075f22a 100644 --- a/ytdlbot/db.py +++ b/ytdlbot/database.py @@ -1,7 +1,7 @@ #!/usr/local/bin/python3 # coding: utf-8 -# ytdlbot - db.py +# ytdlbot - database.py # 12/7/21 16:57 # @@ -13,6 +13,7 @@ import logging import os import re +import sqlite3 import subprocess import time from io import BytesIO @@ -24,13 +25,49 @@ from beautifultable import BeautifulTable from influxdb import InfluxDBClient -from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, QUOTA, REDIS -from fakemysql import FakeMySQL +from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS +from utils import sizeof_fmt + +init_con = sqlite3.connect(":memory:", check_same_thread=False) + + +class FakeMySQL: + @staticmethod + def cursor() -> "Cursor": + return Cursor() + + def commit(self): + pass + + def close(self): + pass + + +class Cursor: + def __init__(self): + self.con = init_con + self.cur = self.con.cursor() + + def execute(self, *args, **kwargs): + sql = self.sub(args[0]) + new_args = (sql,) + args[1:] + return self.cur.execute(*new_args, **kwargs) + + def fetchall(self): + return self.cur.fetchall() + + def fetchone(self): + return self.cur.fetchone() + + @staticmethod + def sub(sql): + sql = re.sub(r"CHARSET.*|charset.*", "", sql, re.IGNORECASE) + sql = sql.replace("%s", "?") + return sql class Redis: def __init__(self): - super(Redis, self).__init__() if REDIS: self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True) else: @@ -61,6 +98,7 @@ def __init__(self): {usage_banner} %s """ + super().__init__() def __del__(self): self.r.close() @@ -82,14 +120,13 @@ def generate_table(header, all_data: "list"): return table def show_usage(self): - from downloader import sizeof_fmt db = MySQL() - db.cur.execute("select * from vip") + db.cur.execute("select user_id,payment_amount,old_user,token from payment") data = db.cur.fetchall() fd = [] for item in data: - fd.append([item[0], item[1], sizeof_fmt(item[-1])]) - db_text = self.generate_table(["ID", "username", "quota"], fd) + fd.append([item[0], item[1], item[2], item[3]]) + db_text = self.generate_table(["ID", "pay amount", "old user", "token"], fd) fd = [] hash_keys = self.r.hgetall("metrics") @@ -117,10 +154,10 @@ def show_usage(self): # vnstat if os.uname().sysname == "Darwin": - cmd = "/usr/local/bin/vnstat -i en0".split() + cmd = "/opt/homebrew/bin/vnstat -i en0".split() else: cmd = "/usr/bin/vnstat -i eth0".split() - vnstat_text = subprocess.check_output(cmd).decode('u8') + vnstat_text = subprocess.check_output(cmd).decode("u8") return self.final_text % (db_text, vnstat_text, quota_text, metrics_text, usage_text) def reset_today(self): @@ -152,18 +189,16 @@ def del_send_cache(self, unique): class MySQL: vip_sql = """ - create table if not exists vip + CREATE TABLE if not exists `payment` ( - user_id bigint not null, - username varchar(256) null, - payment_amount int null, - payment_id varchar(256) null, - level int default 1 null, - quota bigint default %s null, - constraint VIP_pk - primary key (user_id) - ); - """ % QUOTA + `user_id` bigint NOT NULL, + `payment_amount` float DEFAULT NULL, + `payment_id` varchar(256) DEFAULT NULL, + `old_user` tinyint(1) DEFAULT NULL, + `token` int DEFAULT NULL, + UNIQUE KEY `payment_id` (`payment_id`) + ) CHARSET = utf8mb4 + """ settings_sql = """ create table if not exists settings @@ -202,13 +237,15 @@ class MySQL: def __init__(self): if MYSQL_HOST: - self.con = pymysql.connect(host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", - charset="utf8mb4") + self.con = pymysql.connect( + host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" + ) else: self.con = FakeMySQL() self.cur = self.con.cursor() self.init_db() + super().__init__() def init_db(self): self.cur.execute(self.vip_sql) @@ -220,6 +257,31 @@ def init_db(self): def __del__(self): self.con.close() + def get_user_settings(self, user_id: "str") -> "tuple": + cur = self.con.cursor() + cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) + data = cur.fetchone() + if data is None: + return 100, "high", "video", "Celery" + return data + + def set_user_settings(self, user_id: int, field: "str", value: "str"): + cur = self.con.cursor() + cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) + data = cur.fetchone() + if data is None: + resolution = method = "" + if field == "resolution": + method = "video" + resolution = value + if field == "method": + method = value + resolution = "high" + cur.execute("INSERT INTO settings VALUES (%s,%s,%s,%s)", (user_id, resolution, method, "Celery")) + else: + cur.execute(f"UPDATE settings SET {field} =%s WHERE user_id = %s", (value, user_id)) + self.con.commit() + class InfluxDB: def __init__(self): @@ -231,8 +293,8 @@ def __del__(self): @staticmethod def get_worker_data(): - password = os.getenv("FLOWER_PASSWORD", "123456abc") username = os.getenv("FLOWER_USERNAME", "benny") + password = os.getenv("FLOWER_PASSWORD", "123456abc") token = base64.b64encode(f"{username}:{password}".encode()).decode() headers = {"Authorization": f"Basic {token}"} r = requests.get("https://celery.dmesg.app/dashboard?json=1", headers=headers) @@ -250,7 +312,6 @@ def extract_dashboard_data(self): "tags": { "hostname": worker["hostname"], }, - "time": datetime.datetime.utcnow(), "fields": { "task-received": worker.get("task-received", 0), @@ -262,7 +323,7 @@ def extract_dashboard_data(self): "load1": load1, "load5": load5, "load15": load15, - } + }, } json_body.append(t) return json_body @@ -273,26 +334,11 @@ def __fill_worker_data(self): def __fill_overall_data(self): active = sum([i["active"] for i in self.data["data"]]) - json_body = [ - { - "measurement": "active", - "time": datetime.datetime.utcnow(), - "fields": { - "active": active - } - } - ] + json_body = [{"measurement": "active", "time": datetime.datetime.utcnow(), "fields": {"active": active}}] self.client.write_points(json_body) def __fill_redis_metrics(self): - json_body = [ - { - "measurement": "metrics", - "time": datetime.datetime.utcnow(), - "fields": { - } - } - ] + json_body = [{"measurement": "metrics", "time": datetime.datetime.utcnow(), "fields": {}}] r = Redis().r hash_keys = r.hgetall("metrics") for key, value in hash_keys.items(): diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 9b33c1fe..e524a1b2 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -24,23 +24,14 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import (AUDIO_FORMAT, ENABLE_FFMPEG, ENABLE_VIP, MAX_DURATION, ENABLE_ARIA2, - TG_MAX_SIZE, IPv6) -from db import Redis -from limit import VIP -from utils import (adjust_formats, apply_log_formatter, current_time, - get_user_settings) +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, IPv6 +from limit import Payment +from utils import adjust_formats, apply_log_formatter, current_time +from ytdlbot.config import TG_MAX_SIZE r = fakeredis.FakeStrictRedis() apply_log_formatter() - - -def sizeof_fmt(num: int, suffix='B'): - for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: - if abs(num) < 1024.0: - return "%3.1f%s%s" % (num, unit, suffix) - num /= 1024.0 - return "%.1f%s%s" % (num, 'Yi', suffix) +payment = Payment() def edit_text(bot_msg, text): @@ -60,9 +51,15 @@ def more(title, initial): return "" f = StringIO() - tqdm(total=total, initial=finished, file=f, ascii=False, unit_scale=True, ncols=30, - bar_format="{l_bar}{bar} |{n_fmt}/{total_fmt} " - ) + tqdm( + total=total, + initial=finished, + file=f, + ascii=False, + unit_scale=True, + ncols=30, + bar_format="{l_bar}{bar} |{n_fmt}/{total_fmt} ", + ) raw_output = f.getvalue() tqdm_output = raw_output.split("|") progress = f"`[{tqdm_output[1]}]`" @@ -80,7 +77,7 @@ def more(title, initial): def remove_bash_color(text): - return re.sub(r'\u001b|\[0;94m|\u001b\[0m|\[0;32m|\[0m|\[0;33m', "", text) + return re.sub(r"\u001b|\[0;94m|\u001b\[0m|\[0;32m|\[0m|\[0;33m", "", text) def download_hook(d: dict, bot_msg): @@ -90,16 +87,12 @@ def download_hook(d: dict, bot_msg): original_url = d["info_dict"]["original_url"] key = f"{bot_msg.chat.id}-{original_url}" - if d['status'] == 'downloading': + if d["status"] == "downloading": downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) # percent = remove_bash_color(d.get("_percent_str", "N/A")) speed = remove_bash_color(d.get("_speed_str", "N/A")) - if ENABLE_VIP and not r.exists(key): - result, err_msg = check_quota(total, bot_msg.chat.id) - if result is False: - raise ValueError(err_msg) eta = remove_bash_color(d.get("_eta_str", d.get("eta"))) text = tqdm_progress("Downloading...", total, downloaded, speed, eta) edit_text(bot_msg, text) @@ -107,25 +100,10 @@ def download_hook(d: dict, bot_msg): def upload_hook(current, total, bot_msg): - # filesize = sizeof_fmt(total) text = tqdm_progress("Uploading...", total, current) edit_text(bot_msg, text) -def check_quota(file_size, chat_id) -> ("bool", "str"): - remain, _, ttl = VIP().check_remaining_quota(chat_id) - if file_size > remain: - refresh_time = current_time(ttl + time.time()) - err = f"Quota exceed, you have {sizeof_fmt(remain)} remaining, " \ - f"but you want to download a video with {sizeof_fmt(file_size)} in size. \n" \ - f"Try again in {ttl} seconds({refresh_time})" - logging.warning(err) - Redis().update_metrics("quota_exceed") - return False, err - else: - return True, "" - - def convert_to_mp4(resp: dict, bot_msg): default_type = ["video/x-flv", "video/webm"] if resp["status"]: @@ -136,7 +114,9 @@ def convert_to_mp4(resp: dict, bot_msg): if mime in default_type: if not can_convert_mp4(path, bot_msg.chat.id): logging.warning("Conversion abort for %s", bot_msg.chat.id) - bot_msg._client.send_message(bot_msg.chat.id, "Can't convert your video to streaming format.") + bot_msg._client.send_message( + bot_msg.chat.id, "Can't convert your video to streaming format. ffmpeg has been disabled." + ) break edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") new_file_path = path.with_suffix(".mp4") @@ -170,18 +150,7 @@ def run_ffmpeg(cmd_list, bm): def can_convert_mp4(video_path, uid): if not ENABLE_FFMPEG: return False - if not ENABLE_VIP: - return True - video_streams = ffmpeg.probe(video_path, select_streams="v") - try: - duration = int(float(video_streams["format"]["duration"])) - except Exception: - duration = 0 - if duration > MAX_DURATION and not VIP().check_vip(uid): - logging.info("Video duration: %s, not vip, can't convert", duration) - return False - else: - return True + return True def ytdl_download(url, tempdir, bm, **kwargs) -> dict: @@ -190,33 +159,27 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: response = {"status": True, "error": "", "filepath": []} output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { - 'progress_hooks': [lambda d: download_hook(d, bm)], - 'outtmpl': output, - 'restrictfilenames': False, - 'quiet': True, - "proxy": os.getenv("YTDL_PROXY") + "progress_hooks": [lambda d: download_hook(d, bm)], + "outtmpl": output, + "restrictfilenames": False, + "quiet": True, + "proxy": os.getenv("YTDL_PROXY"), } if ENABLE_ARIA2: ydl_opts["external_downloader"] = "aria2c" - ydl_opts["external_downloader_args"] = ['--min-split-size=1M', - '--max-connection-per-server=16', - '--max-concurrent-downloads=16', - '--split=16' - ] + ydl_opts["external_downloader_args"] = [ + "--min-split-size=1M", + "--max-connection-per-server=16", + "--max-concurrent-downloads=16", + "--split=16", + ] formats = [ "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", - None + None, ] adjust_formats(chat_id, url, formats, hijack) add_instagram_cookies(url, ydl_opts) - # check quota before download - if ENABLE_VIP: - # check quota after download - remain, _, ttl = VIP().check_remaining_quota(chat_id) - result, err_msg = check_quota(detect_filesize(url), chat_id) - if not result: - return {"status": False, "error": err_msg, "filepath": []} address = ["::", "0.0.0.0"] if IPv6 else [None] for format_ in formats: @@ -230,6 +193,7 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: ydl.download([url]) response["status"] = True response["error"] = "" + response["filepath"] = list(pathlib.Path(tempdir).glob("*")) break except Exception as e: logging.error("Download failed for %s ", url) @@ -243,32 +207,15 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: if response["status"] is False: return response - for i in os.listdir(tempdir): - p = pathlib.Path(tempdir, i) - file_size = os.stat(p).st_size - if ENABLE_VIP: - # check quota after download - remain, _, ttl = VIP().check_remaining_quota(chat_id) - result, err_msg = check_quota(file_size, chat_id) - else: - result, err_msg = True, "" - if result is False: - response["status"] = False - response["error"] = err_msg - else: - VIP().use_quota(bm.chat.id, file_size) - response["status"] = True - response["filepath"].append(p) - # convert format if necessary - settings = get_user_settings(str(chat_id)) + settings = payment.get_user_settings(str(chat_id)) if settings[2] == "video" or isinstance(settings[2], MagicMock): # only convert if send type is video convert_to_mp4(response, bm) if settings[2] == "audio" or hijack == "bestaudio[ext=m4a]": convert_audio_format(response, bm) - # enable it for now - split_large_video(response) + # disable it for now + # split_large_video(response) return response @@ -280,9 +227,7 @@ def convert_audio_format(resp: "dict", bm): path: "pathlib.Path" for path in resp["filepath"]: streams = ffmpeg.probe(path)["streams"] - if (AUDIO_FORMAT is None and - len(streams) == 1 and - streams[0]["codec_type"] == "audio"): + if AUDIO_FORMAT is None and len(streams) == 1 and streams[0]["codec_type"] == "audio": logging.info("%s is audio, default format, no need to convert", path) elif AUDIO_FORMAT is None and len(streams) >= 2: logging.info("%s is video, default format, need to extract audio", path) @@ -311,11 +256,6 @@ def add_instagram_cookies(url: "str", opt: "dict"): opt["cookiefile"] = pathlib.Path(__file__).parent.joinpath("instagram.com_cookies.txt").as_posix() -def run_splitter(video_path: "str"): - subprocess.check_output(f"sh split-video.sh {video_path} {TG_MAX_SIZE * 0.95} ".split()) - os.remove(video_path) - - def split_large_video(response: "dict"): original_video = None split = False @@ -324,19 +264,8 @@ def split_large_video(response: "dict"): if size > TG_MAX_SIZE: split = True logging.warning("file is too large %s, splitting...", size) - run_splitter(original_video) + subprocess.check_output(f"sh split-video.sh {original_video} {TG_MAX_SIZE * 0.95} ".split()) + os.remove(original_video) if split and original_video: response["filepath"] = [i.as_posix() for i in pathlib.Path(original_video).parent.glob("*")] - - -def detect_filesize(url: "str") -> "int": - # find the largest file size - with ytdl.YoutubeDL() as ydl: - info_dict = ydl.extract_info(url, download=False) - max_size = 0 - max_size_list = [i.get("filesize", 0) for i in info_dict["formats"] if i.get("filesize")] - if max_size_list: - max_size = max(max_size_list) - logging.info("%s max size is %s", url, max_size) - return max_size diff --git a/ytdlbot/fakemysql.py b/ytdlbot/fakemysql.py deleted file mode 100644 index eb96a34b..00000000 --- a/ytdlbot/fakemysql.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - fakemysql.py -# 2/20/22 20:08 -# - -__author__ = "Benny " - -import re -import sqlite3 - -init_con = sqlite3.connect(":memory:", check_same_thread=False) - - -class FakeMySQL: - @staticmethod - def cursor() -> "Cursor": - return Cursor() - - def commit(self): - pass - - def close(self): - pass - - -class Cursor: - def __init__(self): - self.con = init_con - self.cur = self.con.cursor() - - def execute(self, *args, **kwargs): - sql = self.sub(args[0]) - new_args = (sql,) + args[1:] - return self.cur.execute(*new_args, **kwargs) - - def fetchall(self): - return self.cur.fetchall() - - def fetchone(self): - return self.cur.fetchone() - - @staticmethod - def sub(sql): - sql = re.sub(r"CHARSET.*|charset.*", "", sql, re.IGNORECASE) - sql = sql.replace("%s", "?") - return sql - - -if __name__ == '__main__': - con = FakeMySQL() - cur = con.cursor() - cur.execute("create table user(id int, name varchar(20))") - cur.execute("insert into user values(%s,%s)", (1, "benny")) - cur.execute("select * from user") - data = cur.fetchall() - print(data) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 3e9f04c0..6e6128ab 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -8,239 +8,26 @@ __author__ = "Benny " import hashlib -import http import logging -import math -import os -import re import time -from unittest.mock import MagicMock import requests -from bs4 import BeautifulSoup -from config import (AFD_TOKEN, AFD_USER_ID, COFFEE_TOKEN, ENABLE_VIP, EX, - MULTIPLY, OWNER, QUOTA, USD2CNY) -from db import MySQL, Redis -from utils import apply_log_formatter +from config import ( + AFD_TOKEN, + AFD_USER_ID, + COFFEE_TOKEN, + EXPIRE, + FREE_DOWNLOAD, + OWNER, + TOKEN_PRICE, +) +from database import MySQL, Redis +from utils import apply_log_formatter, current_time apply_log_formatter() -class VIP(Redis, MySQL): - - def check_vip(self, user_id: "int") -> "tuple": - self.cur.execute("SELECT * FROM vip WHERE user_id=%s", (user_id,)) - data = self.cur.fetchone() - return data - - def __add_vip(self, user_data: "dict"): - sql = "INSERT INTO vip VALUES (%s,%s,%s,%s,%s,%s);" - self.cur.execute(sql, list(user_data.values())) - self.con.commit() - # also remove redis cache - self.r.delete(user_data["user_id"]) - - def add_vip(self, user_data: "dict") -> "str": - # first select - self.cur.execute("SELECT * FROM vip WHERE payment_id=%s", (user_data["payment_id"],)) - is_exist = self.cur.fetchone() - if is_exist: - return "Failed. {} is being used by user {}".format(user_data["payment_id"], is_exist[0]) - self.__add_vip(user_data) - return "Success! You are VIP{} now!".format(user_data["level"]) - - def direct_add_vip(self, user_data: "dict") -> ("bool", "str"): - self.__add_vip(user_data) - return "Success payment from Telegram! You are VIP{} now!".format(user_data["level"]) - - def remove_vip(self, user_id: "int"): - raise NotImplementedError() - - def get_user_quota(self, user_id: "int") -> int: - # even VIP have certain quota - q = self.check_vip(user_id) - topup = self.r.hget("topup", user_id) - if q: - return q[-1] - elif topup: - return int(topup) + QUOTA - else: - return QUOTA - - def set_topup(self, user_id: "int"): - self.r.hset("topup", user_id, QUOTA) - - def check_remaining_quota(self, user_id: "int"): - user_quota = self.get_user_quota(user_id) - ttl = self.r.ttl(user_id) - q = int(self.r.get(user_id)) if self.r.exists(user_id) else user_quota - if q <= 0: - q = 0 - return q, user_quota, ttl - - def use_quota(self, user_id: "int", traffic: "int"): - user_quota = self.get_user_quota(user_id) - # fix for standard mode - if isinstance(user_quota, MagicMock): - user_quota = 2 ** 32 - if self.r.exists(user_id): - self.r.decr(user_id, traffic) - else: - self.r.set(user_id, user_quota - traffic, ex=EX) - - def subscribe_channel(self, user_id: "int", share_link: "str"): - if not re.findall(r"youtube\.com|youtu\.be", share_link): - raise ValueError("Is this a valid YouTube Channel link?") - if ENABLE_VIP: - self.cur.execute("select count(user_id) from subscribe where user_id=%s", (user_id,)) - usage = int(self.cur.fetchone()[0]) - if usage >= 5 and not self.check_vip(user_id): - logging.warning("User %s is not VIP but has subscribed %s channels", user_id, usage) - return "You have subscribed too many channels. Please upgrade to VIP to subscribe more channels." - - data = self.get_channel_info(share_link) - channel_id = data["channel_id"] - - self.cur.execute("select user_id from subscribe where user_id=%s and channel_id=%s", (user_id, channel_id)) - if self.cur.fetchall(): - raise ValueError("You have already subscribed this channel.") - - self.cur.execute("INSERT IGNORE INTO channel values" - "(%(link)s,%(title)s,%(description)s,%(channel_id)s,%(playlist)s,%(last_video)s)", data) - self.cur.execute("INSERT INTO subscribe values(%s,%s, NULL)", (user_id, channel_id)) - self.con.commit() - logging.info("User %s subscribed channel %s", user_id, data["title"]) - return "Subscribed to {}".format(data["title"]) - - def unsubscribe_channel(self, user_id: "int", channel_id: "str"): - affected_rows = self.cur.execute("DELETE FROM subscribe WHERE user_id=%s AND channel_id=%s", - (user_id, channel_id)) - self.con.commit() - logging.info("User %s tried to unsubscribe channel %s", user_id, channel_id) - return affected_rows - - @staticmethod - def extract_canonical_link(url): - # canonic link works for many websites. It will strip out unnecessary stuff - proxy = {"https": os.getenv("YTDL_PROXY")} if os.getenv("YTDL_PROXY") else None - props = ["canonical", "alternate", "shortlinkUrl"] - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"} - # send head request first - r = requests.head(url, headers=headers, proxies=proxy) - if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type"): - # get content-type, if it's not text/html, there's no need to issue a GET request - logging.warning("%s Content-type is not text/html, no need to GET for extract_canonical_link", url) - return url - - html_doc = requests.get(url, headers=headers, timeout=5, proxies=proxy).text - soup = BeautifulSoup(html_doc, "html.parser") - for prop in props: - element = soup.find("link", rel=prop) - try: - href = element["href"] - if href not in ["null", "", None]: - return href - except Exception: - logging.warning("Canonical exception %s", url) - - return url - - def get_channel_info(self, url: "str"): - api_key = os.getenv("GOOGLE_API_KEY") - canonical_link = self.extract_canonical_link(url) - try: - channel_id = canonical_link.split("https://www.youtube.com/channel/")[1] - except IndexError: - channel_id = canonical_link.split("https://youtube.com/channel/")[1] - channel_api = f"https://www.googleapis.com/youtube/v3/channels?part=snippet,contentDetails&" \ - f"id={channel_id}&key={api_key}" - data = requests.get(channel_api).json() - snippet = data['items'][0]['snippet'] - title = snippet['title'] - description = snippet['description'] - playlist = data['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - - return { - "link": url, - "title": title, - "description": description, - "channel_id": channel_id, - "playlist": playlist, - "last_video": VIP.get_latest_video(playlist) - } - - @staticmethod - def get_latest_video(playlist_id: "str"): - api_key = os.getenv("GOOGLE_API_KEY") - video_api = f"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=1&" \ - f"playlistId={playlist_id}&key={api_key}" - data = requests.get(video_api).json() - video_id = data['items'][0]['snippet']['resourceId']['videoId'] - logging.info(f"Latest video %s from %s", video_id, data['items'][0]['snippet']['channelTitle']) - return f"https://www.youtube.com/watch?v={video_id}" - - def has_newer_update(self, channel_id: "str"): - self.cur.execute("SELECT playlist,latest_video FROM channel WHERE channel_id=%s", (channel_id,)) - data = self.cur.fetchone() - playlist_id = data[0] - old_video = data[1] - newest_video = VIP.get_latest_video(playlist_id) - if old_video != newest_video: - logging.info("Newer update found for %s %s", channel_id, newest_video) - self.cur.execute("UPDATE channel SET latest_video=%s WHERE channel_id=%s", (newest_video, channel_id)) - self.con.commit() - return newest_video - - def get_user_subscription(self, user_id: "int"): - self.cur.execute( - """ - select title, link, channel.channel_id from channel, subscribe - where subscribe.user_id = %s and channel.channel_id = subscribe.channel_id - """, (user_id,)) - data = self.cur.fetchall() - text = "" - for item in data: - text += "[{}]({}) `{}\n`".format(*item) - return text - - def group_subscriber(self): - # {"channel_id": [user_id, user_id, ...]} - self.cur.execute("select * from subscribe where is_valid=1") - data = self.cur.fetchall() - group = {} - for item in data: - group.setdefault(item[1], []).append(item[0]) - logging.info("Checking periodic subscriber...") - return group - - def deactivate_user_subscription(self, user_id: "int"): - self.cur.execute("UPDATE subscribe set is_valid=0 WHERE user_id=%s", (user_id,)) - self.con.commit() - - def sub_count(self): - sql = """ - select user_id, channel.title, channel.link - from subscribe, channel where subscribe.channel_id = channel.channel_id - """ - self.cur.execute(sql) - data = self.cur.fetchall() - text = f"Total {len(data)} subscriptions found.\n\n" - for item in data: - text += "{} ==> [{}]({})\n".format(*item) - return text - - def del_cache(self, user_link: "str"): - unique = self.extract_canonical_link(user_link) - caches = self.r.hgetall("cache") - count = 0 - for key in caches: - if key.startswith(unique): - count += self.del_send_cache(key) - return count - - class BuyMeACoffee: def __init__(self): self._token = COFFEE_TOKEN @@ -266,8 +53,7 @@ def get_user_payment(self, email: "str") -> ("int", "float", "str"): price = float(order.get("support_coffee_price", 0)) cups = float(order.get("support_coffees", 1)) amount = price * cups - level = math.floor(amount / MULTIPLY) - return level, amount, email + return amount, email class Afdian: @@ -279,11 +65,11 @@ def __init__(self): def _generate_signature(self): data = { "user_id": self._user_id, - "params": "{\"x\":0}", + "params": '{"x":0}', "ts": int(time.time()), } sign_text = "{token}params{params}ts{ts}user_id{user_id}".format( - token=self._token, params=data['params'], ts=data["ts"], user_id=data["user_id"] + token=self._token, params=data["params"], ts=data["ts"], user_id=data["user_id"] ) md5 = hashlib.md5(sign_text.encode("u8")) @@ -305,44 +91,75 @@ def _get_afdian_status(self, trade_no: "str") -> "dict": def get_user_payment(self, trade_no: "str") -> ("int", "float", "str"): order = self._get_afdian_status(trade_no) amount = float(order.get("show_amount", 0)) - level = math.floor(amount / (MULTIPLY * USD2CNY)) - return level, amount, trade_no + # convert to USD + return amount / 7, trade_no -def verify_payment(user_id, unique, client) -> "str": - if not ENABLE_VIP: - return "VIP is not enabled." - logging.info("Verifying payment for %s - %s", user_id, unique) - if "@" in unique: - pay = BuyMeACoffee() - else: - pay = Afdian() +class Payment(Redis, MySQL): + # TODO transaction issues + def check_old_user(self, user_id: "int") -> "tuple": + self.cur.execute("SELECT * FROM payment WHERE user_id=%s AND old_user=1", (user_id,)) + data = self.cur.fetchone() + return data - level, amount, pay_id = pay.get_user_payment(unique) - if amount == 0: - return f"You pay amount is {amount}. Did you input wrong order ID or email? " \ - f"Talk to @{OWNER} if you need any assistant." - if not level: - return f"You pay amount {amount} is below minimum ${MULTIPLY}. " \ - f"Talk to @{OWNER} if you need any assistant." - else: - vip = VIP() - ud = { - "user_id": user_id, - "username": client.get_chat(user_id).first_name, - "payment_amount": amount, - "payment_id": pay_id, - "level": level, - "quota": QUOTA * level * MULTIPLY - } + def get_pay_token(self, user_id: "int") -> int: + self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) + data = self.cur.fetchall() or [(0,)] + return sum([i[0] for i in data if i[0]]) - message = vip.add_vip(ud) - return message + def get_free_token(self, user_id: "int") -> int: + if self.r.exists(user_id): + return int(self.r.get(user_id)) + else: + # set and return + self.r.set(user_id, FREE_DOWNLOAD, ex=EXPIRE) + return FREE_DOWNLOAD + + def get_token(self, user_id): + ttl = self.r.ttl(user_id) + return self.get_free_token(user_id), self.get_pay_token(user_id), current_time(time.time() + ttl) + + def use_free_token(self, user_id: "int"): + if self.r.exists(user_id): + self.r.decr(user_id, 1) + else: + # first time download + self.r.set(user_id, 5 - 1, ex=EXPIRE) + def use_pay_token(self, user_id: int): + # a user may pay multiple times, so we'll need to filter the first payment with valid token + self.cur.execute("SELECT payment_id FROM payment WHERE user_id=%s AND token>0", (user_id,)) + data = self.cur.fetchone() + payment_id = data[0] + logging.info("User %s use pay token with payment_id %s", user_id, payment_id) + self.cur.execute("UPDATE payment SET token=token-1 WHERE payment_id=%s", (payment_id,)) + self.con.commit() + + def use_token(self, user_id): + free = self.get_free_token(user_id) + if free > 0: + self.use_free_token(user_id) + else: + self.use_pay_token(user_id) -def subscribe_query(): - vip = VIP() - for cid, uid in vip.group_subscriber().items(): - has = vip.has_newer_update(cid) - if has: - print(f"{has} - {uid}") + def add_pay_user(self, pay_data: "list"): + self.cur.execute("INSERT INTO payment VALUES (%s,%s,%s,%s,%s)", pay_data) + self.con.commit() + + def verify_payment(self, user_id: "int", unique: "str") -> "str": + pay = BuyMeACoffee() if "@" in unique else Afdian() + self.cur.execute("SELECT * FROM payment WHERE payment_id=%s ", (unique,)) + data = self.cur.fetchone() + if data: + # TODO what if a user pay twice with the same email address? + return ( + f"Failed. Payment has been verified by other users. Please contact @{OWNER} if you have any questions." + ) + + amount, pay_id = pay.get_user_payment(unique) + logging.info("User %s paid %s, identifier is %s", user_id, amount, unique) + # amount is already in USD + if amount == 0: + return "Payment not found. Please check your payment ID or email address" + self.add_pay_user([user_id, amount, pay_id, 0, amount * TOKEN_PRICE]) + return "Thanks! Your payment has been verified. /start to get your token details" diff --git a/ytdlbot/migration.sql b/ytdlbot/migration.sql deleted file mode 100644 index 15fc385d..00000000 --- a/ytdlbot/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table settings - add mode varchar(32) default 'Celery' null; - diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 82448a22..29392e15 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -31,26 +31,33 @@ from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor +from channel import Channel from client_init import create_app -from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE, - ENABLE_VIP, TG_MAX_SIZE, WORKERS) +from config import ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE, TG_MAX_SIZE, WORKERS, ENABLE_VIP from constant import BotText -from db import Redis -from downloader import (edit_text, sizeof_fmt, tqdm_progress, upload_hook, - ytdl_download) -from limit import VIP -from utils import (apply_log_formatter, auto_restart, customize_logger, - get_metadata, get_revision, get_user_settings) +from database import Redis +from downloader import edit_text, tqdm_progress, upload_hook, ytdl_download +from limit import Payment +from utils import ( + apply_log_formatter, + auto_restart, + customize_logger, + get_metadata, + get_revision, + sizeof_fmt, +) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) apply_log_formatter() bot_text = BotText() -logging.getLogger('apscheduler.executors.default').propagate = False +logging.getLogger("apscheduler.executors.default").propagate = False # celery -A tasks worker --loglevel=info --pool=solo # app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') -app = Celery('tasks', broker=BROKER) - +app = Celery("tasks", broker=BROKER) +redis = Redis() +payment = Payment() +channel = Channel() celery_client = create_app(":memory:") @@ -80,9 +87,10 @@ def audio_task(chat_id, message_id): def get_unique_clink(original_url, user_id): - settings = get_user_settings(str(user_id)) - clink = VIP().extract_canonical_link(original_url) + settings = payment.get_user_settings(str(user_id)) + clink = channel.extract_canonical_link(original_url) try: + # different user may have different resolution settings unique = "{}?p={}{}".format(clink, *settings[1:]) except IndexError: unique = clink @@ -97,45 +105,34 @@ def direct_download_task(chat_id, message_id, url): logging.info("Direct download celery tasks ended.") -def forward_video(url, client, bot_msg): +def forward_video(client, bot_msg, url): chat_id = bot_msg.chat.id - red = Redis() - vip = VIP() unique = get_unique_clink(url, chat_id) - - cached_fid = red.get_send_cache(unique) + cached_fid = redis.get_send_cache(unique) if not cached_fid: return False try: res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid) - if not res_msg: - raise ValueError("Failed to forward message") - obj = res_msg.document or res_msg.video or res_msg.audio - if ENABLE_VIP: - file_size = getattr(obj, "file_size", None) \ - or getattr(obj, "file_size", None) \ - or getattr(obj, "file_size", 10) - # TODO: forward file size may exceed the limit - vip.use_quota(chat_id, file_size) + obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation + caption, _ = gen_cap(bot_msg, url, obj) res_msg.edit_text(caption, reply_markup=gen_video_markup()) bot_msg.edit_text(f"Download success!โœ…โœ…โœ…") - red.update_metrics("cache_hit") + redis.update_metrics("cache_hit") return True - except Exception as e: traceback.print_exc() logging.error("Failed to forward message %s", e) - red.del_send_cache(unique) - red.update_metrics("cache_miss") + redis.del_send_cache(unique) + redis.update_metrics("cache_miss") -def ytdl_download_entrance(bot_msg, client, url): +def ytdl_download_entrance(client, bot_msg, url): chat_id = bot_msg.chat.id - if forward_video(url, client, bot_msg): + if forward_video(client, bot_msg, url): return - mode = get_user_settings(str(chat_id))[-1] + mode = payment.get_user_settings(str(chat_id))[-1] if ENABLE_CELERY and mode in [None, "Celery"]: async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) @@ -163,19 +160,10 @@ def audio_entrance(bot_msg, client): def direct_normal_download(bot_msg, client, url): chat_id = bot_msg.chat.id headers = { - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"} - vip = VIP() + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36" + } + vip = Payment() length = 0 - if ENABLE_VIP: - remain, _, _ = vip.check_remaining_quota(chat_id) - try: - head_req = requests.head(url, headers=headers) - length = int(head_req.headers.get("content-length")) - except (TypeError, requests.exceptions.RequestException): - length = 0 - if remain < length: - bot_msg.reply_text(f"Sorry, you have reached your quota.\n") - return req = None try: @@ -203,13 +191,15 @@ def direct_normal_download(bot_msg, client, url): downloaded += len(chunk) logging.info("Downloaded file %s", filename) st_size = os.stat(filepath).st_size - if ENABLE_VIP: - vip.use_quota(chat_id, st_size) + client.send_chat_action(chat_id, "upload_document") - client.send_document(bot_msg.chat.id, filepath, - caption=f"filesize: {sizeof_fmt(st_size)}", - progress=upload_hook, progress_args=(bot_msg,), - ) + client.send_document( + bot_msg.chat.id, + filepath, + caption=f"filesize: {sizeof_fmt(st_size)}", + progress=upload_hook, + progress_args=(bot_msg,), + ) bot_msg.edit_text("Download success!โœ…") @@ -219,11 +209,11 @@ def normal_audio(bot_msg, client): status_msg = bot_msg.reply_text("Converting to audio...please wait patiently", quote=True) orig_url: "str" = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: - client.send_chat_action(chat_id, 'record_audio') + client.send_chat_action(chat_id, "record_audio") # just try to download the audio using yt-dlp resp = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") - client.send_chat_action(chat_id, 'upload_audio') + client.send_chat_action(chat_id, "upload_audio") for f in resp["filepath"]: client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") @@ -240,7 +230,7 @@ def get_dl_source(): def upload_transfer_sh(bm, paths: list) -> "str": d = {p.name: (md5(p.name.encode("utf8")).hexdigest() + p.suffix, p.open("rb")) for p in paths} monitor = MultipartEncoderMonitor(MultipartEncoder(fields=d), lambda x: upload_hook(x.bytes_read, x.len, bm)) - headers = {'Content-Type': monitor.content_type} + headers = {"Content-Type": monitor.content_type} try: req = requests.post("https://transfer.sh", data=monitor, headers=headers) bm.edit_text(f"Download success!โœ…") @@ -256,9 +246,9 @@ def ytdl_normal_download(bot_msg, client, url): result = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") if result["status"]: - client.send_chat_action(chat_id, 'upload_document') + client.send_chat_action(chat_id, "upload_document") video_paths = result["filepath"] - bot_msg.edit_text('Download complete. Sending now...') + bot_msg.edit_text("Download complete. Sending now...") for video_path in video_paths: # normally there's only one video in that path... st_size = os.stat(video_path).st_size @@ -268,9 +258,9 @@ def ytdl_normal_download(bot_msg, client, url): # client.send_message(chat_id, upload_transfer_sh(bot_msg, video_paths)) continue upload_processor(client, bot_msg, url, video_path) - bot_msg.edit_text('Download success!โœ…') + bot_msg.edit_text("Download success!โœ…") else: - client.send_chat_action(chat_id, 'typing') + client.send_chat_action(chat_id, "typing") tb = result["error"][0:4000] bot_msg.edit_text(f"Download failed!โŒ\n\n```{tb}```", disable_web_page_preview=True) @@ -279,52 +269,75 @@ def ytdl_normal_download(bot_msg, client, url): def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.Path]"): chat_id = bot_msg.chat.id - red = Redis() markup = gen_video_markup() cap, meta = gen_cap(bot_msg, url, vp_or_fid) - settings = get_user_settings(str(chat_id)) + settings = payment.get_user_settings(str(chat_id)) if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): chat_id = ARCHIVE_ID if settings[2] == "document": logging.info("Sending as document") try: # send as document could be sent as video even if it's a document - res_msg = client.send_document(chat_id, vp_or_fid, - caption=cap, - progress=upload_hook, progress_args=(bot_msg,), - reply_markup=markup, - thumb=meta["thumb"], - force_document=True - ) + res_msg = client.send_document( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + reply_markup=markup, + thumb=meta["thumb"], + force_document=True, + ) except ValueError: logging.error("Retry to send as video") - res_msg = client.send_video(chat_id, vp_or_fid, - supports_streaming=True, - caption=cap, - progress=upload_hook, progress_args=(bot_msg,), - reply_markup=markup, - **meta - ) + res_msg = client.send_video( + chat_id, + vp_or_fid, + supports_streaming=True, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + reply_markup=markup, + **meta, + ) elif settings[2] == "audio": logging.info("Sending as audio") - res_msg = client.send_audio(chat_id, vp_or_fid, - caption=cap, - progress=upload_hook, progress_args=(bot_msg,), - ) + res_msg = client.send_audio( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + ) else: logging.info("Sending as video") - res_msg = client.send_video(chat_id, vp_or_fid, - supports_streaming=True, - caption=cap, - progress=upload_hook, progress_args=(bot_msg,), - reply_markup=markup, - **meta - ) + try: + res_msg = client.send_video( + chat_id, + vp_or_fid, + supports_streaming=True, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + reply_markup=markup, + **meta, + ) + except ValueError: + logging.info("Retry to send as animation") + res_msg = client.send_animation( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + reply_markup=markup, + **meta, + ) unique = get_unique_clink(url, bot_msg.chat.id) - obj = res_msg.document or res_msg.video or res_msg.audio - red.add_send_cache(unique, getattr(obj, "file_id", None)) - red.update_metrics("video_success") + obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation + redis.add_send_cache(unique, getattr(obj, "file_id", None)) + redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.message_id) return res_msg @@ -334,11 +347,7 @@ def gen_cap(bm, url, video_path): chat_id = bm.chat.id user = bm.chat try: - user_info = "@{}({})-{}".format( - user.username or "N/A", - user.first_name or "" + user.last_name or "", - user.id - ) + user_info = "@{}({})-{}".format(user.username or "N/A", user.first_name or "" + user.last_name or "", user.id) except Exception: user_info = "" @@ -348,17 +357,24 @@ def gen_cap(bm, url, video_path): file_size = sizeof_fmt(os.stat(video_path).st_size) else: file_name = getattr(video_path, "file_name", "") - file_size = sizeof_fmt(getattr(video_path, "file_size", (2 << 6) - (2 << 4) - (2 << 2) + (0 ^ 1) + (2 << 5))) + file_size = sizeof_fmt(getattr(video_path, "file_size", (2 << 2) + ((2 << 2) + 1) + (2 << 5))) meta = dict( width=getattr(video_path, "width", 0), height=getattr(video_path, "height", 0), duration=getattr(video_path, "duration", 0), thumb=getattr(video_path, "thumb", None), ) - remain = bot_text.remaining_quota_caption(chat_id) + free = payment.get_free_token(chat_id) + pay = payment.get_pay_token(chat_id) + if ENABLE_VIP: + remain = f"Download token count: free {free}, pay {pay}" + else: + remain = "" worker = get_dl_source() - cap = f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" \ - f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" + cap = ( + f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" + f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" + ) return cap, meta @@ -367,8 +383,7 @@ def gen_video_markup(): [ [ # First row InlineKeyboardButton( # Generates a callback query when pressed - "convert to audio", - callback_data="convert" + "convert to audio", callback_data="convert" ) ] ] @@ -412,10 +427,10 @@ def async_task(task_name, *args): inspect = app.control.inspect() worker_stats = inspect.stats() route_queues = [] - padding = math.ceil(sum([i['pool']['max-concurrency'] for i in worker_stats.values()]) / len(worker_stats)) + padding = math.ceil(sum([i["pool"]["max-concurrency"] for i in worker_stats.values()]) / len(worker_stats)) for worker_name, stats in worker_stats.items(): - route = worker_name.split('@')[1] - concurrency = stats['pool']['max-concurrency'] + route = worker_name.split("@")[1] + concurrency = stats["pool"]["max-concurrency"] route_queues.extend([route] * (concurrency + padding)) destination = random.choice(route_queues) logging.info("Selecting worker %s from %s in %.2fs", destination, route_queues, time.time() - t0) @@ -424,11 +439,7 @@ def async_task(task_name, *args): def run_celery(): worker_name = os.getenv("WORKER_NAME", "") - argv = [ - "-A", "tasks", 'worker', '--loglevel=info', - "--pool=threads", f"--concurrency={WORKERS}", - "-n", worker_name - ] + argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name] if ENABLE_QUEUE: argv.extend(["-Q", worker_name]) app.worker_main(argv) @@ -439,14 +450,14 @@ def purge_tasks(): return f"purged {count} tasks." -if __name__ == '__main__': +if __name__ == "__main__": celery_client.start() print("Bootstrapping Celery worker now.....") time.sleep(5) threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Asia/Shanghai") - scheduler.add_job(auto_restart, 'interval', seconds=5) + scheduler.add_job(auto_restart, "interval", seconds=10) scheduler.start() idle() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index eea2fc67..a1c2b1b7 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -18,20 +18,20 @@ import time import uuid +import coloredlogs import ffmpeg import psutil -from db import MySQL from flower_tasks import app inspect = app.control.inspect() def apply_log_formatter(): - logging.basicConfig( + coloredlogs.install( level=logging.INFO, - format='[%(asctime)s %(filename)s:%(lineno)d %(levelname).1s] %(message)s', - datefmt="%Y-%m-%d %H:%M:%S" + fmt="[%(asctime)s %(filename)s:%(lineno)d %(levelname).1s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", ) @@ -41,33 +41,12 @@ def customize_logger(logger: "list"): logging.getLogger(log).setLevel(level=logging.INFO) -def get_user_settings(user_id: "str") -> "tuple": - db = MySQL() - cur = db.cur - cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) - data = cur.fetchone() - if data is None: - return 100, "high", "video", "Celery" - return data - - -def set_user_settings(user_id: int, field: "str", value: "str"): - db = MySQL() - cur = db.cur - cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) - data = cur.fetchone() - if data is None: - resolution = method = "" - if field == "resolution": - method = "video" - resolution = value - if field == "method": - method = value - resolution = "high" - cur.execute("INSERT INTO settings VALUES (%s,%s,%s,%s)", (user_id, resolution, method, "Celery")) - else: - cur.execute(f"UPDATE settings SET {field} =%s WHERE user_id = %s", (value, user_id)) - db.con.commit() +def sizeof_fmt(num: int, suffix="B"): + for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, "Yi", suffix) def is_youtube(url: "str"): @@ -76,6 +55,8 @@ def is_youtube(url: "str"): def adjust_formats(user_id: "str", url: "str", formats: "list", hijack=None): + from database import MySQL + # high: best quality 1080P, 2K, 4K, 8K # medium: 720P # low: 480P @@ -84,7 +65,7 @@ def adjust_formats(user_id: "str", url: "str", formats: "list", hijack=None): return mapping = {"high": [], "medium": [720], "low": [480]} - settings = get_user_settings(user_id) + settings = MySQL().get_user_settings(user_id) if settings and is_youtube(url): for m in mapping.get(settings[1], []): formats.insert(0, f"bestvideo[ext=mp4][height={m}]+bestaudio[ext=m4a]") @@ -106,7 +87,7 @@ def get_metadata(video_path): logging.error(e) try: thumb = pathlib.Path(video_path).parent.joinpath(f"{uuid.uuid4().hex}-thunmnail.png").as_posix() - ffmpeg.input(video_path, ss=duration / 2).filter('scale', width, -1).output(thumb, vframes=1).run() + ffmpeg.input(video_path, ss=duration / 2).filter("scale", width, -1).output(thumb, vframes=1).run() except ffmpeg._run.Error: thumb = None @@ -134,9 +115,9 @@ def get_func_queue(func) -> int: return 0 -def tail(f, lines=1, _buffer=4098): +def tail_log(f, lines=1, _buffer=4098): """Tail a file and get X lines from the end""" - # place holder for the lines found + # placeholder for the lines found lines_found = [] # block counter will be multiplied by buffer @@ -214,7 +195,7 @@ def auto_restart(): if not os.path.exists(log_path): return with open(log_path) as f: - logs = "".join(tail(f, lines=10)) + logs = "".join(tail_log(f, lines=10)) det = Detector(logs) method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] @@ -233,5 +214,5 @@ def clean_tempfile(): shutil.rmtree(item, ignore_errors=True) -if __name__ == '__main__': +if __name__ == "__main__": auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index f1298a97..3bcf540f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -24,33 +24,42 @@ from pyrogram.raw import types as raw_types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime -from token_bucket import Limiter, MemoryStorage +from channel import Channel from client_init import create_app -from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG, - ENABLE_VIP, MULTIPLY, OWNER, PROVIDER_TOKEN, QUOTA, RATE, - REQUIRED_MEMBERSHIP, PLAYLIST_SUPPORT) +from config import ( + AUTHORIZED_USER, + ENABLE_CELERY, + ENABLE_FFMPEG, + ENABLE_VIP, + OWNER, + PLAYLIST_SUPPORT, + PROVIDER_TOKEN, + REQUIRED_MEMBERSHIP, + TOKEN_PRICE, +) from constant import BotText -from db import InfluxDB, MySQL, Redis -from limit import VIP, verify_payment +from database import InfluxDB, MySQL, Redis +from limit import Payment from tasks import app as celery_app -from tasks import (audio_entrance, direct_download_entrance, hot_patch, - purge_tasks, ytdl_download_entrance) -from utils import (auto_restart, clean_tempfile, customize_logger, - get_revision, get_user_settings, set_user_settings) +from tasks import ( + audio_entrance, + direct_download_entrance, + hot_patch, + purge_tasks, + ytdl_download_entrance, +) +from utils import auto_restart, clean_tempfile, customize_logger, get_revision customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) -logging.getLogger('apscheduler.executors.default').propagate = False +logging.getLogger("apscheduler.executors.default").propagate = False app = create_app() -bot_text = BotText() - +db = MySQL() logging.info("Authorized users are %s", AUTHORIZED_USER) - -# rate, capacity -mem = MemoryStorage() -# 5 minutes, 2 bursts -lim = Limiter(1 / RATE, BURST, mem) +redis = Redis() +payment = Payment() +channel = Channel() def private_use(func): @@ -69,10 +78,10 @@ def wrapper(client: "Client", message: "types.Message"): users = [] if users and chat_id and chat_id not in users: - message.reply_text(bot_text.private, quote=True) + message.reply_text(BotText.private, quote=True) return - # membership check + # TODO bug fix # 198 membership check if REQUIRED_MEMBERSHIP: try: if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status != "member": @@ -81,7 +90,7 @@ def wrapper(client: "Client", message: "types.Message"): logging.info("user %s check passed for group/channel %s.", chat_id, REQUIRED_MEMBERSHIP) except UserNotParticipant: logging.warning("user %s is not a member of group/channel %s", chat_id, REQUIRED_MEMBERSHIP) - message.reply_text(bot_text.membership_require, quote=True) + message.reply_text(BotText.membership_require, quote=True) return return func(client, message) @@ -94,32 +103,44 @@ def start_handler(client: "Client", message: "types.Message"): from_id = message.from_user.id logging.info("Welcome to youtube-dl bot!") client.send_chat_action(from_id, "typing") - greeting = bot_text.get_vip_greeting(from_id) - quota = bot_text.remaining_quota_caption(from_id) - custom_text = bot_text.custom_text - text = f"{greeting}{bot_text.start}\n\n{quota}\n{custom_text}" - + is_old_user = payment.check_old_user(from_id) + if is_old_user: + info = "" + elif ENABLE_VIP: + free_token, pay_token, reset = payment.get_token(from_id) + info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}" + else: + info = "" + text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" client.send_message(message.chat.id, text) + # add to settings table + db.set_user_settings(from_id, "resolution", "high") @app.on_message(filters.command(["help"])) def help_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") - client.send_message(chat_id, bot_text.help, disable_web_page_preview=True) + client.send_message(chat_id, BotText.help, disable_web_page_preview=True) + + +@app.on_message(filters.command(["about"])) +def about_handler(client: "Client", message: "types.Message"): + chat_id = message.chat.id + client.send_chat_action(chat_id, "typing") + client.send_message(chat_id, BotText.about) @app.on_message(filters.command(["sub"])) def subscribe_handler(client: "Client", message: "types.Message"): - vip = VIP() chat_id = message.chat.id client.send_chat_action(chat_id, "typing") if message.text == "/sub": - result = vip.get_user_subscription(chat_id) + result = channel.get_user_subscription(chat_id) else: link = message.text.split()[1] try: - result = vip.subscribe_channel(chat_id, link) + result = channel.subscribe_channel(chat_id, link) except (IndexError, ValueError): result = f"Error: \n{traceback.format_exc()}" client.send_message(chat_id, result or "You have no subscription.", disable_web_page_preview=True) @@ -127,15 +148,14 @@ def subscribe_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["unsub"])) def unsubscribe_handler(client: "Client", message: "types.Message"): - vip = VIP() chat_id = message.chat.id client.send_chat_action(chat_id, "typing") text = message.text.split(" ") if len(text) == 1: - client.send_message(chat_id, "/unsubscribe channel_id", disable_web_page_preview=True) + client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True) return - rows = vip.unsubscribe_channel(chat_id, text[1]) + rows = channel.unsubscribe_channel(chat_id, text[1]) if rows: text = f"Unsubscribed from {text[1]}" else: @@ -159,7 +179,7 @@ def uncache_handler(client: "Client", message: "types.Message"): username = message.from_user.username link = message.text.split()[1] if username == OWNER: - count = VIP().del_cache(link) + count = channel.del_cache(link) message.reply_text(f"{count} cache(s) deleted.", quote=True) @@ -179,26 +199,19 @@ def ping_handler(client: "Client", message: "types.Message"): else: bot_info = get_runtime("ytdlbot_ytdl_1", "YouTube-dl") if message.chat.username == OWNER: - stats = bot_text.ping_worker()[:1000] - client.send_document(chat_id, Redis().generate_file(), caption=f"{bot_info}\n\n{stats}") + stats = BotText.ping_worker()[:1000] + client.send_document(chat_id, redis.generate_file(), caption=f"{bot_info}\n\n{stats}") else: client.send_message(chat_id, f"{bot_info}") -@app.on_message(filters.command(["about"])) -def about_handler(client: "Client", message: "types.Message"): - chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") - client.send_message(chat_id, bot_text.about) - - @app.on_message(filters.command(["sub_count"])) def sub_count_handler(client: "Client", message: "types.Message"): username = message.from_user.username chat_id = message.chat.id if username == OWNER: with BytesIO() as f: - f.write(VIP().sub_count().encode("u8")) + f.write(channel.sub_count().encode("u8")) f.name = "subscription count.txt" client.send_document(chat_id, f) @@ -207,15 +220,15 @@ def sub_count_handler(client: "Client", message: "types.Message"): def direct_handler(client: "Client", message: "types.Message"): chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") - url = re.sub(r'/direct\s*', '', message.text) + url = re.sub(r"/direct\s*", "", message.text) logging.info("direct start %s", url) if not re.findall(r"^https?://", url.lower()): - Redis().update_metrics("bad_request") + redis.update_metrics("bad_request") message.reply_text("Send me a DIRECT LINK.", quote=True) return bot_msg = message.reply_text("Request received.", quote=True) - Redis().update_metrics("direct_request") + redis.update_metrics("direct_request") direct_download_entrance(bot_msg, client, url) @@ -223,8 +236,8 @@ def direct_handler(client: "Client", message: "types.Message"): def settings_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") - data = get_user_settings(str(chat_id)) - set_mode = (data[-1]) + data = db.get_user_settings(str(chat_id)) + set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" if message.chat.username == OWNER: @@ -237,113 +250,115 @@ def settings_handler(client: "Client", message: "types.Message"): [ # First row InlineKeyboardButton("send as document", callback_data="document"), InlineKeyboardButton("send as video", callback_data="video"), - InlineKeyboardButton("send as audio", callback_data="audio") + InlineKeyboardButton("send as audio", callback_data="audio"), ], [ # second row InlineKeyboardButton("High Quality", callback_data="high"), InlineKeyboardButton("Medium Quality", callback_data="medium"), InlineKeyboardButton("Low Quality", callback_data="low"), ], - extra + extra, ] ) - client.send_message(chat_id, bot_text.settings.format(data[1], data[2]) + mode_text, reply_markup=markup) + client.send_message(chat_id, BotText.settings.format(data[1], data[2]) + mode_text, reply_markup=markup) -@app.on_message(filters.command(["vip"])) -def vip_handler(client: "Client", message: "types.Message"): +@app.on_message(filters.command(["buy"])) +def buy_handler(client: "Client", message: "types.Message"): # process as chat.id, not from_user.id chat_id = message.chat.id text = message.text.strip() client.send_chat_action(chat_id, "typing") - if text == "/vip": - client.send_message(chat_id, bot_text.vip, disable_web_page_preview=True) + client.send_message(chat_id, BotText.buy, disable_web_page_preview=True) + # generate telegram invoice here + payload = f"{message.chat.id}-buy" + token_count = message.text.replace("/buy", "").strip() + # currency USD + if token_count.isdigit(): + price = int(int(token_count) / TOKEN_PRICE * 100) else: - bm: typing.Union["types.Message", "typing.Any"] = message.reply_text(bot_text.vip_pay, quote=True) - unique = text.replace("/vip", "").strip() - msg = verify_payment(chat_id, unique, client) - bm.edit_text(msg) - - -def generate_invoice(amount: "int", title: "str", description: "str", payload: "str"): - invoice = raw_types.input_media_invoice.InputMediaInvoice( - invoice=( - raw_types.invoice.Invoice(currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)])), - title=title, - description=description, - provider=PROVIDER_TOKEN, - provider_data=raw_types.DataJSON(data="{}"), - payload=payload.encode(), - start_param=payload + price = 100 + invoice = generate_invoice( + price, f"Buy {TOKEN_PRICE} download tokens", "You can pay by Telegram payment or using link above", payload ) - return invoice - - -# payment related -@app.on_message(filters.command(["topup"])) -def topup_handler(client: "Client", message: "types.Message"): - chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") - invoice = generate_invoice(100, bot_text.topup_title, bot_text.topup_description, - f"{message.chat.id}-topup") app.send( functions.messages.SendMedia( peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), media=invoice, random_id=app.rnd_id(), - message="Please use your card to pay for more traffic" + message="Buy more download token", ) ) -@app.on_message(filters.command(["tgvip"])) -def tgvip_handler(client: "Client", message: "types.Message"): +@app.on_message(filters.command(["redeem"])) +def redeem_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") - invoice = generate_invoice(1000, f"VIP1", f"pay USD${MULTIPLY} for VIP1", f"{message.chat.id}-vip1".encode()) + text = message.text.strip() + unique = text.replace("/redeem", "").strip() + msg = payment.verify_payment(chat_id, unique) + message.reply_text(msg, quote=True) - app.send( - functions.messages.SendMedia( - peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), - media=invoice, - random_id=app.rnd_id(), - message="Please use your card to pay for more traffic" - ) + +def generate_invoice(amount: "int", title: "str", description: "str", payload: "str"): + invoice = raw_types.input_media_invoice.InputMediaInvoice( + invoice=raw_types.invoice.Invoice( + currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)] + ), + title=title, + description=description, + provider=PROVIDER_TOKEN, + provider_data=raw_types.DataJSON(data="{}"), + payload=payload.encode(), + start_param=payload, ) + return invoice @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: "Client", message: "types.Message"): - # check remaining quota - red = Redis() chat_id = message.from_user.id - client.send_chat_action(chat_id, 'typing') - red.user_count(chat_id) + client.send_chat_action(chat_id, "typing") + redis.user_count(chat_id) - url = re.sub(r'/ytdl\s*', '', message.text) + url = re.sub(r"/ytdl\s*", "", message.text) logging.info("start %s", url) + # check url if not re.findall(r"^https?://", url.lower()): - red.update_metrics("bad_request") + redis.update_metrics("bad_request") message.reply_text("I think you should send me a link.", quote=True) return + # disable by default if not PLAYLIST_SUPPORT: - if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url: - message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True) - red.update_metrics("reject_channel") + if re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url: + message.reply_text( + "The ability to download a channel or list has been disabled." + "Kindly provide me with the specific link for each video instead.", + quote=True, + ) + redis.update_metrics("reject_channel") return - # non vip user, consume too many token - if (not VIP().check_vip(chat_id)) and (not lim.consume(str(chat_id).encode(), 1)): - red.update_metrics("rate_limit") - message.reply_text(bot_text.too_fast, quote=True) - return - red.update_metrics("video_request") - text = bot_text.get_receive_link_text() + # old user is not limited by token + if ENABLE_VIP and not payment.check_old_user(chat_id): + free, pay, reset = payment.get_token(chat_id) + if free + pay <= 0: + message.reply_text( + f"You don't have enough token. Please wait until {reset} or /buy more token.", quote=True + ) + redis.update_metrics("reject_token") + return + else: + payment.use_token(chat_id) + + redis.update_metrics("video_request") + + text = BotText.get_receive_link_text() try: # raise pyrogram.errors.exceptions.FloodWait(10) bot_msg: typing.Union["types.Message", "typing.Any"] = message.reply_text(text, quote=True) @@ -352,15 +367,16 @@ def download_handler(client: "Client", message: "types.Message"): f.write(str(e).encode()) f.write(b"Your job will be done soon. Just wait! Don't rush.") f.name = "Please don't flood me.txt" - bot_msg = message.reply_document(f, caption=f"Flood wait! Please wait {e.x} seconds...." - f"Your job will start automatically", quote=True) + bot_msg = message.reply_document( + f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True + ) f.close() client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") time.sleep(e.x) - client.send_chat_action(chat_id, 'upload_video') + client.send_chat_action(chat_id, "upload_video") bot_msg.chat = message.chat - ytdl_download_entrance(bot_msg, client, url) + ytdl_download_entrance(client, bot_msg, url) @app.on_callback_query(filters.regex(r"document|video|audio")) @@ -368,7 +384,7 @@ def send_method_callback(client: "Client", callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) - set_user_settings(chat_id, "method", data) + db.set_user_settings(chat_id, "method", data) callback_query.answer(f"Your send type was set to {callback_query.data}") @@ -377,7 +393,7 @@ def download_resolution_callback(client: "Client", callback_query: types.Callbac chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) - set_user_settings(chat_id, "resolution", data) + db.set_user_settings(chat_id, "resolution", data) callback_query.answer(f"Your default download quality was set to {callback_query.data}") @@ -389,7 +405,7 @@ def audio_callback(client: "Client", callback_query: types.CallbackQuery): return callback_query.answer(f"Converting to audio...please wait patiently") - Redis().update_metrics("audio_request") + redis.update_metrics("audio_request") vmsg = callback_query.message audio_entrance(vmsg, client) @@ -397,24 +413,23 @@ def audio_callback(client: "Client", callback_query: types.CallbackQuery): @app.on_callback_query(filters.regex(r"Local|Celery")) def owner_local_callback(client: "Client", callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id - set_user_settings(chat_id, "mode", callback_query.data) + db.set_user_settings(chat_id, "mode", callback_query.data) callback_query.answer(f"Download mode was changed to {callback_query.data}") def periodic_sub_check(): - vip = VIP() exceptions = pyrogram.errors.exceptions - for cid, uids in vip.group_subscriber().items(): - video_url = vip.has_newer_update(cid) + for cid, uids in channel.group_subscriber().items(): + video_url = channel.has_newer_update(cid) if video_url: logging.info(f"periodic update:{video_url} - {uids}") for uid in uids: try: bot_msg = app.send_message(uid, f"{video_url} is downloading...", disable_web_page_preview=True) ytdl_download_entrance(bot_msg, app, video_url) - except(exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: + except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) - vip.deactivate_user_subscription(uid) + channel.deactivate_user_subscription(uid) except Exception as e: logging.error("Unknown error when sending message to user. %s", traceback.format_exc()) finally: @@ -424,44 +439,29 @@ def periodic_sub_check(): @app.on_raw_update() def raw_update(client: "Client", update, users, chats): action = getattr(getattr(update, "message", None), "action", None) - if update.QUALNAME == 'types.UpdateBotPrecheckoutQuery': + if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": client.send( functions.messages.SetBotPrecheckoutResults( query_id=update.query_id, success=True, ) ) - elif action and action.QUALNAME == 'types.MessageActionPaymentSentMe': + elif action and action.QUALNAME == "types.MessageActionPaymentSentMe": logging.info("Payment received. %s", action) uid = update.message.peer_id.user_id - vip = VIP() - amount = f"{action.total_amount / 100} {action.currency}" - if "vip" in action.payload.decode(): - ud = { - "user_id": uid, - "username": users.get(uid).username, - "payment_amount": 10, - "payment_id": 0, - "level": 1, - "quota": QUOTA * 2 - } - vip.direct_add_vip(ud) - client.send_message(uid, f"Thank you {uid}. VIP payment received: {amount}") - - else: - vip.set_topup(uid) - client.send_message(uid, f"Thank you {uid}. Top up payment received: {amount}") + amount = action.total_amount / 100 + payment.add_pay_user([uid, amount, action.charge.provider_charge_id, 0, amount * TOKEN_PRICE]) + client.send_message(uid, f"Thank you {uid}. Payment received: {amount} {action.currency}") -if __name__ == '__main__': - MySQL() - scheduler = BackgroundScheduler(timezone="Europe/Stockholm", job_defaults={'max_instances': 5}) - scheduler.add_job(Redis().reset_today, 'cron', hour=0, minute=0) - scheduler.add_job(auto_restart, 'interval', seconds=60) - scheduler.add_job(clean_tempfile, 'interval', seconds=60) - scheduler.add_job(InfluxDB().collect_data, 'interval', seconds=60) +if __name__ == "__main__": + scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) + scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) + scheduler.add_job(auto_restart, "interval", seconds=60) + scheduler.add_job(clean_tempfile, "interval", seconds=60) + scheduler.add_job(InfluxDB().collect_data, "interval", seconds=60) # default quota allocation of 10,000 units per day - scheduler.add_job(periodic_sub_check, 'interval', seconds=60 * 60) + scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ @@ -469,7 +469,7 @@ def raw_update(client: "Client", update, users, chats): โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–›โ–€ โ–Œ โ–Œ โ–Œ โ–Œ โ–โ–โ– โ–Œ โ–Œ โ– โ–Œ โ–Œ โ–žโ–€โ–Œ โ–Œ โ–Œ โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–˜ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€ โ–˜โ–˜ โ–˜ โ–˜ โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–โ–€โ–˜ -By @BennyThink, VIP mode: {ENABLE_VIP}, Distribution: {ENABLE_CELERY} +By @BennyThink, VIP mode: {ENABLE_VIP}, Celery Mode: {ENABLE_CELERY} Version: {get_revision()} """ print(banner) From 7bf4331274264b873edba52994da31ce7ada297b Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 12:02:04 +0200 Subject: [PATCH 069/297] membership check #198 --- ytdlbot/config.py | 5 +++-- ytdlbot/ytdl_bot.py | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index eebd7f4f..b471272b 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -33,8 +33,9 @@ # limitation settings AUTHORIZED_USER: "str" = os.getenv("AUTHORIZED_USER", "") -# membership requires: the format could be username/chat_id of channel or group -REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "") +# membership requires: the format could be username(without @ sign)/chat_id of channel or group. +# You need to add the bot to this group/channel as admin +REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "mikuri520") # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 3bcf540f..082e1c32 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -84,7 +84,12 @@ def wrapper(client: "Client", message: "types.Message"): # TODO bug fix # 198 membership check if REQUIRED_MEMBERSHIP: try: - if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status != "member": + if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status not in [ + "creator", + "administrator", + "member", + "owner", + ]: raise UserNotParticipant() else: logging.info("user %s check passed for group/channel %s.", chat_id, REQUIRED_MEMBERSHIP) From 282bf3c97bab726e5b829fd1d17a46e5da0be301 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 12:06:15 +0200 Subject: [PATCH 070/297] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7c04526f..12939362 100644 --- a/README.md +++ b/README.md @@ -236,14 +236,14 @@ about - What's this bot? ping - Bot running status help - Help ytdl - Download video in group -vip - Join VIP settings - Set your preference +buy - Buy token direct - Download file directly sub - Subscribe to YouTube Channel unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. uncache - Delete cache for this link, owner only. -purge - Delete all tasks , owner only. +purge - Delete all tasks, owner only. ``` # Test data From ee3dfbdffab8d42c366eb80fda147c528d85ea04 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 12:49:29 +0200 Subject: [PATCH 071/297] remove webm and av01 from codec detection fix #216 --- ytdlbot/downloader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index e524a1b2..83a36588 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -163,7 +163,6 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: "outtmpl": output, "restrictfilenames": False, "quiet": True, - "proxy": os.getenv("YTDL_PROXY"), } if ENABLE_ARIA2: ydl_opts["external_downloader"] = "aria2c" @@ -174,7 +173,8 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: "--split=16", ] formats = [ - "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", + # webm and av01 are not streamable on telegram, so we'll extract mp4 and not av01 codec + "bestvideo[ext=mp4][vcodec!*=av01]+bestaudio[ext=m4a]/bestvideo+bestaudio", "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", None, ] From 49877854a3ce691b5d4e22906a41896dd339e04a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:54:29 +0200 Subject: [PATCH 072/297] bump dependencies --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index be225b9e..547b628a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,11 +10,11 @@ filetype==1.2.0 flower==1.2.0 psutil==5.9.4 influxdb==5.3.1 -beautifulsoup4==4.12.1 +beautifulsoup4==4.12.2 fakeredis==2.10.3 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==4.3.3 +redis==4.5.4 requests==2.28.2 tqdm==4.65.0 requests-toolbelt==0.10.1 From 0427a9a2f2ee7354855053d57ac30426e6f75873 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 13:25:53 +0200 Subject: [PATCH 073/297] fix import bug --- ytdlbot/downloader.py | 3 +-- ytdlbot/tasks.py | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 83a36588..df9d88d8 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -24,10 +24,9 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, IPv6 +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6 from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time -from ytdlbot.config import TG_MAX_SIZE r = fakeredis.FakeStrictRedis() apply_log_formatter() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 29392e15..9b4178f5 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -33,7 +33,15 @@ from channel import Channel from client_init import create_app -from config import ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE, TG_MAX_SIZE, WORKERS, ENABLE_VIP +from config import ( + ARCHIVE_ID, + BROKER, + ENABLE_CELERY, + ENABLE_QUEUE, + ENABLE_VIP, + TG_MAX_SIZE, + WORKERS, +) from constant import BotText from database import Redis from downloader import edit_text, tqdm_progress, upload_hook, ytdl_download From 455cad608018af4b681572bd591b809ccf88307a Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 13:34:54 +0200 Subject: [PATCH 074/297] bad config REQUIRED_MEMBERSHIP --- ytdlbot/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index b471272b..505aebfb 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -35,7 +35,7 @@ AUTHORIZED_USER: "str" = os.getenv("AUTHORIZED_USER", "") # membership requires: the format could be username(without @ sign)/chat_id of channel or group. # You need to add the bot to this group/channel as admin -REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "mikuri520") +REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "") # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) From 1db7c684cd3df0503fe6ef3ca66c3a55ff10803d Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 14:35:11 +0200 Subject: [PATCH 075/297] fix transaction issue --- ytdlbot/downloader.py | 2 +- ytdlbot/limit.py | 1 - ytdlbot/tasks.py | 5 ++++- ytdlbot/ytdl_bot.py | 19 ++++++++++--------- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index df9d88d8..f57cf40a 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -30,7 +30,6 @@ r = fakeredis.FakeStrictRedis() apply_log_formatter() -payment = Payment() def edit_text(bot_msg, text): @@ -153,6 +152,7 @@ def can_convert_mp4(video_path, uid): def ytdl_download(url, tempdir, bm, **kwargs) -> dict: + payment = Payment() chat_id = bm.chat.id hijack = kwargs.get("hijack") response = {"status": True, "error": "", "filepath": []} diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 6e6128ab..91b342a3 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -96,7 +96,6 @@ def get_user_payment(self, trade_no: "str") -> ("int", "float", "str"): class Payment(Redis, MySQL): - # TODO transaction issues def check_old_user(self, user_id: "int") -> "tuple": self.cur.execute("SELECT * FROM payment WHERE user_id=%s AND old_user=1", (user_id,)) data = self.cur.fetchone() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 9b4178f5..e8a8d4ea 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -64,7 +64,6 @@ # app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') app = Celery("tasks", broker=BROKER) redis = Redis() -payment = Payment() channel = Channel() celery_client = create_app(":memory:") @@ -95,6 +94,7 @@ def audio_task(chat_id, message_id): def get_unique_clink(original_url, user_id): + payment = Payment() settings = payment.get_user_settings(str(user_id)) clink = channel.extract_canonical_link(original_url) try: @@ -137,6 +137,7 @@ def forward_video(client, bot_msg, url): def ytdl_download_entrance(client, bot_msg, url): + payment = Payment() chat_id = bot_msg.chat.id if forward_video(client, bot_msg, url): return @@ -276,6 +277,7 @@ def ytdl_normal_download(bot_msg, client, url): def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.Path]"): + payment = Payment() chat_id = bot_msg.chat.id markup = gen_video_markup() cap, meta = gen_cap(bot_msg, url, vp_or_fid) @@ -352,6 +354,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.P def gen_cap(bm, url, video_path): + payment = Payment() chat_id = bm.chat.id user = bm.chat try: diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 082e1c32..aafd82ab 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -55,10 +55,9 @@ logging.getLogger("apscheduler.executors.default").propagate = False app = create_app() -db = MySQL() + logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() -payment = Payment() channel = Channel() @@ -81,7 +80,6 @@ def wrapper(client: "Client", message: "types.Message"): message.reply_text(BotText.private, quote=True) return - # TODO bug fix # 198 membership check if REQUIRED_MEMBERSHIP: try: if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status not in [ @@ -105,6 +103,7 @@ def wrapper(client: "Client", message: "types.Message"): @app.on_message(filters.command(["start"])) def start_handler(client: "Client", message: "types.Message"): + payment = Payment() from_id = message.from_user.id logging.info("Welcome to youtube-dl bot!") client.send_chat_action(from_id, "typing") @@ -118,8 +117,6 @@ def start_handler(client: "Client", message: "types.Message"): info = "" text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" client.send_message(message.chat.id, text) - # add to settings table - db.set_user_settings(from_id, "resolution", "high") @app.on_message(filters.command(["help"])) @@ -241,7 +238,7 @@ def direct_handler(client: "Client", message: "types.Message"): def settings_handler(client: "Client", message: "types.Message"): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") - data = db.get_user_settings(str(chat_id)) + data = MySQL().get_user_settings(str(chat_id)) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" @@ -300,6 +297,7 @@ def buy_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["redeem"])) def redeem_handler(client: "Client", message: "types.Message"): + payment = Payment() chat_id = message.chat.id text = message.text.strip() unique = text.replace("/redeem", "").strip() @@ -325,6 +323,7 @@ def generate_invoice(amount: "int", title: "str", description: "str", payload: " @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: "Client", message: "types.Message"): + payment = Payment() chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") redis.user_count(chat_id) @@ -389,7 +388,7 @@ def send_method_callback(client: "Client", callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) - db.set_user_settings(chat_id, "method", data) + MySQL().set_user_settings(chat_id, "method", data) callback_query.answer(f"Your send type was set to {callback_query.data}") @@ -398,7 +397,7 @@ def download_resolution_callback(client: "Client", callback_query: types.Callbac chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) - db.set_user_settings(chat_id, "resolution", data) + MySQL().set_user_settings(chat_id, "resolution", data) callback_query.answer(f"Your default download quality was set to {callback_query.data}") @@ -418,7 +417,7 @@ def audio_callback(client: "Client", callback_query: types.CallbackQuery): @app.on_callback_query(filters.regex(r"Local|Celery")) def owner_local_callback(client: "Client", callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id - db.set_user_settings(chat_id, "mode", callback_query.data) + MySQL().set_user_settings(chat_id, "mode", callback_query.data) callback_query.answer(f"Download mode was changed to {callback_query.data}") @@ -443,6 +442,7 @@ def periodic_sub_check(): @app.on_raw_update() def raw_update(client: "Client", update, users, chats): + payment = Payment() action = getattr(getattr(update, "message", None), "action", None) if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": client.send( @@ -460,6 +460,7 @@ def raw_update(client: "Client", update, users, chats): if __name__ == "__main__": + MySQL() scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) scheduler.add_job(auto_restart, "interval", seconds=60) From cebe1d857c37bf9d05b821a1c1c44ed3bd623425 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 16:05:28 +0200 Subject: [PATCH 076/297] create in memory --- ytdlbot/client_init.py | 15 ++++++++------- ytdlbot/ytdl_bot.py | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 5f9f9019..0a9aa0fc 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -7,18 +7,19 @@ __author__ = "Benny " -import os - from pyrogram import Client from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 def create_app(session="ytdl", workers=PYRO_WORKERS): - _app = Client(session, APP_ID, APP_HASH, - bot_token=TOKEN, workers=workers, - ipv6=IPv6, - # proxy={"hostname": "host.docker.internal", "port": 1080} - ) + _app = Client( + session, + APP_ID, + APP_HASH, + bot_token=TOKEN, + workers=workers, + ipv6=IPv6, + ) return _app diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index aafd82ab..0aa46f95 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -54,7 +54,7 @@ customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) logging.getLogger("apscheduler.executors.default").propagate = False -app = create_app() +app = create_app(":memory:") logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() From 3fb3dd614d3fcee51abbece0eb398aafec8f5cf0 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 16:26:01 +0200 Subject: [PATCH 077/297] large size detect in hook --- ytdlbot/downloader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index f57cf40a..38b46e31 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,7 +26,7 @@ from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6 from limit import Payment -from utils import adjust_formats, apply_log_formatter, current_time +from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt r = fakeredis.FakeStrictRedis() apply_log_formatter() @@ -88,6 +88,8 @@ def download_hook(d: dict, bot_msg): if d["status"] == "downloading": downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) + if total > TG_MAX_SIZE: + raise Exception(f"Your download file size {sizeof_fmt(total)} is too large for Telegram.") # percent = remove_bash_color(d.get("_percent_str", "N/A")) speed = remove_bash_color(d.get("_speed_str", "N/A")) From 61c02be8e1b77f83801bfb7e5ffb5bc6b90d4503 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 8 Apr 2023 16:29:39 +0200 Subject: [PATCH 078/297] extend auto restart time --- ytdlbot/tasks.py | 2 +- ytdlbot/utils.py | 2 +- ytdlbot/ytdl_bot.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index e8a8d4ea..0f763f59 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -468,7 +468,7 @@ def purge_tasks(): threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Asia/Shanghai") - scheduler.add_job(auto_restart, "interval", seconds=10) + scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index a1c2b1b7..3f4f4794 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -185,7 +185,7 @@ def next_salt_detector(self): def idle_detector(self): mtime = os.stat("/var/log/ytdl.log").st_mtime cur_ts = time.time() - if cur_ts - mtime > 1800: + if cur_ts - mtime > 7200: logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) return True diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 0aa46f95..c45c6275 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -463,7 +463,7 @@ def raw_update(client: "Client", update, users, chats): MySQL() scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) - scheduler.add_job(auto_restart, "interval", seconds=60) + scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=60) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=60) # default quota allocation of 10,000 units per day From 337456007fd14bfd5d6a7343491c14c0a44237ee Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 11 Apr 2023 17:49:23 +0200 Subject: [PATCH 079/297] flood wait mitigation --- requirements.txt | 2 +- ytdlbot/config.py | 2 +- ytdlbot/constant.py | 4 ++-- ytdlbot/tasks.py | 21 ++++++++++++++++++++- ytdlbot/utils.py | 12 ++++++------ ytdlbot/ytdl_bot.py | 2 +- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index 547b628a..1a6e6a85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ yt-dlp==2023.3.4 APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 -PyMySQL==1.0.2 +PyMySQL==1.0.3 celery==5.2.7 filetype==1.2.0 flower==1.2.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 505aebfb..c1d16365 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -11,7 +11,7 @@ # general settings WORKERS: "int" = int(os.getenv("WORKERS", 100)) -PYRO_WORKERS: "int" = int(os.getenv("PYRO_WORKERS", 100)) +PYRO_WORKERS: "int" = int(os.getenv("PYRO_WORKERS", min(32, os.cpu_count() + 4))) APP_ID: "int" = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index a9702871..2b06da1d 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -23,7 +23,7 @@ class BotText: start = "Welcome to YouTube Download bot. Type /help for more information." - + rate_limit = "**I'm being rate limited by Telegram. Please expect a delay in processing your request.**" help = f""" 1. This bot should work at all times. If it doesn't, please wait for a few minutes and try sending the link again. @@ -90,7 +90,7 @@ def get_receive_link_text(): if ENABLE_CELERY and reserved: text = f"Too many tasks. Your tasks was added to the reserved queue {reserved}." else: - text = "Your task was added to active queue.\nProcessing...\n\n" + text = "Your task was added to active queue.\nProcessing...\n\n" + BotText.rate_limit return text diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 0f763f59..50829299 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -23,6 +23,7 @@ from urllib.parse import quote_plus import psutil +import pyrogram.errors import requests from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery @@ -39,6 +40,7 @@ ENABLE_CELERY, ENABLE_QUEUE, ENABLE_VIP, + OWNER, TG_MAX_SIZE, WORKERS, ) @@ -248,6 +250,10 @@ def upload_transfer_sh(bm, paths: list) -> "str": return f"Upload failed!โŒ\n\n```{e}```" +def flood_owner_message(client, ex): + client.send_message(OWNER, f"CRITICAL INFO: {ex}") + + def ytdl_normal_download(bot_msg, client, url): chat_id = bot_msg.chat.id temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-") @@ -266,7 +272,18 @@ def ytdl_normal_download(bot_msg, client, url): # client.send_chat_action(chat_id, 'upload_document') # client.send_message(chat_id, upload_transfer_sh(bot_msg, video_paths)) continue - upload_processor(client, bot_msg, url, video_path) + try: + upload_processor(client, bot_msg, url, video_path) + except pyrogram.errors.Flood as e: + logging.critical("FloodWait from Telegram: %s", e) + client.send_message( + chat_id, + f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", + ) + flood_owner_message(client, e) + time.sleep(e.x) + upload_processor(client, bot_msg, url, video_path) + bot_msg.edit_text("Download success!โœ…") else: client.send_chat_action(chat_id, "typing") @@ -277,6 +294,8 @@ def ytdl_normal_download(bot_msg, client, url): def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.Path]"): + time.sleep(random.random() * 10) + # raise pyrogram.errors.exceptions.FloodWait(13) payment = Payment() chat_id = bot_msg.chat.id markup = gen_video_markup() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 3f4f4794..b79f97e5 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -182,12 +182,12 @@ def next_salt_detector(self): logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) return True - def idle_detector(self): - mtime = os.stat("/var/log/ytdl.log").st_mtime - cur_ts = time.time() - if cur_ts - mtime > 7200: - logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) - return True + # def idle_detector(self): + # mtime = os.stat("/var/log/ytdl.log").st_mtime + # cur_ts = time.time() + # if cur_ts - mtime > 7200: + # logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) + # return True def auto_restart(): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index c45c6275..75bd515f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -115,7 +115,7 @@ def start_handler(client: "Client", message: "types.Message"): info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}" else: info = "" - text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" + text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}\n{BotText.rate_limit}" client.send_message(message.chat.id, text) From cc459f75c62257b6941f8cff6d9659be9c69859e Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 12 Apr 2023 19:06:19 +0200 Subject: [PATCH 080/297] add worker data in ping --- ytdlbot/database.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 1075f22a..e9067311 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -26,7 +26,6 @@ from influxdb import InfluxDBClient from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS -from utils import sizeof_fmt init_con = sqlite3.connect(":memory:", check_same_thread=False) @@ -74,7 +73,7 @@ def __init__(self): self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True) db_banner = "=" * 20 + "DB data" + "=" * 20 - quota_banner = "=" * 20 + "Quota" + "=" * 20 + quota_banner = "=" * 20 + "Celery" + "=" * 20 metrics_banner = "=" * 20 + "Metrics" + "=" * 20 usage_banner = "=" * 20 + "Usage" + "=" * 20 vnstat_banner = "=" * 20 + "vnstat" + "=" * 20 @@ -143,14 +142,24 @@ def show_usage(self): fd.sort(key=lambda x: int(x[-1]), reverse=True) usage_text = self.generate_table(["UserID", "count"], fd) + worker_data = InfluxDB.get_worker_data() fd = [] - for key in self.r.keys("*"): - if re.findall(r"^\d+$", key): - value = self.r.get(key) - date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.r.ttl(key) + time.time())) - fd.append([key, value, sizeof_fmt(int(value)), date]) - fd.sort(key=lambda x: int(x[1])) - quota_text = self.generate_table(["UserID", "bytes", "human readable", "refresh time"], fd) + for item in worker_data["data"]: + fd.append( + [ + item.get("hostname", 0), + item.get("status", 0), + item.get("active", 0), + item.get("processed", 0), + item.get("task-failed", 0), + item.get("task-succeeded", 0), + ",".join(str(i) for i in item.get("loadavg", [])), + ] + ) + + worker_text = self.generate_table( + ["worker name", "status", "active", "processed", "failed", "succeeded", "Load Average"], fd + ) # vnstat if os.uname().sysname == "Darwin": @@ -158,7 +167,7 @@ def show_usage(self): else: cmd = "/usr/bin/vnstat -i eth0".split() vnstat_text = subprocess.check_output(cmd).decode("u8") - return self.final_text % (db_text, vnstat_text, quota_text, metrics_text, usage_text) + return self.final_text % (db_text, vnstat_text, worker_text, metrics_text, usage_text) def reset_today(self): pairs = self.r.hgetall("metrics") From f8fa6724dc9a982e009b2b3d440e7a858eaf06ee Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 12 Apr 2023 19:29:41 +0200 Subject: [PATCH 081/297] use rate limit --- ytdlbot/config.py | 2 ++ ytdlbot/tasks.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index c1d16365..10b57003 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -60,3 +60,5 @@ FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 5) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits + +RATE_LIMIT = os.getenv("RATE_LIMIT", 10) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 50829299..d35f3c24 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -39,6 +39,7 @@ BROKER, ENABLE_CELERY, ENABLE_QUEUE, + RATE_LIMIT, ENABLE_VIP, OWNER, TG_MAX_SIZE, @@ -79,7 +80,7 @@ def get_messages(chat_id, message_id): return celery_client.get_messages(chat_id, message_id) -@app.task() +@app.task(rate_limit=f"{RATE_LIMIT}/m") def ytdl_download_task(chat_id, message_id, url): logging.info("YouTube celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) @@ -294,7 +295,6 @@ def ytdl_normal_download(bot_msg, client, url): def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.Path]"): - time.sleep(random.random() * 10) # raise pyrogram.errors.exceptions.FloodWait(13) payment = Payment() chat_id = bot_msg.chat.id From 4c337fae415e29b58a184b2ff2db79f371f7fd7b Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 15 Apr 2023 13:41:53 +0200 Subject: [PATCH 082/297] hide some info for ping --- ytdlbot/ytdl_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 75bd515f..82e78c7b 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -204,7 +204,7 @@ def ping_handler(client: "Client", message: "types.Message"): stats = BotText.ping_worker()[:1000] client.send_document(chat_id, redis.generate_file(), caption=f"{bot_info}\n\n{stats}") else: - client.send_message(chat_id, f"{bot_info}") + client.send_message(chat_id, f"{bot_info.split('CPU')[0]}") @app.on_message(filters.command(["sub_count"])) From 07cbf8f0fb2efa78fe27ea4dcba75192454184b1 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 15 Apr 2023 19:59:02 +0200 Subject: [PATCH 083/297] use session --- .gitignore | 4 ++++ ytdlbot/client_init.py | 2 +- ytdlbot/tasks.py | 8 +++++--- ytdlbot/ytdl_bot.py | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 11572941..7f7fc30d 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,7 @@ reinforcement/* /ytdlbot/session/ytdl.session-journal /ytdlbot/unknown_errors.txt /ytdlbot/ytdl.session-journal +/ytdlbot/ytdl-main.session-journal +/ytdlbot/ytdl-main.session +/ytdlbot/ytdl-celery.session-journal +/ytdlbot/ytdl-celery.session diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 0a9aa0fc..a3129326 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -12,7 +12,7 @@ from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 -def create_app(session="ytdl", workers=PYRO_WORKERS): +def create_app(session, workers=PYRO_WORKERS): _app = Client( session, APP_ID, diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index d35f3c24..f3f65565 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -39,9 +39,9 @@ BROKER, ENABLE_CELERY, ENABLE_QUEUE, - RATE_LIMIT, ENABLE_VIP, OWNER, + RATE_LIMIT, TG_MAX_SIZE, WORKERS, ) @@ -68,7 +68,9 @@ app = Celery("tasks", broker=BROKER) redis = Redis() channel = Channel() -celery_client = create_app(":memory:") + +session = "ytdl-celery" +celery_client = create_app(session) def get_messages(chat_id, message_id): @@ -481,7 +483,7 @@ def purge_tasks(): if __name__ == "__main__": - celery_client.start() + # celery_client.start() print("Bootstrapping Celery worker now.....") time.sleep(5) threading.Thread(target=run_celery, daemon=True).start() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 82e78c7b..76209a4c 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -54,7 +54,8 @@ customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) logging.getLogger("apscheduler.executors.default").propagate = False -app = create_app(":memory:") +session = "ytdl-main" +app = create_app(session) logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() From b101ff2b927fb67e4c812487eed022196b413de7 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 16 Apr 2023 14:27:24 +0200 Subject: [PATCH 084/297] support instagram --- README.md | 45 ++++++++++++++--------- assets/instagram.png | Bin 0 -> 1581864 bytes ytdlbot/constant.py | 2 + ytdlbot/downloader.py | 26 +++++++++++-- ytdlbot/tasks.py | 83 +++++++++++++++++++++++++++++------------- 5 files changed, 109 insertions(+), 47 deletions(-) create mode 100644 assets/instagram.png diff --git a/README.md b/README.md index 12939362..35e4192b 100644 --- a/README.md +++ b/README.md @@ -4,41 +4,42 @@ YouTube Download Bot๐Ÿš€ -This Telegram bot allows you to download videos from YouTube and other supported platforms. +This Telegram bot allows you to download videos from YouTube and other supported platforms, including Instagram! ----- **READ [FAQ](FAQ.md) FIRST IF YOU ENCOUNTER ANY ISSUES.** ----- -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) + # Usage [https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) Send link directly to the bot. Any -Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/supportedsites.html) will also work. +Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/supportedsites.html) will work to. # Limitations of my bot -Due to limitations on servers and bandwidth, there are some restrictions on this service. +Due to limitations on servers and bandwidth, there are some restrictions on this free service. * Each user is limited to 5 free downloads per 24-hour period * there is a maximum of three subscriptions allowed for YouTube channels. -If you require more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your -own bot. +If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your +own bot. See below instructions. # Features -![](assets/1.jpeg) - 1. fast download and upload. 2. ads free 3. support progress bar @@ -50,6 +51,19 @@ own bot. 9. supports celery worker distribution - faster than before. 10. subscriptions to YouTube Channels 11. cache mechanism - download once for the same video. +12. support instagram posts + +# Screenshots + +## Normal download + +![](assets/1.jpeg) + +## Instagram download + +![](assets/instagram.png) + +## celery ![](assets/2.jpeg) @@ -57,6 +71,8 @@ own bot. This bot can be deployed on any platform that supports Python. +Need help with deployment or exclusive features? I offer paid service - contact me at @BennyThink + ## Run natively on your machine To deploy this bot, follow these steps: @@ -162,16 +178,7 @@ Type "help", "copyright", "credits" or "license" for more information. ### 3.2.3 Setup instagram cookies -Required if you want to support instagram. - -You can use this extension -[Get cookies.txt](https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid) -to get instagram cookies - -```shell -vim data/instagram.com_cookies.txt -# paste your cookies -``` +You don't need to do this anymore! This bot support instagram posts out of the box, including photos, videos and reels. ## 3.3 Tidy docker-compose.yml @@ -265,6 +272,8 @@ https://dmesg.app/m3u8/prog_index.m3u8 https://twitter.com/nitori_sayaka/status/1526199729864200192 https://twitter.com/BennyThinks/status/1475836588542341124 +## test instagram + # Donation * [Buy me a coffee](https://www.buymeacoffee.com/bennythink) diff --git a/assets/instagram.png b/assets/instagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f19a0e3907b3e144213636e8490f2d3b0e42c9be GIT binary patch literal 1581864 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�#5?G_uOH|x{SX|Sv3YM9<5Eta-n#5TbM5A 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 z z*R9&_bg#BBp1;{Sxr1L{de6A_Kj78740Z2}?CY=3^HYn8;g0};U+o;&-V2ig zh}NLUJisH+My<@7RXU$m6czqmzjGGD>spAIQJeY|L_?felhdg0oamCFU)U?^u{mWd zZ94)0t4_2IV|@A*K9&T<9a5Dfeh=zRo zI9F)ty|n-#k*~2oYYtVx;E{vKsa*IF{OJ4erI0QAV7!=f9WSG&9_HM0;J_R^KAl9Y}?&9jDszQltqpizuH<90G8=a{h0?>1Li#uPTd?@L!@13SbioB_OoCClP#2-vEKE?(J-!g}`hk1G-2lwh zfRDG_1e}LMBimoQVu*B0*u91T&9bInw+&i#M|>1TlY-J@zzp_om3D+(gt2WjDmRf4 zt*X{!9m4Nkj|N^aV%9rQF)Utr@BH@q+l_&pa={@&XcF>Y7wPgOMd$|~e>M>!RuKus zanwXplnX0uoHedHMWa}er7#Hz7aIN|)Ji`yqt)V~mtJ+q|AU zcT{fg5cxP(vvojKg5jJ{LE4GPM4@Q1Ii*jDnk?;`E-A zP8Io=fBI*e%m4A;>*TPacz*sx#$eGp{iSp~%?P^|o;Y0WSq8VyaI|tqP!_{qLYz_| zyf{=gb{rrn@r@rPag@V^hHK}8K&K%lQ*zQB$r64$$H_ujg<<8=$BonuCsQEYnY{kHJA zQ6hJh{nB6jb7xrm(xZ1@zpD;x7oGVT77X^C=iuGjdXEaexpw_`2M$)B_2-*1f7MST z52{C9JN%A8qQZ9#4U)eZkM8e82pJ|Rdq6}eFY^H!$Nuc(O$QAQ8i)@IfDyxJo*zk%Na zyo=M>HWN8To;NpXm=si3ABXPG_FEmxC=x|tE5)l(q82@Xje)SDynvYz(0sYpT(%7b z|Jo^bIxFIV5g_R-$IP{y7B6adaNk^5$r?Ek7?*9;3I6k(;jb%u(yHIydCrrJbK5W+ zlg*F;FIjIsavpJNfNADKUE#=b;vTwx(w5SbXD6Ev;-MEWHSc@xJpBRx{iLV~3bZl( zs%@f@96Q?E)}`rtb(#P4jKlh6-aac)u;|LGCC}aCfC_Ks`zW&YK~xQA3#FGc-`saf zpjBpDS=LF6&vG#3+#lLt7}dAhu{~`1R$oMw=o3c^h597N0>xOFy>i^6XND+e00#rb zUAwjzo#fy)&g0Y$2ey8RWYjORhQrdYp4eR9HRF@n7Ua zFkY)IGyrEVuq$(=^5)l**-linSNBdTbAI}G#yj#HIjc|TL8R7cPDTBDm}83L{%Gcq z?NFU!S;x@%%V_Oo<@duCXOp}?oK2!mx8Wma$1Ri zcpd!~S=G3R;zd*7dCqy6tUF9jlJV%GU$tc`RZh?5+k^7uz5Eyddh>TmK>NELpWXmR zH0F6fwS{-t=0XOHPas%1_}tXWj48$D0A?nsN_7v)J>EC_sPkDBVrVX@B=&BN$XpV9Z^4{YA!J@AchjGT}DMcOes z%@c;d+({PBT=?w89Ge%?snC$I!T-$9Z1i#7TxonICngg)1E0pX8297{_{k63wj>U%=j9xgIL`0cO%F!-HKKX*XBA?+<@p)s{cO$)cM<4nEr*HZZ2{W<-{)6o>c*qrW zqjK{@g`*dBYx2)TW1JeVUl;KJ@0DFY)xB*D>7Y~0*_OjeZre1a%N!NOg-=-YQy%^D zkWQ3MbT4X6+jI$f-+2f#HxHueW5#^AnD4>BIROU!hvR+DD|LbyTp^QlMmGj0{hfu^ z?elRE*Pl0S>)go+_3Uw^U=-@_U#-u=YCsH58iH`aQD_e4@XJ#(I#YTk`;sGtvqSsF z6D(<-jRxLSAKQ`zc5T(jLfecBLbXT{ob`E!SH-F+n0nl2xS@->Z>yGwnEbU1*6?!B zmZT_nXidaBJnqIHz2l$WnT3@45(M*$jXXM5b$2@$qXBJp9S^<0MpwV)FBGk}2WF4R z)CHes`e%gIy2b>B!RQe0md5ZTstu>L#S%_wyoQWKpWO}@Ytg}?!Q~c~9JF%zI45$^ ziukq2#L!&6y;}8tGDsBQY%vY@LlZ%R25o;Ihf|#km6h2-8g4`#M&SK4j_Cl$eRk@W z1?M;MQGl9zFKk0@?tkB|@2#T;(uOb^^oIYN3$+ggMk@W~$@JBv^%sHl%G~#ocZF*1 z?yoy0Z3LF0w%%@-w(U)l$tJ)%Po{UHD1>T7$0U3m0~hjc(wVTc@`trn#~6JG6%gCn zO$^TlKtTQW`dfQ?4YcdtNv&0deh`)z-pv6M*BuN$|TU z8@me)O0Ib^g_A@4NyfwT!s_W_m%1;n1%=WU0?Pey!t=CM=Px=1gAz{xx2kg4Ui_Rp z6!4=IziUOrh|rNN@uH=um?q~71^+ZfGcGs`R~<(wCztmxl~X6CS9^03%gM8x7#B7# zb51>N)&1}N!S8SWpa1Kh4GiADniBgW1zO#26NvZkKT3f~>{Y)+pXQ6%DJRCunCC~W zY+i4rm?Hk5$RH=QTr0`tNvpcC=*`D>Zf`z(@?i5QA#k~z6P!eQgh4QWdH?>b-v6jA zV}JJ3&nKk#dW!VRl$d8{52mdN|7|i;nD#p{!SS?*2RF8YHAY{*mQ#59Is6hha#~Oh zjU7Q{wT)Ak0qMdo4#MYII6kN3baA+ymDG_kD0<=Dg=KW%RkLy$kD~CQ0srT!41v6J zy5L(b@3<%df7uH!T>NUiNkOW+BcvySz?Uv{dxrdme`3JD$epL0U)xva9fsLNMp1OW zq2jU-#Ny==lyu?+UB?9s0ldLPqQ^ ztXwhY<=vY4)wwl*C{lBEZ&t@CIgKF$!^P3-)TRY#eW)&-Ya~ZG8)KpIqeQ>!Lj`88 zMYnL^GLO=otbCmOm)A=P&?{<#v{X9gon8X&P(yhi^q<$PUQ}U z=WT1ryvlG1x0QFok7W3G^Q;P1pTe`MpMuFzaBcs3hLbI)D_@bVSQ6e|-0}`l2eyXE zEyG~sgumDJQan-nc&*$ooJ(ZF-u~6azvHL!zgXeqgy&oWhdIsRVT|FJ)5kN89Yz^k zQ}As+adOr;rmFWsM~*N)hc+p+-Q$GQuifce^q-?6(M^qGZNcAmICE&UW$E>k!JUP( zNn~65zJmwAcHy}`G#>R+o+GeW@qV7cf5KV8qu)a7>4~#lppF<%`IT;$ zC+TVQ^E{ce-<-NwJr^&$oGnodHe2wD!HHHg9_t5wGf8Lb_lL>fqwRB1bLPrk(INLc zFaYj3?Be7zCQg%iV_rqGFKt1KWFI|#5KJO%&2PWaUXG&1>T&Y^L9*52$|cb^J)C(o zPM69hLoG{+Lm6>WBQ>PU9UyhSX*Zw@O3tbii-ysf%bb#a16)=*0knA{wdOSq-Bn^}@&SmFgQiuu||Q9wb&gWhTn2cJKscN0z8^ z4M#xKGCJ$uVeJ}c2OAh8b}f5TDv+bY81$kzk>Ex3&j$CIetm|(jS+V&l9a1eZlG6RU3H#gYw-{KAbSZB`KMDNX87{{$7^@Ob9=+Jy%`stR z@aD~rGI(NcoEFjC^^!6=5}ne`01&Fqll|eH4~8x&59*vqlb9-Jhb>AJT+t$zTQ#vy z)pd{)4lc#S}iPXFvP=^UaM9?`$rn)JWn+_{wF#S#kyj zPLuvxb+;N~B{43&IVb_@R~=3Jv}Dh(QvQEX63Hj6WdFba>R(L`9E|?X?K_*#|NN)p z?7Ek7c;n-ng+F#oaYoSRMFM^HQ3)@r|Fe(F0Ti58#R^iGdCuj&{in!Ie!*VN(!tE`5A|?EF&O3EkDe(h7Wp35?FWY|N zbR?@4nAbQsD(eMpOftjj8U9B|SH@~qTDdSt?&B*Sx@4Uc^=Y8{c6?PA+ zTy&IjozPXUABxKu9?_ZWeJ9FyHq?XJS`!}}CueYd+XZHA;o8fYu#`212c9f!I*<*; zw_H8z?kFhV^7P;3tzDnt=-oxG^kuee8JP8LaHh;|JPz99K2F|kkujvhEBW;9>e%Iv9F0>-IW(-;o~HzIKEH_UJ!2q??Ag0`ZL^Q(lzX4v6kq+sf9eJEPF*aTGC5pc$vy zvr4P2F$$_j{pS`C8t>UURh^=g;QMh$tUqoWob6q5yf}G`o8+rxrIWE3VvJsOOg}5< z00K$)MQKjtL=MBVXizjye$9z0cyRAw-HD&qzK9=-4QN4f;#@B@wuMI)gnC&(SOo$cE7wLR?X`!B}Gx{$%H ze>=Vchmy1&6xH&g2%wkcfjLcPlk;-KJZPH}8gS~?Bw%K+$xqYY7exe}6``ligkcWW z@E8oX57Gf%H@nuj)L~=3__^>%S zSSmKO^}2RXn{%f**v=k4*?j)1g`IjPORC()k>ktF2w%V83S zIhBu!(BS;W!(aAy@~_~##wPrrsjx8c`#Z8cI2*Eoi?i0E({N(EUq6F!wt?yN;BD2w zIbVRw!2UYtJ=$M7Li&yQoxwc(Gk8VO%_&LMjgIiEng78!Sy7@J3l6iTMtjlNo93qZ zYi_~mUPd@a%tRA52IiKYkY(TpTdy{D;kC98g5S;MsHq&nj85RZioyxKqBcdkREGt5 zk%h}qRNcK_HwIq3fHN<_)WX5C+r}BFzAauvs%8T62&{U;b;B~ahQo>253c1*3WnNM zFM1mSG~U5$jK+zkZ$-Y%w&Tj7(ZCzqQXiIGipCc`ab6iQ@2|dAUI2`?`=?!m4Ie=` zj#9E@#u1!uYqQ_#M5o4Y=U}Od%GBOqd(jaG-w@ZlyPnT?HC1-#yker@qojp6js`APS5U! zuO0_|bWx0KVCSfh7l_j1sNR!XEgS9ob|Ka8?HPurik%k+6i!iG5tre2_);&auQN*Z+04r}Q6)oz~ zTqd=BqhrJmMC$Z#Tc8Otu&?63(eK@){>xOd1mSw)FZF&N{J+p;q7Dc93zhzxQ;4zr zKD4_5fB)WJ@-u@#2rtrfg3KF=RgKIkTyd{ikxhsrv8Q>at?W?FF;d&D%nO%sbhO}N z=!L|_h!_h5lw2@QauBgbk+oV&*h*#?o5Qnm`V<;Yatbt~*KESp82msVQ?CO4{)h-S8T`o%$N7uUQn&y3x?JHu3x+7qriAqTN;c=$eV4G>o5FQ zz`ucID@%55ht}zXzv&U%aw+LF%XzhN=r0^CNAT*;JoG%mS_vu#h`GJ%E08GD#?5M_ z93#UMDmCrbAaJYez1FMpi-7DwAS<2r*Kee<~N>gS-3_bm{Il=Lu@t$sO7GvwvSOg41RogG4 zEA&QpdUpTb@NteE_#fGnoRU!gX-ztO=FrJqN4P&4XB-3G$x57FCna0nJTvdqu-RNczqaWdu5pqW zOT5PX1jo$F-a}ir_HJy>pGU<#f6)Sq{$DuayzCst%FnU%;rv!kqo=*ghuu% zc+z1G39Hnzm8>!nlHPAS)$1$4nz8%o&9*~awXMUrI|#uxxwXRGEZkfy0_Ut~-qYug zH(xeazW)4+&7c44Ki%B@;;Zsh-QHY1mS^ZjIzth0MQ_;J^d=|8oB);NZQ3UQn?Y~d z+I8_pTQ5G`e02GCQ6CpKU$#&*$65#H$w>*ayYO~&`u6FfXh(bqxp0yob6y39D zIV%G1QsX6`((C$sp>11aE4`0#ir!HW8sYTWZM~8>9N*)_Mn^sDo%nQp9Y@FTX!DSq zUww_`j(qNg>FaFy?bV(9T5@#zy}HM_Q2Q3g@adY2FUNIzrq7}a=K;J;c@7fq)n0v& z3lfr-m8K+mZ|+Cu682x}*`XxXIV|6ljC-Ox<7t6AGH7@}@Z(YPBH>SO-jtuH_9ll< zb-}TS9OK~ryxi{{-ra$dgGq)QOoNTeR(RRJ#_KF=moU!SJ^`g^XB@am}LlBZ8C;p1JfpDOM~k&qBLGcRingU?)NHSo()9) z)V@KJ_9{Qvv^!v@9b<^94)Z&FuEUg+ub({#KfYHXQyJMrn#C;jcRBU-8%}t@IVJEU zWB5s9>?Hjovr8Um;j`!FG5rG|W3mnyco?Da5zZv=1czsw!bp~@oKyH~y!#+NNj`be z*>`1yZqT-x8SEx-ILzcRw^Kl+FMP<7`HU)&7> z3fJ-%38<{)nE0jOoK@5&E8YlC5>^>Y4rAyH92UCSW-FH{rM;?VA!b+pX#!{KNwS$a6tB z>#wGWRbD)V0n907n?X*zAc~dMc1IzD)3y5T@3lSTUP2ComcFMT4)5FZwZ2C1^lkDr z32ofRL`(TzY295sXWb((d^st{ZVTTm4%db90*ax{a^qfjvBvMY=Mbw+aOHYR%%Qg z7$;qiVWHJCikG_J^JQg>K{hxT40tH=R?w>Rt+1b*Pbp5ocY=>Pq|*@?g?m_#z2$J_B1}9>Pp{6YayX!nls{c}3O(9A7VzuDqm)d5g4UXFuc$cuq zc+C67vEmEMy%XNS|1^Wx3oB41Te+;Z@&J-<-p)Iz{*Q^ z*AGvoe~Kv+&Yy-O{2QOMl?#pN;}?0goym|tQr;^E-FPivz9RZM)Ug`qFlzTeof$&!%yf zQSV7xqUPkMaE%5oSkP(P6NQBV3ySvgAvKlWl2S3c*)$=g`RbLqz zCmGYCjd$N^t6D~*`x&fKbCLt{WQ$hlZ)BF!ZR;~$Cl*QfZa=uQd-&-`yT{e@X`VRW z|Ni&$qWvh3oKI(Q)FP1bFP;;uyt*Q5k})L-%uWazN>5lBzueiTm2u?g>h6#H!Y}N8 z=4bzr-OUU~AAk4z@wMsMXn@fp8tz?b@Y=UkcyvWdS*&vz7#7#6)_9MxdOBr}9OHF) zZ6-QOHez@ZR|m&XCWZXvQP5Q`-bofAON>Gs{_t)x5gC9XmK^ac*esxF_nD_Wj4a*> zu$=tVzh{pk*#0{b`p|BI(pt2Mk1wECdqTK?(&F_n5R*Z~s8V+z41XCubV=Dth_L79Cq{^IHQvb%7` zIFqm5;%(uD{Dq~ESs722&C5>EX-4IP$K;_!i~4EisOmB17N0{M=C9xzSm$#7C|4e` zvIz{x#?;7=^^3k4UgGi8AAOW}1u$s_%UQAN0YK_sm(wvhgUfeGV|=86eV^ZDc_v$pw$*J? zU-QcToelPt@l}@K4yGZ<%E)R1=Tq|lJR`F^psu`dTMhkUIYS|Dz|e2tnV!zVpf&inGV+$Cx7N@z+;ER9lP&?|=}^{2OuF3fH#&Y#S)&}x ztcQjp8ND^1#V`=^?E6$c#i&u)o`BFIS3RBM{)jgjxooXU{KAOpRM9 zU5+!iJ?Y)!3?31_)g8(}5j#epVAL5G#e_ez*OlG}!zhLONyheoQ3{f@#rYwuJW0^D z;L8~C@W!3pZ~V1iPQbjp`!D|M|Ed^0KfC)6{>;C(`!E08e;#98aUo1t-|R?v+blj2 zi(>Y|Ca|R3e7|j56cHCHi5C zhIX%shkTI0T^*x{QP{#I&k4fKY(uH9t@;xPY~iv>@95Rx3r%#qfWSNaqf-6Hh~V01 zJM|9@5K#5G@7U48ec8RMUS&(Mt@+Hwk|XEmZ}$g$(wl}2*MuabM>tc*Z0DbGHu zb%Ka8c36oWgGoHe7NVyqbK-j8zq3uwyYVVGcH}SKV5N}~E!N4$pM5k7R##qL9PbQ4 zU}5Z{V9mi4m#zHbs$}@4n5z4P5ZSY3@X63!KjSY?n{jjZ%~KhZ#FeSA@@L4tRaxPO$Ev`|j9KSfD0uwIr}Y=%I~+#W z_cF9R5U;7zn`~b~XN>48_C+|RxThqU^UMeO>Pd6p|#!{GI>juh!quNb#3$ zbh6y7=JU@pWL;{jqs1g!8+dKpxpAwQS4xV&w-qx( z5WU!tj~bJ+ zU9U2>IVz4Db`~!fi$kr)ztvk2RYh& zjhM7i8{$1K%qzCnoemj8^}(zRf!!Gfd^TQcyq{*=TTO)iDPz25^m#?11!HF641HEh z`Qc8?nJ_-4h*=a{fDJq{%8M#jTJtA7!G(T4)xrahG5O$dj5uA8{YO6ZqU*i{N6*ev zKg3g8&K%iPe<-Is3kOV>=y%oab@kyvny~ft=(ObvRWP>4r@r2&@-Db5-(5oIq}2w^ zRF5rl6Gr9LQ}ya6U@LdQ2ZvI(_Nn_WJYQe?ygYrhcC}|-==tV(G=S>#DK7qBADLe- z$N!yXX_5O%I{?m}iqG&g9*bx6v&Yl$yyp8C@hikb&c~n5C0m@&eet+mEav!Ub;jeq z^@HtnV}L84g{I>S3@sx~tTzr|wuL$6EUd3KY)yQNzTaX)&xR!mQAEF=l=LrJAq-&H zcrDlOA$9{ZCd^f99XE)C7jYwU%fmD9hxqg}3aXsm@xG2^%6 zwy&?}+W^Cyz;il*4CC|8Sn2Tz3XbcT;v&vo{c-vOivf(>mWbKv7ZzqqMFc}2BqUh{ zem6tJ*%TE~`_Ur>grZ_{{N%HQ;gpK=F*etPcdNY^*0nKUv|^cY;W$C_e%qG_AfC_U z9rL=Cu(XT@0(){DUmhm}(_?Wc8WL6w#pNNyUw!YLG4!0+iqwSkG&=qu1JzORF(lb) zsKliO~@BXtt_h)zC`_A`v|Nfu(cXz+~*Z;-{-`6g*Qp*D0 z)(pqITX{W~aQSho;!a;7I9c8LsrNtFysKXsl)l`KH}ivuG9odM`ZaUQ{+|0#_?6YX0phH@4Qf|8-jzn+ zuRLXI)4(0xq}9ZIpUczzS^ew*UNQorIWWa0k`9MUX_?@;=&Z3Jwig9YTQnJu8K8sh zHSB9{-DnRWHz)uAKmbWZK~!mQQ(iDT9@~2dCwm18TzxhH&!kKVW&C(S`3)Xp`eI7o zgXn$5_GvXNC0ErvPz1=$-Eb5HE7+f1&+ywA7Z8?Gt-N#9_b~4f_)zvweEpll|318) z_rMqutpvwAf)SjILJw;YI((Q%(W8{-Ff!wh!Z(4BgYRB(MTT!BK*Kj9N}^rw+tzb& zK3jnG?|%O`cPE#Nsg~lkR%OWn#o#IME*!&?xI5yKJ#PFTRrg`AJRc?I^myKYje!aO z6(pA+>13%Am zATJ*C0%L%9Oyhab{l%y7yOf87)+AyaN^_vmg{P8kn|Ned4Fhfm&f=_2W zd)$XYM_4}y_Tyl?BWBuc6ON~#*JP&53T=UVR8ZbLR%`={$1-9p<5PJ`IL`B=eV@mp zI4=gn2x$#AA_afuue|H-E}##<8+QhFafE(rf|r;CLzUO^Tf80_w~$nmmqAcouO z*NiTCSU5C*qWZjd$EX!f771dmZzw7H4FYacX${JHb?@ol2_x`;+ zf5Pz?b;8^2+A4>Ww|LN;yON+@=T^f((OY(qu?Agl{=laJfUxJtND$(9E9n&n> ztHlgPDGMZzn{VNkkv&o^AbQ)jGkS{8bRomoN%U~LEp316$*&gk?w#FF|KzvZ#&v0w zp_>`ezW?EeyYGJQd(qha-4AYjIx@#eFqt#WYv(gU&RiPJI|*kxyLN90?_{9sai8!q z&~&po|I}ggJflwbm#g3U*6wFA5Pw#i*U|JDr+V*92Pc@fw*?Q+mORghW#!)D2m?=E zJM}G^!SAfj*9C(|vV;}+s3yM2aMQNJ=z)GgZm`9NqJ%FqKs+_ZGDffYqCTk}N&LiJ z0oNHacv9&maX`+W%Wx6S*F4cUu6cOU@b+llcr@nmNnRf6S@Zmwnp7PeHK2B3gvgQ3$eTq)-X?XDM zSZ#55@Zc%0Vy7RfL;K+YJ^-nHm@>J8XJiV7GqH8LcQ_rat>vxllXpt(m0lkBp|d`w zvyK6pF-okZk%{D~Ep^w>L3wfWsXrLTkkvzRZB}960Gt2S*KgN8=#_T{U}#ge7=a!H2vF~Y2e1J=kd+Pkt(TXnIbz%zy?&*Z2* zLj#_t7Y!+di*j{G-@{2cV*)c?>Zd4Ar&e!mY^A>*#dea~Udmd5v*Z z??-#eu?EG9mbL6f1~$|#=I8lzoAdF*Ge{rA!))i?Qd6r?#A$raT@KRX+4c^~SV z@m|)SdGpP9j1FyAeB07nYv?iQ&-SW1X9z7K!w3+cgw_c)nDN{}SbiIXLm2Y*>Yf0n z3@@(C`|s$b32q2zBh(@82FY>xQg6=RW#nO9j67rDCd}vl^de?~ z0me6eoPLaf5k@hfSvhAENl1CwF~o75S^YYn@j>EJ|DJkS$O6d8Ye(5jle*HVY zvHN%at$#aX(Usj_{>%SY#-J?X32~o(dVRJ(5U#CyJxGAH%4wW^n#a+VR(Zd5?Y+E{ zF7K{gx{!hEldw`7jKPE^g7K}CDU%Kbf-rv)%vO?)6WUD1j8Px|;0J>r44nr6_Mxs> z?XJy~10FaO-`zy25Vnz@yr8*s2_Uyx4a?$Z^`!*4MDVe_}cAQ>@ zcXLL+M>u*TZ#}N!)lzL404h)a`EsjwDGrK zgOEO&70U$d4W>6hdG!;F#(*sf3%u37FSkMB-7%DmO@3r@kXO(5E( z)CSH74`AGkBB~Hrnb!>*;irPsTH!a2hd;#Q7-o6S*cJxwcn2DB8V{q9N&dcfEAL*) zJB9ka3@~H{iUvX5mL53geR%&?v3UfI4iCGR#SX*6>x)@s%#d_8KEdmV*OIMo<^YJF zRreZ#-^16rbIZ`*!!cIKZ$8)zJ=%1;ZLG@W5yC@&0{kRz56bCD1^_Y)gNC_fw((TI ze!2gkehOEo!n-YJjK8-#p-Ie}p+7V+7NZOdHT*GrWL$W`Sa2(t!-=5HX#P@OreYsF z&RFM2_EE^A4xT-a@(_8Dw~xew%MtIqd(4C4ve}XaZ=-mGOR$Y$BO?ghOzecf5!j z(tJ_5=x6wU-{`>yFV-#wA+UfEojN`8W_|GRZgY$g5w2{tzFmyCjaDv28<(Ra`Ceum zbIdr84cm~#M`6sYsxcB819-Ig#Dd2`^Zx0KWOd5aJqE<;K&!k#7}lP)<(cS;=h*6# z&1A;AF$pL4GUlO`i5-}7bEd7(XQD4--PWwn9z?Tk>v9w}Kt8>Gqxtod-K`c%EPQMy zNTLfLcvxGvf|=Zru`7zGH}deAo?d?_Wv7d?GDF??JcAEWgyU^_67lpox_EVWrB(Nr ze>)kdEpgy{8J&zFpx78=#Oc)+F_M;9FlU zB|ha;MZFgRPu&7ITdc#o%sCd+7L6EV)!jWFPZ0641$Rn)xyJy?AcK#LPa+rf9&A${ zLytb708iQ4g*U|a9!Gd&R|i+{BHIl44y{<;Oe4(JG4xfx@oEwDNvB#ltr74uKf^?bQrpy9C2RXnH_(xM`k~Jl zL#D^#c_9vV{6klva=blO{n3)f^k6($f)Ov9SUu)Ja97^cDPMi2Z2>-wgiqy|tH(fP z9;}TP&D@Vf=3T}N@6EfF17(T>lkz>8XZ@LLZBSk1$UB9|-*YrGFu7ma-b3%khFGJ< z#5#U`|EkKSws~d$4qUC8l_5Xe;boHg2R_#>22Q-xT{s0x^$kqxo}LL4Ggf&Uo@Agt zI-onzNqRzNZH$h=t+r_po*A}AKUcObSLfnO7vj;j$G(g>=FyDpm(uVM@{AQ2E^kx5>}syFqlj@8+yY1F1}yDs0FRQ>L%4z|UE zYaAi3EPH`*4aDteJ1UX1`+Jo(MdZ!$mV)-?oqx!FL-=qA_}r)Iv+d${ z|GgOC55ML%o9qS-APUW|wftnFB{A{(uEem&vy;iC*5xp!Y{ z>y!x)`G`Wg>(BLXR7vr`* zmB)Tnf+>Z^Rstt&h~YDl`m6tX?${SoT+UxeU}@YOW?->Z>so;mxeDJ);JjLFo`dfH zkN@3&yZb+W=`Zg7 zy|yfTn9@d>u%+u_3fk#@KacrR(k=z_|>|Wz?CYr{fX45+0|-P!8||%7nfo_}JR!h<3s}LC$KSl{o_BMn5yAE0?mm zl)WhP6>+W~vwDFajU*RFAQkwp@~!JP`Genv(-MHozM5Oc*9i=gyqlo$`so*cZAKmW zzMNJuVyf(UW=hMJZ~OMnR>q7QZ77XU5Wj_!5kA4(@2zoLZ5ynfyqT20wGAAvf4^?q zmVe*eS9#ob4_tDq-;dQd9?lFg*ZNm_6JGPlf^~y^(a1jQlr;j)zGCgxo@vX5$2Xo0 zFah8>K^0Wy0dv{LYZh!^Nw6Udf+3jYN7u9Z4Nx_8e+g|%P`9OLZucG#>%ZO#sk7M{_`m1AI*|7=g!BG;@8?A!mW{xp8JjcI@X)iJN#I8DOW?zH*?<`Z zR50kz<5%iFzJRyn#ka>Tz9lXVoGLKIL4XT?Rb$k8!^|zHR4%)6IZ&I^%ih z9izz2;K9Rq0icHwP|It}7Q+wTPLX#qA_L%TOUpPuo)6Inoaz*?Y9GT8Jfbhh$A<0Q z^Y|?9$W!s7XSIzNRl{>UM6_L7;OAaO74;L!;Mve|x+MN5Mx4{N*7IV=Jd92(0@M#z zn`6NG`RBLBV_?L!`r>|`SC8_}RHv3`C4D6h9rp z!-ulOV1%wrI1Hr?6^ER?r!&Ax@V8$S{XN*#9$H3RzcJzRUM#j5jTd|zHJF#D) z{qw;(%0>p9nfoi7=f>l_ie@_zTFH}%@_e-glFL^w)c1Msw5<+K7){6?VZJuzany$) z({^cn%Ja%tJbYOY>-rzejFfP9CXbxk(bv7oS#;6!=C#Wi<#^;CWpooSPdjY4x>x)j z@oMfAi||ffK_9%^$z-+RwD~RCK^J(%a~{skTRqV&-Ab`ftUt_?ZTM9nXG$A<>~;+1iCKg9Y4U011{e%>ly(s;A~P#nt|lda9BwbmXLZaKTX*WRObn?=9jx;C4mHe6MFWiMRSs($o; zPwU=)IfPU{!G(;kYf#I;6~^kz`m@(Ves#@*N@J*r4)n7vhRqpAdD*ZC_K~{kB$b|ZoNebXL(E0B7o>;2<{-u&S9O{)eyp_ zuq>io!qChPLo{2HP|1+m`l~GeOm=Bq-~osE)hXxPKY6CFS{r z=-o2W%m9y|41NT)a?Mcj%8%0@Ud6Y2z=zPI`kJ{HB6GL(Dy?ZTJYgX=#Y)^Rmycjq_|YLRf8tsnaG=*XVNG?1%nsQnNIhxq5}?r%kFiIFlFMfBYS`8 zm;U1Jzxf~j``y3!ul#Enpsw!z>Rgj|f7>!mTrt^HJ!9ChUK z$;CW`!bJ?3a%k1-T&IP7_=Ar#7+n}+Esv+4`H7$EnDvZmxAN3!VtZbDB&$MjMS{(Uj;>#_%+SHLGo z?P-&;p#~rLjNE2SQg-Jb`kd!|_XC~GcBWTly{>VDwPm?2zN{}{fyJqD&=9o`f4_?L0{&`C*f65gzyIc3XfObiD4 z`&!#KH)HDKJU?d3Py@qhf(%2-G5(J-67V)!iz`+dQ+yg*V)MLgkw_g7zhVJ}vv|d+ z2`UC#3qE@^C_M0@qv4uYiQu^};~y6&f_az>>>YVN9)9s-3Ewu}Z6kfRty~Y{Pf+?G z{`r8xIb>Y8e10iE@lgFa2AGsLaU*y)3cibcC?NV4E%7SaymNRH;M4ay70Cdf#Q`r+ z&N7Hmn8XX%XhoUueKi~^4^EedP+MYp2T!5g!jpU#cz|Z8NBImJJb@S|tRA0Dk)hyG zCUGoti=(#1(TT8_0%@1Eo#_4u)E zebHO*Z5|6ecT5`kn?cbW?m2qpg`zxj{2g0*Yv0ATFJ8NPY4^4FTEGduItpn{ePiI{ z$y49zzXe1zn*#Q%_MmG-WLxyb7DvW#Qr*T9BM60h;v>}_o>Ujlw1qmFHOCk;7Be2y zS5KaP-U1Gp@;90xiqBc*L%#)VE?qvidvqrw*Zn(f?P|MY zv3ah2`&%u>q?om}YmPw<_Ja&2anIos+AU>nX}nUN!%O`%C*DT)XWEu=>eP)f4!nFJ z4quB`;R_z`XV4u_yXv-RMxJsX%`3+ zAQ7|4Rv(-RjY67trR|S1fFnsT+yXO|N7}Ntln5!$}4-Z0cF)G{YxbSG56F7)-pb7e$)O_XTcCI>Py>dLly+h zJ3}93*00`kABd7((ogdpEbB^^)dt_)uHHox8n$^}PyhB@9_<@LR%1P%n4s&`ygYWH0AXYx#KU63EZ932lH`l}PBG<{pCHEX=@YX3ot)8~&fWETtU z%wZn2jew`=R4+529hKK&3Y-{gkcg&@oAc>Oa6PBU1*^IFUi^6$W8;ZOZ7n<{R!@0I z4ZM2}&GC(Q22V1N_;~jp-5Yt|VmV533LsBi@w48xWbSw?(-0~Q*E#k5Fi%P*0^k9$ zkHy-CRtrT^z&y=h$g@x@`0dY8p!gn zfFh0QB<3x98(#W zC}`rn-DjU&ANN7tub4T7f#*)k(Iem!Sj2q5V7Rms?g`(7|I4|A8MiNLYNF5s0@8!S&v~PJKx@eR(0FnUG3&qC^nv39B*K|aW5WF5eOK5-4F~OHF&QZKPj|2_4Pf9{X{Q@h{%=m!};Y?WwruFu`;39hYVQ!Kw( zJ%9MyKeM~iBuPNF|KCcM*jQ&es?|y=VV!~J{+Uj$ig~|Cpg8XPaRS9Ruf5v|IL~Jb z7!M)=KPeV>V(8)lee3qq&7L(17w(5&Dlh$+0JsyTAK z34BeOb9?=XKjAg6)1G0u@g{TY5)8HAb=Eh2y=m}xm34Ggt*opEO5CIYf;CPFZW^ zEU2nu=FEhV22Hk^#YZw1XtBjrGRFGp89;`eUGg&7Y$Gmh>aq+~aSCw7e0 z-I_o_;gO(jt7}x9khz(n3=ReY3JA~0nmxF) z4d8q+gWh}h-R`{HUB7;Pcjbd(-o#_Hg>hZGoJa^S6cquBKg}bN$K^ZmL!LW=I#N>b zY#w{$1D-pd=NZ8$d?`;6+Z({MDMSQQ#+-B2Pgycoh|L35N<03?c%L*{TTkz&Z z;cT{$1+o5tA09rEZMf(~c?{~dpz&sAV7YU5$uIJQQy;-8MN&WNQ{Ipd!e`XGyYncI zk?x;QVL!-l@@x!ld02qe*4_A&m3-{ysB4BJ;{ffWmvf`kwN*IJ9flnSeJ7tuGqVS$ zz@yii>kgZTcyZ#ZjDHV{5%l0}LA`^6GK;$3&-?Vr$%;u$6wF(<@|1~|z<0hy4@Nriku-c3Rl?Ez-jU|SvtV>gI)ehMQ2wCui z(X?;d%FnfB^3>D%wG+i|Cy(5`d40+{dcW~fC-V~FJva-e(c7HDHCW@t5|QYX(hEAk z%p39f;KaQyws0a3Z(SZmwtSy^#3RoZ%Hn+0hiAjx$l~Em3>F^vjci4h{O&_pt(;LV zyPj|nRUN9Ldt;39VM^xi@#Zi)>~K;lhFTsDJW9RE5C_ zK;Q_ioU~wl@!X|&XTj>*PO5M4!;pXtRdsqngL%B3DxleF=)g{avod6~u_YDHG@t6L zdM{oU4Ey$EawJ+H^MS?K;c@H!?f6eXjn3eqlN+C8eSDZM@wO!#>a9#;2@jd}kd8Z^ zj*D67bKQ?fW);7V9pSlFSallW^Bd9jPfowfJnb9;>KC>abbKTh^wzO5XiHydv5;t&mVXUEN0py6XPQM6Bz2gTFQ0k1mh8=eXdE_O6RzD=Y4ZFh2&}z%Zg=ivir2Uz{g$p zxQY5W<>9aWmA|t4^Z)yw-~II0e`5DXe&%O)KluKKyKjHsdYq5k004~^CY<=iq8V84Ft+PJzC+{b;sZE1uZ%H~Bb_bE8n z#YX5KWcc!XnjPIK)HK=6L~YzJ_EKLgj~!l5>pjX{s|l^}30}=ur!Od4 zR=b|$Vs2#yOeR?T$tn?lc*7d#RS(E(ssSr&rIO*Z>c$bF}m%>@A4Zi zB)9>q@850O-gzZV3{XE@$`l{@co)`9VFJ z+(mNA^vf7^*5vS|nl_-;{IFI|>)pw_FGyKiHpNyMp-Vyup?K!Ax$c=UJCCEgNPL7z za{2-?8Yz!52sh?QnWwx27xyfXP)5+!Dw9l9V_XsnrGW>IyWKtXK1x83mY}?rLkQKj z|4>w3k-@ZWm8~fEgQhxGBhmKicR({9E%+E%58`7y23|BKWhW?0#u@`HfX47t9arL4 zb8K|^MoFtPjOAyIEiqx#YMfIdW;|B!GNv)0+J_&^a(Tb;BL6m8x)*V#JJe?9N$QUodOAimk6k4n24C#2zcq4}sd3YrHpl`;H$884@_l1HZ!0f5`SwI$( zE#)#=u$>Am;){>(Gj!C4$wrq7+-osHvRF~+;W#BXes_uzsGlm6k>zs1hxG^V8H*jZ z)96F|8U3C_PY=T7QAcm%2Rv`k^w)p-T5WzYUQ7D^R(#wREqJh9gCUP%`ORxrGe)F# zs3X4r;m030@1Trum67H~+lzS3!o#r>y6S(t`oVq+8c#4vmpKBC%o7rdhhuZ41qgZY z^b57gBFnD1(lPSSn(qX!wcX2D#?!?Jy4LBD7n%=l7WCSd!x?k+!?On|#An(TkepyK zgQr;F&$|LC&sMOWozAaa)X*YNGzK$SSxE*M~0A)!Yk-cnQv8 zE(h=M^7{BJei>cQB4KdZY6#E9t$sisaNyq{o{TMqlkIB8&;NY94lhF#fRu*#yF;w3OQ+)y#Taq+ zFU`R8ngO)0tYYVVFJLkT*%)+utJ7eQC3^Jfa%`tV07c}}Cy?{*Ywr)6BvP(#o~UzaW=z%>yQpf0D}tSws%H3?lQ z3nnYEZpM=&VS&+PynPbtt%NIwkj35qMUw)rn^R3Vgj#`4DFP24w{o2YzDp>#eul;JLrF^>k`FFq75yPJwNu#U?T7K%*%~|Dso>0OQ>4OYC z;&dEY2@MZoF_`d9nkZ+oXkxSK!9|r<&)JMQcM~M7#4qB9tML@4_SjDlW^hOdpM2V-nP|6n;{=cQqhg@Q0}QE=cD{({RY z+ZIF=FQ=Sf5}cDzZ9689k_)XEK5FYo z3}tsK!^)?he-=NzR6LnZ;Hq5~rsVxvClXSI;DZ49G;fFVZFjMS2rDSGAEHHYEnP6|1r2Y3aI*Cu%J5t~N* zAir<56^ju<|Kmrt>!y8&hj@SY@tM0R?YyiQd3dYnNc`|j&yPBh(1~Jjiuue5qZL7M z(w^gsl%|v$b&!3+FAodBE{6swof!^hi);LIMoxHpFG1bH!nP&KBF3|n!H6scEd%@T z(t#a3V8Z+KLNs|4jvWzyn6~d|ct%S+pOI*c0)9Ud zJh>pxl_wUE*hRd7FKIhtOY=Rt8TtStRE*_vg+9-z2^QQ%I4R81>J?2GYz+%-h7%ZLv(rW2hZ|e|6K$v@dz=OAqSA}_? zPq!xdA{U`=JjnO9^$lFwi?-3%(-S;2JZB)*Z}4TyBYaZApI5iw-|ChRdP3i7U*))2 z)eDx|4z__Cf{feB7#&UfdQp5KcpGC+M!q|@?~kE!VxUzJs@HeyriI&ZBdP&55ZvL(?S5Owr>EQ8&I|WWo}$K$g{C>=rup-9 zy2FCI{tR#D($k#MtKcV`*GR=S*$QBWxgdwC5 zeHvq_M_rH0I3g+;7)Y&tL_PCocrYf6g6jAosGh7hR+(~2UYRn>D?&O(pv=kK_XzA= zOg+kbBMnd37q0UOP3}3ib(Q-@=D+8kYkXs-uh=zc5Gb6~0 zdkI$w6;|mO817{Rc~tOVuHp|KwngjQ#a0O!6<&^z_oCPZ+;{KAz&`xwqoF@;vjpPD zR_LHH=5-|n<|uC&+oMk13YVX!&>SaNI7-$;PQd=|#~)2S+A(`<6W%Xexi-h$3gky9Ql8cR7|g;`69;Z$ zEE7a1Rbm<7IqI1Ss(a%Z7!=|yOq_#A!AdZtL2QiM3C>CF;UoA$ya{J;9%&rZ$BaBm zGm#Vi1(*cWX)E>=U9*-+nmE(XiJQ{5w&?R9?Asg^U#v{4yM8aBrT6f#N!$1SvM-bB zR~lJfXPaO0)$B<#`R?o8-k(YROYgW`?|~IRov-k)zPER_?{!yOOEQ;l1h1(#I;0%U zs?0o^>~{bZ8ChnwL4{-et+kGH&sM!hp^Ut#4jCZsjX*5nKTIUAOu`Qan0PHkY+H4gNr z`k;w7<5_}nmIIaIN4w@kxMlxMLi6_tKS7=F@sd(3`DQ@x~j)|2k7V z6b4;eCeF0Y=oDqOGD1?oRBv^`&^Z*BIK1C|{Nr3(VaD)V6+DPeiW|f8;YmD|0r_EVvYL1? z?@oAuFFcqBCy#La+vrDFf`F@MgY$d_2!QVuDDQ>r)H0PENFaIo~;raT{(dxEvS=e|tPoRTnP!MMH%rog!{rWNw7{SKz zc70wmGvM8hHdefdV~ZO-!%xp-kkM!67u%ZzTdpUHz2u~?hZ8U8r1~B_?&P=WF{{;L zWZm7Jy4&ewdm7{kW}H6CqZZ9Qe%yAo@a3bv7vbYkC%7GCxPdRhc?B}J@Z$Hw4pMmj zqD3X1#cfAtsNv1^wDI}4In#C*+rmyaJ_C37!HYaf%$dtbwQAhgpRVD=so`N*RRqG= zLf%k5dB&m>uMNjBL%Q*1P7jd5nOKZyeVtkwWn-Lus(s2+W~EMh7%6zONqFHX`gm}+ z^s5gRzwzqRbGlHO5w6rb{ht(QwX2K9C~pJf;UL-Q`4~lerLv4|hULVbF>u4*^V$as zds~yL=c7=#GG~GzZq$vL^eP8#;ZZ@sjYcXPybLzz_(J`5As$JW8jtGw?bPW9;jq*4 z;tS_mJj72A?lbcx^aQ}QdwX!N08%GHCu(f*EpJ!2<6d_ z`!b=a8$*vlqKH?tFa>lC*OIPX+11lK>-IeJXOW9`qD``AygA&Plc((Ng9-1~M=SO1 ze}#d0$qjP7PD^fB*GuMG`IRunyn)Kq>+%O!&)sWmdn&CRGy{c)wWAyXjy~3ilfRM{ zddt7?t+C2DXpzMM4o~r@QiOf@4$&4 z$LN9kA2C^R8s>*Ub$;(Hgf5aAChD1ihERIA7*NmW{u=J?v1U<&7tyYK=wKsh`G+pt z>!vatGq}Vkq|;NC8>FR0cD}3Mv+@oTS$|fZeO@uShUw3;2%_ zU;WV69+q_<6Z<_*1m+(#*~k5(rcPb>p|qi(2l`F=>greKE#JyKfB)Y9=*L%#vbu*T z>yKz)(id>;&Km&Ne@9I$%@h-m(!__BxX}_`PG?EJ_U?Pb_!$%)Cmh`>m>~i4orF=0 z_CW!_7;%oo-3c1;&Op%UI1Zg?hH0sZ98jqq_hfxA`1j7U~bP8YQOVu2vx-?1mf9SUVZRq33Zf_+JlZn^>_P8F^asa zcWoE9t%MNy!Ue9-0~6AjP6TMfM>1>MAD$MYLD35H7=OxtG5nfzC`#JMb-7ul zI$B*1uJKAx+t6PVSQCqW@>y`<3DMAgOms1cAXimuVoFKq#qzthLjOfOKbWP>_#xk$ zzbP^!JbbB+^%nNHFJONIU*7I*#vZS}d4GHN%ir=yCe+z-6w+RZZJKWQreTKfa=Rw}H#lw~`vv4$>`#Sslnx>CIWbsk3Rnq$ZM*A5}mf+-(-+cqU z{?{jyyhd=H*X!$G*n}{7MhJ?o;6i`t-_1w?_JM!<_Ox=^!`hQ>E z9v@f|C4USqWq~98Wp$on2il>rDxjZu#;ap=6=;iAqsUR3z)ujR08^wWKa_G_jI#2| z5$qkh^SMbGZCr<;D63O@#ws*Is~B5PG8!|cXeYzP#5;+{HK;~GWcazz7<}jImCC%E zM@Gk|@9VO~`quqhc^!&FR*>CrU`ywVyaFFJ4h4Z02jeNvm0;3l%Kn8`2i5a9&Em<+ zGx4ZXk-`h}ehRRDWB4Bz@<>a}ATT*`k=F^~l`-jl-aGozcBl*S7BRqh zW?Z@$eC5$!H-bfCC=x3OiRz&%m#)tCUj0m=zjOEc7|(1QVEB1lx$c{n#4CBQUwqh3 z5kdR-q@;bkJ@q$3{+u>be;mhCUQ*=Cb0$N~Z2Q_9CXO4ag-A4)P=TZR1 z;9kGI0C#mfZ9Y1D81D`M&lw_nk^fzS{!LXP@5N-ME{NU0%3%?q_7Howh_v3>RxbV<|}D7FE;qAx|;w zUh##hv-~$3+l-Uq)R@QKFJ7NH|E4)J+HUmLpHFtb@!3ba!;5Ek_kQ*9?wjxZ?Cx9N z`1@Y zdz4YBae~+ve~k~-w#ajgPQ3DG>qp<{cx3#ISA<*i6l$x7L{G24DkBa&qdQ^+jn{1j zt}Vm(GW=8Y=@%)F(K2fZ-GoQ+%d*^a(swr@SjI zJq^#L^fQooXP(FN8UDy^XbkB?yg|Rf zG5H!lR9i*GZ{t0^2Tr)t=iq=d7#ky>`+Wwt^BdgdFRyum0oK!br~aIGl9QGINQHAH~>Qrs}?`1egxp9 zzJalsh}U&Wy|P>TSNibUDx6 z%EQwvAkz!

e*pdh=Qh<=s26z4a|iUzGIUBH%Egu(|!$-;<)#I#h#-L2Uc`UPML)v~mm<&-&B-*9101Se?DT%8+C7u6Hm!G~vyaObJqV2=L(P(@{sG3o@Dx-RQz1kW0MI%Myuit*(f;e9!Kh1svBzTfv{(Ofh8+_K zdUJfQ$;XPv{ikjF%A@CDg7>M@9qSp*ns6~7LUnpjY|~0)0UV2mLlG1F4wHM4bR%f6 z*aEyr7?=b@oq<>QvTFtLWNfe%>`capIgYZrV`GH?06+jqL_t)&_BT^nuJ+92d_RSO zFv|Pr7k>Wdv$CJv{p#QP)lS^Hyt{wv^WAYqAa3!-1BPgnBJ`EFae2mu16Qh2S> z|KXqe+2CpG0^yg!l;AkqH)5c-I$rm3O2vhYPbQQ5tvrh(z@;4?wGksfNx;9_O8&)= z{US;CVFK8S>+ypWrz^7+jd03i%9KfwaAFU|oG?If0+(b30S|Dh3*~lD? zo}Dfb=5ed$_Zey|J(WhltZy94E?M0%1&`9!`z8%1WKe<$ZxmgEt3qcQx1x#ngfnp+ z4m!G)K%{gy(N6RemsxzTvI!;f6EJM~qg-!^K(XR8tULN*Grnwj-u#})chBg`I*Guu z-dX(7&F$IBvHk*E`f~YSzTWb!-0Sa_2Yf3nAsqhanDoO$#ZPj@<0+kGzb=t)`6i{oJ@8}W{!4=||X=~j8~7K~r8=0!hkrFn8Y zMJt1KunTt2I5#p9K0|}YC{HhN%;_lgxB8C`pDu${dDQK+MKREF1IhSu zX;xnGCb8)>`q}AtAKo85J&aZe?S~l=8QxBs<8GmGurMrzzZcJ&?|Prd!+CvHzo$Pn zr#zYZLPs#3Ys;-|(1*Ksdd^Eo-=ZHzIo`115iz3i$T5fTVi1`5sD8xDd0X8d7G1lW zG5War!$)~md zq-_pME4vtM&B8X08l`$zdz2K?BT^^fG4#xS^3Em zhdtQ7i1r#^^%JkF(`TE*9|wEn>|`Yh)p31i%e#QjQZ)|0=utQ`W-|;mmX4nAl+Lgg z4*_3aODU}lPWf_!F$8|bux;$tPx#V{Ir4m_-}1>5-olA-Kwi>+Q$%^ka|Qh<2hEi# zDL&x6#Z%C4oltn1wsMC{-XPDzC&MS+!r-8<$vC5+r$D>z|I~+Hjl#U=n!{&3ETKoq zoXC*SThq?r{k=y{m<5RP+bV0G5JMztn0G0v(8HLceUw1Y{73gSW8tai%dcg)`UlJl1{vKQ~r6Q-{2JVL;Y-RVAf)6^EZneb@esf+0R4gnuyfTlW2z zA1}GbbQeUZaf@B-7yMt%5eNX0?wS7(UOhB~+aS={(!4)EDyvRFq{>?Zs-Jb}{2n5# z==Itvr}vbn`o-+$#$=jzl&|anGH6W>@~%H_FL;)cvL*nzf1Li{VqmTEN1p_OmE9ieAv%E(jMp9S~b>lt%`>HYVDJt6pR0LLMeSZcKh3-*;-j&=j9~7@B{y(rQSD@)z#k&A2Z&gSzN@E=ZH^vEI6o#Y8Lm|eaa@!7mtZEBGcM#dvZAmFw61Wc7*q18)S;?hGFq;VxEJSA8MjPXxT# z*FNth^n69y_~Hmr8uz6XI)2OaRriNCzVU|1c}`X7-Uu%HXUo_V>=iUCi;1G1gU9E~ zNxt9fG-&%R%7srj-i#@rajg#}&l!0lmYEy&vd_LBesnm^OO4Ljy9AgqCe+Djfsibo z422M`&=F-pBAm^f1(yC*z~F3zW6u*5!2}X*Sjt4y-u+Q7;tT5b-lW>%7kv0eSAH$p zjboG#FL8g2JJ$Nc8=BiM(aA}3GEsu1)3y| z;sL{J@Iqb<6bQV5B92~^ONd?D-zbvs)G9j9r;sBrfqS;SJcpNr+}fib$TBDK>hY|; zzsBD19fELui}pMdTZiY2m3J%I_!HSnTpwH1W(=o*kLPG@M$NpIczS^MeA{;!hLkO@ z_whQki1wG!FYnbnfox@!5ApIs;SqHHN!y(|_MYL3Md!-J;?PwNL-)P5Gu;h;Vk+43 zmmP1?gSO-`&e*Dpucxlm_Tat1=yZMpXXD=t_vlAkc&?zgGw1Shsc+tUCy%P;C1c@k z#v-Siog7zg^e9lV@*l_38S$>v@2LG@9t}_PYLzDTUU$t?6HhK<2E~_`jxAQt(9 z!wEhh1c24-+mCLAgBBlZt5aXDXOs~G4y|9#Q{tVxj^NGQMT5s5z{2~Aap!3?{;H~?&+ahKkR~;F0YTTo^KK?He8R zOn5!a5NILoQHHTI!E+{KW{X2;8GQ}EuKsCPg$>L{qwqPU{lrb^{&wlqc8r; zsOiHnI>x`o@pv@kC3Zyas4m_nJl4iLsB#&-1nH)Dhu|?_nYC1fKlFFCt-o6w9P~7X zm8f#~No}p>K`q!8bl&Y9Mb}qo-&Q^b_4|$6dwuX4@w+Z3FW$~@i?7-eIVoHm;`fcg zlW1nfN_f{ECmWfo7Ox0qeH%Xq%ksnyFYzc4=y8A4?_;hruAk=tWr09{gAu;Rd!)DE zoFRP0a?;jlDLk2b1TV*TpFMAzV)Y?DatT;mqsTeQaJ+gL02;r&r=MQb-e+PCRo~g_ zhw_=l-RG9`R~?IG$3xJg01(%^H04vZwTZT(UyR@`riPqhGCBG?eFX z*U#{X&}G5qQ8C5LS&Zvr$ZL#VcendEK)In$kAx(0Pe|H@} zn1mRoY@|^6!!&w=P^XN3yVt8R0x$KTe2W+n(0UJO6+Hvez22Iw|68@=%jqr{s^E7F z821toXZ5}F-wDkW2?E87=H>5w^x^JN9zZ|$(?7j?_x<-rpmZ$pyBU3sS_L^s2>oCF z^8dNJe&h2AR{7r7-rxPk-~Dbv_4!tAVw&Im&YTt^c97Tu1l_#0Gp6JMpU`N^7F<+J z8;XHdO+i5KJx$2nZ&7&v-D`Ojt&_!gMsb(N*j^@_zGzbY?E1|KvUWM)90CZ)6dHYT zwfG)4TIsf*-|DW_NjNY8T3LP)T!ueI%OuE^lM;);6A(<|2Ti(;2d0FN(9i_H^N9d0 z2q*!Cz(6^Dlzaa$B*u$`4*gG9puAb}CB(q>W;m*{X}A0OSAP5pU#T(-W5^rFpk}(k2vqDa%~0d|#bstKPQ+6lpWse7S7T-Sgas@VKSz{#NGP-xo|V z-xs}QfA#IXWej?C>-YCZjaY(FZ3#Ybt>y=#38Xu7zwVfP+OTiOS2p5DfOu1c8F49Y z6zz#$u&R7j!eDA&A2^0q_7x8f!x!%Z^!;U=f_GXo61wO8>lWd6N`mWX5bq*bCNq^| zwSE{2`Lh?iGoC%wq3!yJA(zsN*Uai>?`jJn%b1BAhCea*9K}Cg_RKwVC|cna@*pD! zArj!mpY51O)oDv9cn-@)*}0!$`Lc0)I^`L>>OH$39}7_n#&N6P;|hLx|G?|R>N$Ok zHd725!{PxN_k#02NC{`Ccy_WBk9+Z@UY)V8Y{u$aw~IXz55JfaqiqZYy7*@2AfQld4GO+?UJL&8E7<=aQt_(+vU_2S|Tm7NW@UqaJ@Y}Yz%3=_M zTc=8$4c3?O=!ZuPILo8S;=mjbf5EBCcezE2YsmoSp2s|C!l~0DZ+8s( z=_?eMws_U=rwa&fp@M<(`LlaNoB9RoF^5@%5ct^qY$j0N{Z7}sRlD!pdlWvO)b49T zE8n^QVL2M-&0iF?&pXuaca5?r{qf0Y`bQ@d@7cT|Y2>#(6$xv|dybE3n7zd+>F?K3k zxr3f~HB0(>+^}g6>JRr9NZVnrB>SI{p zb%S5>WHJvVIqz%K15)*eJov;*V#m|Z3 zQX_)AO@=mVw!RG9nS+c^!K8V8O+OktfCxS~8C>=@BZk`zpdNu^@ykH156rdoTYWO+ z)*M&RcuFE~jAxk1 z*dqQI-hVE>arUq+Tv=#%@to=T*e-)fXo;s!GybBR_#%Vn*~6yO`yfI6bgQ>liW%W#s?YOGId}HL?%I`iXX_OK z^CXw@tUYb_Su3ShvrIgMbM;suHxecu&6B$=2$(Qm?yj9EKX><5)NIMRoX5+#RuS(O ztAIhthX1dB^BXf+pXxW`g5!Lze7#lggqdIdm0#Qa+|T`7Ow)d5hKYmSum9Sw?SAnW z{_)-Owp~3-P!`MLvjoM~J6a*X-O6Y&C}NC`ggt4P2%Ks%eUxH%>DrZ;cKGS~y=xhB z@`_R47<`fl;E-~5Ts_u;1pAzmwv2iFAqM=oZEaTnF2%^Z+` zVHzF?c~-KP(zN355UeD4PzD24$eep~zk6Sm@LC|Kb0+>WE~Cnu?=N9uD{BPk#SRB1 zRe47>$}*vjkZYB+8=?3M&*fcZ%~yX2m*lP4_h8%vqq)A5-xgp7p!bSp$2c>d z7~$f1yjdCI9>I;dj$sF0toYx|SYe^$LB^js*(xL9yDe^5NV#>|sVmW8bDkAp-eF|O zqr7I#xx=R$_qMk&(4nnIZKsB7uKbS*!0C8j^EeNK8!y||MUgGIuoIW?UuB>JZJ*%S z@kRkbLwIz%JV4MX{y$zu4Z{~1MQqi(TVUk1MKA@U`H9i&#RKECEnxLaQ%|Y+BqIm! zUtSAm(X%a2_4B<>345G3iD~0$R+R5`Si*RCG(Rw8Iy(4S-edQg_l+%lg;&w>`IOlD zgxZTX(KSN=qYZp9Ch3!N7m{0=E1%zDP|N^W{pgkD<+}zh%tgse6#}d!un9w#(N(_+VfSQyCWVyo#DuM}y{=1bwG5xjBlt z8k=ih)K?RjR%TL1JO!PiLwpX+kYgBj7$;wb%%w~Z#QjF&m0MCSeL`#88iRMdiWk5X z@6n8#3{>+@<#vn0j=#D<&kWNCy?4-x7L>?3b2RQ6-MO)E%HSD2G`t25oZCCb5}rSt zydTdqR^jIZQ$5BIhhEQSX!HIO1=hQ^)53$clhbT-)UUiFkrFQ-h6AlaGrW+WRCa~K zSnot5)eQ!;^2D4G*n`vVY3jlmWUcvNcyF2TAM|t5co5*+);n?%cx;KW^_3Sjy5%ut zjFBsNNEw&tre2DtC7*Sz9y~6oYVwY;$4cR}%^2F_6D0Nd7)Gi~KMIg-zQen`H%+dj zuw6a1zCXq*T9=F&*Nnsrx7rLJ61Y5j>B#fsZGnHE2H&$58pWYo2F9M(5kS(vehfdX zEaeRx;rO8KWph6yA7)fwh}1^U_3_iLr|t1wFb#C|6G+S@#$I0M##4x(E*aIPa|Q!KpjFv-rd+bNZm#PX#OE$)Zb?sodIDJ4X%-rgezYs^56r z+Q05BT>3pw?JJ)?DDUa`Tcb47W^(gE@tiWYoXeZ@_y7-#wlj+HzQR{u#y8>NY4h`= zz&vVP2?TB%HX{#%jDFQd3$y~nr*Q3_M~jEe1LB9_UHB~n;Qjc$@clTnG}#;{REz#@!hUq$(W*{j zxzIE2)Q%c2STiGN97ZH5o|ji zX3A36oL*yWMXk5<+_{_L!L9jfh80TAK@;lX(}dK59w)c9>erU1YkBK*|6YpF`;?sW zJ%F#bahVLX-QA2p zl%toO>R~$;gA=y;q@!{POIC0%B|zWGg9tCUTseN{i>LGzZ-s=xHm(K z3C-BX#@-ymXIr@)L7;k+u>>Ikq4&R71|A3>-hb2MhEOMM{CE9Mxv6d2eQ>W}iU9!N z(l)^kj01arqS=RnlE}@y-V>Z|GvKMolzWSmXK`$=)Fniu9TvMKR8~i!JZ9%bj~ER z6k*C7v*w;RaXSiuclr!`zWwX{H|reZQ14sal~!BONp)*yx2LS$d-ZN0;8Ru%c|=VQjfwP-Z;!OhVuokm^I_Ozez)i_K*fPa7!S5@Ql?4DB$M^^>F1Z7V=4 zVwD)PXD+mTC}TWtBe8IJ%?N}q4i`_IF*a29|Hs~+{92x5iGAnw+5P!E#gvhmm6=sp z6qzL!MU;vXOA`SJf+%(XOUNl;QfL5!kOA;xT z$YPC|S(TA7x`)rh^ZDufId-qdi^#}im5PKysQkIVcklcC>}PiD*s(L&v11pD;MnEr zF&}T`*j^Ed^+m3t+QC;u{NQ)Cw>fp|QO@w^MH9&#V4uH}!sLm;j~ARdvNDov!ARY2 z&ftMgG6e6rc+undbk2l$*wg318^5~OR$uU4s2n33Z)p+y8Hr}E}bJPXPr?w9_`w{Mk9xx~QFb4)#%Tonwl5?{A1?#k5@oA-YF!Ir<{W1U`kr_+VHAL@58oNe2i=$er?y@Qy$ z(8x(niyU_5yS`c&vUr5{-BWaUe#p{?Kn&;ln2ZaL=yLS;ijv3U`05$?sGRX-*kWfZ z;d8RNdaHiji-?RDIzE=(hyL+5j@4H=RbSPQ;W_YHowMCF3fsvRyi0C9)&rPuZ4l7| zIO0VYJgY5z8AH7~VBlOw+Mi7qTkVk7)t_Uo$Kjyzz02r+5nN5b@E45LIk}7K5M2ag zebL7t8tw)qEV!9=dp0bGO2K6efe{T$=I2K8S#IB`!BX3;Jy5pObQ=4npK`oK|2 zXVFuGgJvee5YYYSSc)9RdCj2+$*+t<_?uimcu+q9q_OF@ss_p}tUeCR=ZMgKcthjU zKyY)?O|t&l;!qiSs*LxHqj?Bcc$;>V=%Ic@oZVYG;+)KcNe!6nSGb;+nkNe{ySWU1 z_*g_ynP?P#x8;x3kVP1@jz;w7ScTcYM{@>G-~0wsWzErX*31)>!$HzNMS}CSjJP~W`|W6SOh_+IpHh{rd#g3bKL*Tyb}i~*#vo&wsbJGu+xfySg%Ex-fhwK`P205 zqG0z<=G;%WunR(@8$PKYDDFkDyiC@faR5!S>U8{ugVKC;QXzfD7G4VibQv;b^6J&j z%jAetgDo(<{o;C0cv;JMp&8~Emuw0@s4aU{eDai zkA8mrg~kA;ajK9&`4`acZFzUvXMZur0%svZl+k+KQv z$71?~k$wG{p7FMvE!F*4sC%?3iSpx;CmtQ99|Mf3p=(YD!b)_CqiJpVVo;z?d-Scu zN(8yL*YV?R4YT^IzBgJyJ(a>ec@ir7{`((nE*AQ3<>z4z49e<-&O^VGf_<`8*$+Pa zXmcSakNw!c|GU38^XEI?_{QeZdq0@f)>!xS^?XLe%@R9v#0ZhJ!qeJmE25o7Ge*w} zuN;RXn-Aq^rCx5c} ztN+4Z-rQ)#@cnWNFc6%AV#U`AD~Fbqn`U@Sz2g?G&?MBXJk1_`b3VSpB#9%0Q$la$ ziQvpI9^SPgu%7{PC4=>GP8|0sQ^NB-V{A$o_qZ)xiJlCttDBEYIP17a+u0(53>Wp{ zHAacyx|Bv-W>$89QlEALezm_sfZ-GkQOZKiC`5#-Yp0}94k#s&<6P@=mGPcuOZeA! zm2{z@T|Mg_#c{#7t81rFxbGVHw{48#H+nlZ= zv;MO;r}$6(1@7VHKjnp~{!89o&VrwOV5N2`Rg1^(xV(FF&BterZBgH&fbT!ZM~V7E z)!s0ss-dLsb<)Hrr0BeVJ13P&ra$3koIMn&HAEwGz_D|x=mUj#A?q`oEz`A{W*Fet z_XRU0F7$T!wH%2QqfuJX_$oHEutu?x1Hh_hR?BLa6MzzL!3I1lnQ*7DG@lqt94#VT zL?obfIq4pDqrK2Sp3Y(?a1jryTP;xmAKeG~?YI3};d#-+ZflBEgq>JA9h+_*6Mqx;4q@ zMLAr{=|d3~^+OpKH8|Uh@PhaZ9p&DGP7BOo!O1kqs^h_B_SqU7p&*6zd7~77oSt|%V-4LyCDEGKX7=lCFxtO_$!IY4Zyf(N-L8F^>J=Yu{umM)O1{kxyz z$0ZWSG1%n&%1a~ycO|Pf_a0T}tRUy`V8~y*bar4i@lmFk7nvkKIYo{(BPS}&iDaCIPI?DV;aqvtyjc$4B?GQq$sv{O`sAbg&6VgeoYN;hNq^y( zf@`wUwrmR_@>Gco8qU$P4{v@t5ekoT`dyUVx_m;75k!Nrxe~uR6L%Yf54fH;1ki%&tU%wWAoL3j0iVtu`4v%IS z?jnT3Nu`GdhC9}WolJr+JSlWmUo_HdwhXzH;SinI``cZPq_sGm?UC*!O6_!ssBL{0 z34A=f>=pp@O9TstMX9o;o=HwU_iL#7$%x^*Rc@SKUp@0az0I6Zjw8a@xbT*d-=R+Q zmMu^i{juP{OX10oi#l=A>Iu%L=*S|ZT@#FPCyl~s=tu(8cJmJe`i(xo!|C#RAw%K* z8y~!M?6=D36P%k9w%4k#p&0!KzUNiHxkhwREt9<)xGHBa|5+)tLMSq`P$ zwkPvcMD_C=&y$$FcLo&w2_F1zTWK*!AcFDF;Q~JWlXt*mhMQeF!od9g1x^-f156$qG`Ejz3iEJrJ{j_ z4z=2Hb8PN;wBFI?II!vy9LSx7M%6HLc3=cc5Y6}1HplE--CXVWSA9TV_~Gef_l0&c zTs|L6p&}QO9IPs4LZGk?@Xhg*DebIz9i4h=zqg1M6wsBS53NUgc7&xm5q@gzV8Z<;{ z(A`$OcM*cR)|45_T*>@-b)dB6_X;spOpNKu}?5q5#LK$pQBYHsS|CldJ>JD&f(*2_8pHd8s$uimN~%K zaG`Zdo}3g6EZ%dlVR-hsUcdfE%G=S+|L4E|tpxMl<}ZEc7w4$pTmSIC-+Zm3iT~%{ z`Ta?ztS&~o6cE zU_YNR(Sr$X-p}ws4+QmuhT=p0jG!jGE64Fn*n&s9OGqw7R$0d4iiBh&FCkswxpvN( z-QPY5@tt#K_lXu9#l>;Bw>{@B4X4*g!9_ji@f z^Qdn6sqO_&o&8^RdHr=A)d1$72HR(fb#kb3F3rO8)G;u^{}-+=+*+@G0q)8y+4Peg zF~$o<(GnDP^Rf{>R4>^>k#=)vBH9D*7R)aIwEGiHLZw;!nNqu+s=WHDgAz4=MKdl+ z;uzfMuYcNRNSGVM+pY(OX!1C!2_Us&WzCN~Rh*djO7N#IJ!dp<3_r-3ZHow67L8{$&$domvTPMHcbx>qAb>AhC63~mz3&+CrIe%d-fzyL zI|`}omL?MiEE>lXI1n6he)M#(x5b2GHnuQM91#vL2d*Kz*a=&5 ztW1ofS3oyKmy{2MT^*O^0cJFw)&7yqEc^^`-bDrBEv!LpxeoB zRHW70ISsUTCTGunjtDqI19ID3;IIomd|6`O&2ihWPHb+p1&Px_{kEyd9g@#42V~pR zYP-0VY3sv}KB+BRP})8nuF3kx<)_i7&RCu~CD}F}a=U2O^YQwt!Q`|qGsjU!8!t0r(7W}}Dc5*B0$)j?pXB(G(E5$) zdG+Gc!r>Obz&adlJu;%VPev0WWuG**Pel&bAB#fI>%)y3w`LCRJDxrR6HdYPxb4ZD zu?&4iy6tnKik^DUPX^YhmqkhydZIouofGd?^Mnz_iF0vJ{yWjP_cs@xHV!W`xY8T; z(qrO81Iy{k<)z#l&x>2ii}KsTR8(^-5zv0uArS7rDQ9*Lje0PC_ncMLxuMJHoe_Z>c79<)WSZFq2X zw6Uge;YBO@B}YTFRnT4VRo9j`&bU;LUSV6|*df}SA`5mr*w$DJVCFP>03qBy5jhhA z@I=YcMZ^U7&@Ngr|7ZJF1bF_q?d{-c?1E+NC^dDM1v0&>U#ohWH^Ch};US&pvKP}>W_*p27(UJ}x+X^{z02VQbz;id zCRyu)pT2|_s(KXFW3p#--}=;FkUNemIexr8GNc#SqVPnQR^N-4q9fxm3614^t4y$u z*ElSmDKcejR8_{W@%Tv$-TOdU@_(SI-bF7oJPz*Bxf%nsfv2hmy;ThP3=qcNWlZb> zP!2!l(LO8J0{LB=cozM=0#dW6(wU*QT`(C25hRWOjD?~v^Q>?DL?h}_dyzM zWZpZGdtmKa9awO!?oYl3RQvPmFFXcN&_TlWSW5jt!j5C*m6hxi$wwJ5pWe7RP6#Jb zool4|f@MIju1{qj;tuDUk z-J|wf7dCsuNoE*1LRM}G3c_1&eQiSS2_j*Q-~ProOKf?s zH?4l}ZSJ||FvJ=+_`;o^H2W%KiK?t-~Yo5y1mWK$}-}<`wQP3 zJV=_UUpH@joKtIsf(nBsoFpzipE1NZx%u$cv^PpeZC%RAb^7E-DfgW&R;aR7%ZJrP zQFHnU0VS&EWJcCr#?pf0xNu6i+Vxg;n6PJg*xl+yb*KACo{CYi@py+>iI z9xHEz(X3)LrG2M7S*2TomS9~`S;|N>NO({zg#KeWCo@<$nB*U@0*bFmG%DKSZmYr) zb30{hIq4E+;mS%eAw5_9f`d^e!qK$fwg>lIl(kWS8ju}NG`_CkdGigc>*<%UrQQAE zX&7G@<#?ikMq@@UTc*3MYw>0EFU4X#=MdT!m^#+?9SoyHY+=~FuY~WzQ+GTQ(yGW7 z42thKFjMToPFWm~wy0Sx{ONx06n>@k?Q_?m?kyC60dAL4G;N37QNH?I*DB-t!h64~ zXWlVh`k}1vD)Sd5e*1lHY=0R`cz{PZ-5Km2_h{zJ!k<+qvMU9#&v`!h>RU}wwzdEI zQs)+?-92==VB5migB?uM#O{rC@nuAvV!xup?r?JoUSC)%ZKsZSp+Wz^QQi58hv z@XDZTrvkmmm=py=f$9Lkfqk4j?xo~wd!pNF1iw(wq8=h?LNuJL@XMEd+h9`jCnCUN`#tGp_Y7Sm-b2gl6`^~BPD$mS{ zqJw925I#e%KFCvcw4{>H<8kVgH|GBRrCgplb9KscE+7MjayLe1JkChgzw;ULcnqYd z_g=h)^3NG$Mbh>?5h}L7%|b*aIWIV}jH8ot&?o%4=N-9qL<>#1_?&TvXGZAo#rmw@ zoL{0cMO5PHWD|$Mm9{g14ZYght6o!@fnd&y-jb(j3=?z}++b90KLht^30|G7MHbvk z?uazKB-*Oa^En8vY?OUa~;wWW=?FI4F}n4};})i!>$|967o1S+W-2518ao5m0R*%KE6zq9<}p*^+{H z+s0;<{>hB7`Orc_boQXAFGh&13VUaJKe@3N45Cp)mcpO$vkifyn#i~e<~=8a`TMRD zv)XcCzV~AhK+6%E_a<95m{Ck(1PEM53t}&mvfiC%!~vU`ONJG3P7q zh;aOf(*{i$cMhlJ*a_F&e3@fNQf-UHbgqdm&Y|E&k(v`!PrVAx+Ni?t)@NgGj^Je@6Gx`?Ze!{)C(i_|IiSwz3mWN}HaS^1 zc||XJrKWOj1XFz@+os&WvgRWAMP}fygD8Bz?<^pAsZ{T5@AdLHz0{Mgb&zB4SY9Y) zcV+szaIK~Ir1?8Ph>gR`81=F|Go#By{(ADPty`0)C^~$aL+^A>8DofVbFiOyQq0kg(u?+c-_ibw5oVv4p;X&0TNw=lkilPUGj_d(bz!4)5d4|)VO$u zL#)B;XT3N4tm69D?`|&|n`qT4^bR=CwGrA~ijPw!`q^#YQkJs=t|!;jt}qN#uWf=; zQSZPBKGofUf8eN2{1<&su7W3CA^pPD%h&sPqe#%J@$(C_FnZ8-RJnO1Vg|Ql>@)8x z->ftQUFpp^gpNdC<`^8I9kK&Nv!z?x;SBC360G}Rd)mAc5o}xa<76H=$06mYcMBYN zDD%&xNLXm&WrbfwI8$pxI_<1Cv|3$=7-{r=Ann>20b{a^mw&afZE4D) zCVwyGa^3p*@4vVh;QO<>^UTUrbcPW!(wxG8c~CGYe>;X`rP?vSwrCB_#jx)^y4MO~ zkv1_jRxT$6&L}MXYKoE7R*K@yk8X_MaJ=dDYvqv8&UR&CR$4Mn%nQnbQ2&!D;7+kkyeyjwoj1-2A6{6!KpfgJPB$!=`M{u|i_D&4j z?`?vh$}2MM1aoRxf~DzzK83Q?%y@T)43cHcp<# zK{>QVs7*9a-(3`$oug&%G#LH5~Ju3aCMTNP}({<;qD?wlyv z{^A9O6;ZI1-Cda(zir#fsCSfR{qLHSQrzoj96fXQpZ;t3dSJ|Z;cpa+FL`ZAo6kQW zGsv3PSM|Vm5Ug9ugS#E zuD+9TyO*~QW-fnL=rs(7ZY}s4&L!`nqX6EuNyb_@2aBq$RE|=3xPrbDlac4^dB7-H ziOQGJ&!Nm2LV4z_ImpnaTvM4is~K8XJK^el#-|84ImzH_zlfXrB`bc|2});rrjavy z8NVG>e78AIS%o+AfRTx=@DWNWWzj)e@^x83cb#m`!KpUA3vL>K{)#%dQPjng_`~_O zprU)LesZ%sJslsZ9t!ob`m$epj&zl8YZ(M>8A~~oQ^r=SqvMdyu+G7JP+m2O<%N?o z)b?{Yi#(BhUgXZ%oB?AT1dQW{jf1(zxx(PIx@arXnHH#AGd9iB$yX758uPVUoza>h zWb2VR#qsl`ZDTLvBMcAYho>+iIaZ7*rA?{q4-3l3X{Y;i-yLD%-3i{m7}R~e3* z6GbU-x>;1f2Pe6AP85#%{Two%v@PgCbioOOKVOV~-fRn<$SxUQMD2{>m$RNs(e9;l zm)iDpzVec7<}ADOaNv*@;A|`HwrAmWqk|@s(0kNc67Cy4d%F3wh$}RBs)(&yEsD(c zn`oWGOT@ynqG&>MJc7aZY;*0+tKl>{Ci{~a$9mV6u*W$+Y$r69a7kW??tK71t%jd3 z2M_*=Z+P!QTeXfIJwI~ZiCXs_7NOJg*&fD_>^qsonE*eoIkl@gMHASfbN!8LZ4r&C@JfC3p0VbH=3|8anTG+epeRZr+@oVUfSY)#F(9}78yTXX)i>i3jvKie ztZ?rvJTz|Z!b`VT-SC5EI2rKz;7Z1iIb4mWn>%MSeM|00&UM@W!1<^jI9)o+;)7%p z-NETva?|MD@XJ2Y@IiI#9VB&nbR>N)&_}Nj$#f(KmG_KWU7R+mpeokb&<&k-hHhuP zYJV`9ap)v7W5tO=_j~qS|2go(XZ?H`T%HLmFot#K^`)!hSaf*D^ryb48;ts6ko?g_ z_dqXb(KB;(;c}gBIdoAuWIu~Qy;reyA^7T=e_%xqQ-Aj>V_cWL+t~Mf@&pC5zV?4A zWe0nI;F*e59$vepe{iGm&&p_e!S3GRZcB8vRyEsaUQ;byTQ0n!cdlK2WAnz9a{xlA z002M$NklGzaFJVVUAGXxs(Ahq34KxIc;K$Rqx&?hr>LP@0PJ{+eU%|^Yr7-h+{M9OD_}Z@H2n^ z-guwpFamqUXPcv_OlQJPBLIU)Da@7+81XQeu| zc2>?$l-M*gBmtB|rY%wv#nRRyjt6_pF9gS#lUJ5vnUVG~W%12-zA;fU6gt6_qLE&{ zXuc#3pFVk%K-S*z&2RqWf3$fs=g)qO){)20`+4->@#dW?9kUw4{N(n%%`d$DwG`(Y zo3~2P=#&u0aUMU}cB_m!D?wi`Ptnubyr06%$%7AYhH!{cvL7%W;}-|tMwfP|DlcQ}r1ZeZi-~GkS-~Zj;o%}dX?h*!kA)_vHX107taD0{{ zrv6j>IoZ^`25W^V8|h`##CiII<4zK7&KkBQh0zw&*oX-Lr6IvXnK<4GmbOQst8uHgl!{krvINoOW)+;~xkdpwvR$p%3FTd(!}HR;!+(#!?;hIs9p82BzAL-?J`Z-k zDkcrWT3O^B3HQMbmgD7+5Y1CV!HWmu%Xsptd)LOUDxcclUB0jK)t`dRpmX^S7SG`Y zGU|T#-u}@oIQDMe`q^Cz4u7p@>pog@?O5efp6Yw=d$xP7%x5k5tL}y;{n_=)cd&5~ z_*{QesrnvXW-CJV?%-8l#iorVw{|V8wx^MPnI}h@FCsY@KmIBID%z#o4-C@{tNcxt z5JSn?U5!fexbab@CP*E8iqiuQhYq`k7uqfrv5#C z)>sER5`5I^`tgng7YSlj-0I=E3vD$DmZLcX?&O$#^=xl*vN>;S>+?F}y-PW%U*vE& zozeEFXd01%atJs%aOQB6T>xM}pTGCyNqbry@W-HekR$s+`D5O^_Qv402ZJjow5)9g zFc+S}?f%SP(J$+$ajS`U?@QEdTS`gXGm1E$UKMeGkJv&}brVG-ipC%AKh4>oELcA6 zgvP;J&z^U`o=j;m5V{BuCG+aui+9dGqF#^Brq$ETgr?hO@%FbqbL&vGwo##kC!o zQjb@kZ2{tCbaRkn;Z|ey>783+Bsg&h7B~)WB&R>At@{s(j^TU@Pp^t-!5iyP|Jx22 zo(c&Vo=(4Hj6JH&PotBI<-J2gPByyKm_+HDd&#ELR-LPVuQ9EIgA=l1>a1RKdY6oJ zuQ52^G4d7fUe7k=4GI>r2A-aG#ITWln%B!=47O6g$nbgi;m6Ts`H3!+xV&UA_wPQc zPt{Qw4j@LeoH`sZO}OAhLv8!c>1NBqz55o0xVl6YcdFJ+ha%wJIk%#rYj0i;x6gAF zbsC)oi|Qt??tl2n$V+99$V;_XedP@zJ#DFc+Wn7mEo^t>qhWh+yL=nRliT=~s3Z$f95zmv!q22-AWOOv zkHRy%(ofK?zmJ-;p`I~#H2%sSm|T2~D_*2brK6?FhhN{#5nD$r5~E>J$h|madp!Lg zZrCk+pBxj#CHD*mD86ocw|YXsjDMA^c7jwsBy)&knl6t|fZGw{6TMVD6V21jsUHsw zz_Dp+6AWXP^ySFFS|g@iuFiv*;HPI5IdS$rv(%%{N=Xin2LbUti?h zg~q3qBcse;5uOvFo^FcfPUpxL9e3tb^D(C>c<@DIu5F+C;yWD9R;`o>j&ZUc%uB|J z-XPZ`cd!2nSs`<>g91|_(WIkipywJ1Y zfrHsnrGkEXI&+46HTIlYqbGzTxJPT#QXfkXF(-0~)DivW&|U|IRJPO)w&lHQ>?Yq* z!@?21x}vTA^=#2#{fagg4BZFI_6PE+jR2qiFZ~8WQ8&25hj~`{ZdD9Seb&EFxvmBF zlpP|QXFc?M`n1MNpGLO{u46gd&t!{SzIbKx*0s0FqxMEyJd4t7F?XWO8Xr0ZDe7+v zo^a)aw!j%j@Fu=jshU93i>{~1@I~dr9sC9uIk10VOJ((>xYpG!y>;x*Vql>Ny_GFeA~R$YTk={geAnU#5i-h`V~ac?*Hyn$=l z&r}&Y;ghNPEn_uF7OWTvBP&SOjX%rp2=k|33xc2i{D1#XFwOtHUt2PmZs-lRMDe5y zCpR}Q^A^h!wO{1U9K+a<6YP|3Q6!Gz<_y^h1AFHkOZn!TZ&a66hgMKKt-{Lpi|6IK zNa5K_VZ}_HB(hF*S&I7Ik{&;NL^-F_r|{-1Iv%5MigLfwz%@Dj57X?YdC^Y+&_AGBKf_?Q23&fE6%mtSCWWF58nJR`?xC00^w z^N=6oUdpyD5TD+=JrPg)G39;v0h)_WEIF1DcRU65Y09tUo0M&*X$j9i5p7v9J(EIM zwxZ3$94>$Tul?1{Kl~@Zldz!ppV+*cL+Wk{==0YpI=BOEX5$Nh2iZV7OlJ?$M%%^E5M=WKiGQ~9}e>}LCp+xXa;hxqW~ ztIGbgxw)%q>O1tyeFAd#9)7zkyK}VcoGq_k3sfv{6b@iB@cUT2m3U9EZ~w-Lmjbfh zR{xhdd)Cth%kI1S^=2J!i1p1;&yt`LKqj6aT*+-Xp83B?aTUBHLxxj-T`1>hC zwLTwhyBQvXhj64Ye)mdvtIGX&;hlS(LKXZj1{QiUZz!*0a5YXGO!$#!wtSht3=p|% z@GN7@nCH+rcxsE76NPNgsSM{)KX|408Kdwhl7+L#b}zYgD9xN(whGJParsK)#44w~ z_ak4|{Xka7q*>N?pPIUle87u!a5rR_+U%F)Do`7k*cx6YV|kLQ3wP3FF+ zMTUcf-xDpyE!5aaNP4YP;T}J03sdqg_K8~Sb67uakGs-qi{~D%DHp=L=W}zKD$bNbaG=&T6>%VH?@QWG`MYkpgv6 zgwKQf(OL6}lLytJC1a+{LC(5~f=JFj6Kz!gI2O#&6Y&6%%Xj-R4=iBaN*{=f)3vwT z&dE45cHvtSNra|EtXXg&D@Dtl>^%c(>-eed79Zb5#h_Qt#e@Dvze~4Y#%VA0PQUXs zTSOsrIC&P`G7{-u(QK2vW*ePo%ZyhPnQmZkuQ_3UfuW+~@S+QN z23+sj`ZuR=HBM^k(bzEu-4}^t``SUcaVpvIoET0I3n4S#jB(9QxyebST|`=6X3=wj zIr8FFcwBO)7y1q!P6@mN?>bfVw8s5ID|pAL5PXo%<77TjIqK%fe)=p-c&Bl<)sn7b zPKsoQSA1|zL90f%7<|;=M8TwA>w-3C=SXE3{hTD|)!akd@I$Wb^pY9tZo2Op{Eerq z@9{+rBI7X;VB5m_D|+*3Q9UO+J=Qa036AOOwlZTHI73hRxEu*|yyi${j6=WE`#5gk zf8j!3N6*=gJ!g+Ti6Vl>N)2wtG1xnH6L8v1v;OW2nYB*i(yK$)_W8ozxxBcpJU&JPW)QJAcWlo;!SIfy$BoEmo!iP>k zzTtV(-ryaD_hvLZPR}?hdEZc1u4<9ov=yJVP*!^@7tvY>7=MESh%h{x1^PbW%QX=@ zWS^h-NdTrJ==0(e19x~|hY$SR(KAT<)BJ+-8K5;f+d*=ZVuEoYl>RjsE`w{bncN7* zq#FiTB{TVk7`A1RSoOM3K)JSLILu=dRqf7?rpL^1b# zUv-RnyCs+&{rviKU|?r79Ik?c?N57s>HAw6`qS3&=P5UgFD6CFv|{l1!Hc#C+}Pas z=;Ky;%gd86MPnygg|#*5L`RGY$v)YA`CEACpXKP0XNCY7lPB6g@0i^L6obM_2geU~ zY&!s9pHXx#h4>p^e`h0{e^2t_6a;-e zxSjKrQXe-zk;W$E-Z8;PpB!!V{oLlga7#I0obH#X%^up1%fs_lq5W;m-~7Tizq9%6 zAO6ASM?ZdV^RNEezr6Vezx~@|EWUr^!(rTL#{SaO z8ANg1m0X`5gNxic+}`0*!vntX-tM_JR^L(Tatf`wfY!4Vol&&HjkZKu4KL~$$R>Qh zFB3lB-(UfUx;QH4d$5@=FB^yRMbF_c9Nd(=iI8a3w!a1>`{x@EARB^!{x5if3|WJuaGvvsN?%W0nFc z;=)C~;H&taA-EV$Jeu(>CV&EnujF{KeDsQNANawVXhw8c%q!IJvgR^gX)eSRpGBh{!SyJX`BT z%35S}R605PFsI19qJ25yjFI`pa5-5t#EFt4zby0Zi~66qkH()QJC`$LjusB*#%ttA zGM-^5Q8%ZXNE6Nib8+YV3yF)RHPQ9qVo_6dZRo+)GEu2Va(G$j@V8Si(IdF;H}9Sl zA!Q7W6~g|ch#&K}Suk{hHqrYsfRn9{UTl8&{(CbHKfdw7jNj!FN&mt(zuCzO-%M{P zs;d`{=CnFl@?DX9r}u-uKHy7medLI|Q_bUO*)hqJw7PK@L3Sj%6jh?!I9ANDlNpzy z1*;dm8&luM(jXgxRUiDe(hpYm@UhS}^UK1HlH-twXNdNhs9bvMAHnFS5psdM_$XMpSg9oRr@`qCT8jFBt23JQ9jEtMb{V}9cWW)u*~0Jr~>n2 z#vQRLP|wL|csNo{o>~|jjNG1nvCcXBa$=$J;J`=J6EXEH_~IcP>*mR9bL$z0&old~ z;?*-G!Lw6cv`@cwEWKGyuREyT7cEpyv|{vo1Wi|m_^Id5@i$Rd%b_)J_s-yGJ5TWV z*zoW^bJ4uVx1};@at^JKdHR`P8TwKN{mfNm)HuAuxD4IZ-Z%zxcAmr{cd<(KRjVCL;ri(0p@t+KtKd!U!KjdU5PJ^)Nrn*UmJbXs?eCv; zAq!RP=;LRV{jz8JJ`RaRUSC$^C+`KH{!wmRvvNYPcTtFT@Xw7e{35K;*tXlBW6`64 zZC~D!`|$H!S-gE0C2=zPor@z44hEtxG&X-8?ii@o^?4YR9~6;oqtl07!R@D7K6~4D zbVGex%9yd@Trke*Dhm(Y8$5mC-Bo2zdjE5Rr(0h(<}*H9aj|M_-g}*F&4Bdd^vGSk z6U`HRaMhzOif1rId-{R)*Hth2E^3jXO1(B`-~nH;`tK-b7en7kL>wDWns2tH9M33v z-rw?oJuZCOyKrzZW8OTn9bnq)7X#pa5qP7__U_46&(Ae?CFHfz`#ePtU!wp9z$pKW zF=Js4J>k}nVKPEp=^@v*1 zzB~dVQpWjH^3Z3!_dNdopoowA!F;v6H9O@SPrqEmhjvBQaNJ1xO9n8q8Lt!^PT`%7 z;*;TC;l{%;=}Vr#F(=Oq0RE87Ve}#h&*WqC+5a)ZIgbLgzuRJE(4s8u*%~7PN1yQ( zIc7Ncp3Zi=6s~<~ZF%Z;AGnOGrfMBI0#yygqVfoQ%<<;dLDu z*!Ocn+YGyJ8BEsorO_%s>vEV&}r#g!t7F3dK< zn>X8<);}^-B6Rt17^b4^&Y#T4s$(~cej4XbbI1k0qdD4M)_2Y)(H+NfJ{f-tAm;y3 z+kj$z=8X#<gnd)U;3LJ-+pe(o6u;mC-nClSJ5-(cf&sQnHQX&r%ra-X7kFT(3x;Lr>r#wXxC{^ z2UDi`*<5vig>Bo7%tZCrChO!}JQF|HPd#cn4egA4;^pq12$*4^^V z(>LQx4s#Bf_jp6#j84m8D^DSQIqsJ3+a(d=bLcGI1kdW=+@Z(LB1|{v#O5F8JQhU< zqwkF4BU!}>V5{AW%924Mx6K9hC{vY#i{&UWhHijaa{g$by7W)fiv-}yL9=A8Ncd{1 zY;~fo%IVuge~rGeaKB`>euP&PP`k~5>EkZPj=5vI)pFp>ppU2q(7+U2=FK=1*$LqH z**p4Ob5k#eCnxKxBKE*FFjbbbV>-Kj)ybj0g$oTp4%+KEI;z^K7tQG7z~Cjtl&c-r zJTq!rASWuOzrnO{iZc8$#n<=wTFpay9;x5&b(vSa+&zvKc1H>G8 zkVjk#>gWzD4pN^f(kmrD)aGcDjIB~q_83H&u z=VY)7ea@cygvX=1DO?;q=W@WrWZ;V-WQ7&;-YKEzUi8fAGwtnf3zHQqMu;SrlsHQJ zIdJtpco-Dd$|G?+hQ>>6mG^FW2`+XrhWRlHT>_kw#0uH7$0c!$>Fu|@ied7+q^Wyt zoqE6RTc3XTVJB)_-u&?&bz)XyXCLej-g~d;nfEup{GIOJVu z`yaMNf%C^UD2AjYuR_t!r(9ZHrf53>h<~o6URgzrXn_IeXq{MgRQ|KbYvI z>qY z+uszb!K;t%1wrh5eR_HIslUOgjH_})#Nlh;+cmxw2*zo5?Y=8_xa{;{Ml&2Vw-_+! zLqgQ)yFTn3QM<~%@Cg?1ZKcuYpKftL`8s^PE;}zTx*@v^Lm&=}IyrOMjk)4ADc4(! z&7GZd&mV)r<^l(fRZZKOI5BLI!LN>WtS?2DqD`^p2M7{d}I&fnhLP z0qUzcCGW?ZLkXxkg%08c=vlsnMSHe(?Zwk<*^2iT*(5=-i$M#IqH6GR z?}5!~Bqt1N_zH#Yl(K;8}wiiCC^Gt>U9Gy5@Zk}WmnF2?*@7*a%sL0ElgmOQu zZHg~5CN6CD8I$Q8l45frJt*4BG1#J|&Ya7+*H$9YDYh=b?X4C8j2GHw#G;?Gm3tNL zBppUGBD(xA9`X2d;pTj&G?6prvLwWCJ&{e7VVv1E>~GPnPAAhB3mPwr_SrjcfhIU> zQzU{mo<&bG8;wJ@2kIkt%+FI6YL1qmy0(UorZ3AWWnt8NuzMkx!9#AFcUa09Z8U#Q zcff(8u|N9wljLkP(fq`F9uzH%R`AHv&7F&FBa=tZ>1}f8O+-xPW-DIvhd#n#aIx*$ zPLo3i;KL3%j^6v=qjALIacI$Y9<=({G0DvV^Z1k7ZPoGC>?>U-3QRH9nk1OZ%h;T2!D-11Msv=P5iHTz$Pxx! zt%hR;_2SO~+kac+mZPNteOnwgem+pHIWqIF9Ub^R+B}+Z9>XVz# z-*_gNhMz*%>ucFB=J=~;lsW&|Or`}i5m~F8PVl*_-4bzb&-+B~0 zi|2!vVch?LQLWR<@IC$L0r)vvIGud|3_sra3}4fa=}Tb2V>B^hRzDl_am0WN5%yp$ z8icDV!8cx@>n(l32fCW=d_4n{ZZC&W&X>NUv%m`C>Z(d`?{JDnjMa_~IX%o{Gzc!a z5;^(kZSr|>-m7~e#-hz*?O8q+-q)Ien&Cs8+ViVF{O`x#J*~b)6Zk>XE1cCPnyyL;Vni=Si758VM5TAq2SKUKV2U|oZNCpNzE0H+Ba zk2l)Nda_f1kHrUO4%Q#=tVkbGV(H((N>^DAs=u)sx=cRn#Pp|!wum;8Gp?_@^cDKD zr1e(_pdBbX!ayu*@D@^cjHg*MxHmzDftUcTnnAvI2rA)uk`>O9MWtu(*NClo7AFTL5$6bO+u`Xbi$04Yl*)^dOS>-mYnod z+Z+~SJDB50DLy0Qnhf{ahV^NYK$8%;tzhG9h7O^OYoENs4VSx7wS>J#BU~~D}g%tIYWu}zdmUg=Nc(0ZAuf6qV z&XX&f-|Lw57loHokFFL5&Kv*6)vKHL%3*Q6tz8##MoIkq|NhP2+H1nO6?n0 zQ`Rdlr2lb>_?xZZQh=@eQrbSeSvx7lX+|S3ICehho)z-j;YgGW1>GdSU*ykQ_4`8i z{6Y)gAc;xgB)CK&#L@~IeSDcee3IApZq@@PH$bkzWH(YueZ{0HmB3o9BayP@n_6dr&mDXFWo;oo`^0$+% zzEV~x&2L_Rb8;o!YU>^Y;8nRWURg=oUJXsF8@4?q5DBaJ)HLroX4KD4S)5}ryG=yn zZw=e<2p{i7? zKD&}0jS=pO!5p~*kiK+bXM^$8s6w@Pci_`DD;b0VM55Ixw>pemP_eo47(Q&8`xz=EOr$belq_=-Ns@l{(A7K5FWL3xfIG^}mB z+2wONe9n(E=5bDWr)pXF$}3&n94qC03)hrdhPP|+*5!oRMWDDp+YkCYo#R4V96Im_ zPDYC{f)921K6nUEPkJ_|K?WNKhW910l#9t0EIjdIjtrcFlZP|vUb%08>1gvCciKMn z@yE?U#@Vrvao4Y2n?;8Al~})n8|^Q?yS8MLtDJ;Bu`7!- z_%>O}pGtv3I%CFDrMq99f(`VK7`KbAoZ&tq)|Mgz7Hx4Xv7U3|lZbZJ zbeQ;pv1SxL3*OBm_{cEtIsKjU{B(1TBUz&MCnfN%#vD1E!eOp)nS{OIGv6ajNp?9s z(2;(*@L-Nf@N>1yp)tcL7x(G)%Gdwqebo%y2RRUI)mk#AsKolLO|Z%tH93x|a~Y9T znrOa%=o(!5u=-KG>0id%d_5xis4+v491u(D4wzGy`Z%T7NmIA6G6a2&G~9k3gr5j{ z+VXI8R%I3Llh{B%<|f`CLW%*+(ZeBgwEQ_WHD$fEyTIwbCK=P_j2{siXpNH)p5#cA zPIudDB>?H9V)MLx|lNDV(R+ydOiI!HZ{O8)BkbUkU_R-%L{fq zo-Y~^M9(Wbi?eMr9n^Q1+&I+upr2F+8ub*?!gc33+SCMB5+@HCJL7 z!t$<0OqEBt!=QXNu)rFEns5Cv!wyKp|JfBu44hzH#Qxc{uiCf63P0Tav%3CVb%js3 z9|p7osBz@L_=yXW{~Ta{u8k@m#`Y79{Ij`1c@T|ae<#Pvos9dFc}+&>2|cOb7jqzR zz&vdA-m1c#wjEg=6}CN!+ZGgXQqnkv4BLwo8YjDmDshi6aWM*9azdQ!lo}Vsh|m(! z&Osvg48_;}ccJ#@Tg^L~&}JxDwXM+#dJ^>Vk>BEtT` z!#gQ$k}BTX{K_wWCqe#l^UbfnyH;0Yj8@SpxINh1Zq@r@%H1z~?HeOJzxAzeZ+`dp zesA-;KmO6?cp>&@XH|5wdAqRu7p-tVD#D2J{oQYWck_>b@cr>t$ggoWrfe)76aPt0 zkvQj|_`OC8!`>2)LfB3t<+iLLH&G&!! z$D6Oc{Z^}rr#64{*Z;=m?stB%<6U!T<#4*(^CvlaF4o?uPEEP+bqSfv4G^EW*EmR$ zX-gYN4?%K}u|NT(pP}omu zQf&~_Yr9lDa`$3olqE2iF~7?0>hli6QMfLc(X(gnZ<`o6PxNe@EZz5DR*kBgV0C%- z@U{D!F$+eI(I2{8^{gh@fU$IPMw&XZC00@s?@UwMJ!ZpWke33uOcQG zASq&JQ|xSUJ$*6;F0#>O^fn6RO|#Q@H^+PvHOVgL_5YeY&~&=G&W#)xl>e z!8XUSd|({&2k}MY@^OjP_0{p|I?q7k*tt-|C8HWGJui3Jop>#yTSUPixp~U5L6)r^Vcy7CtguGx=#yF5q3~QHm%y~2b7SSQ-2&{P5&~4?w0cd3z{qpK~ zCK5@1j3Z}|?K3XNqdzY{3;uZTp@j7D%kcE9a`#Ixd@ml(ki)-2ZoDq1NWAcB^5I&M zniB1U>2Xn6R^ZP!p7JjYJv4B_^v|>aUc8n%NexSA_$}FLVa#%=bcBAC8Otz z5(ga&-wSu@_x>1Zy(0(EB*x~G;bY^IlQLRx8YTH5%IXQn zC1+n_e)cS*rgnn&S&K_^G<}Or=7(HSr_L00GftMHE9rD{=XCnxS&|wjgN>@8ay?IE8l?I+0L#Dh@Z`;4B!ssy>3#y>Tog+IBjC@^}ilWqz&b8qQZv zvJC#-BmBnT-;OQV+#VbZeCuy@!dt&J(DTNg&b^g~2e$bPT*OaKCh(X0$z$))Ek*U1 zXUmb@yZT%|IRCV#;prQ&`U748=>x9F8BbjJA^P1V4i21omrbCQKCDam^%q!u^Mk)k zL{^pS+qAE!?|qU%@J9BCw6)O2Sz^J)eeKin2F~UKILWkrPhI4DB~{qFaG$*0jwb-t zwy?%#OJ}-O)$6M5Swz_Wsg7CT$?4^)d3XAO8#H2!&4IR9#^cC_)5&w`7U0)z29D3C z$KnO?7h4@C?^^FPSTl!2($Ed`{Z-L$&n6-+O}9ku=9@@Fr>CjkA_FJS(HuyTce@dC z>cTtFhd4~7Z>FFOdz4G^0?E30Ddf*Bj zxP}n71knI20*PEQYLGtfrf|d^;mjmNK!f}U10y3m8XQE1p(=|uF+A6r9LwS3?{VtH z)x9(C%?MVu@vKHgVs2_fn}70L|&kiS1Dq090)<* zztia)DR5Y!Ts#b4#{XFYC&Pe}gi$*xJuN%sHU&DGV^BB>{8&uuajT!VQm&j{@$}4- zqCl=EWUXWi-@mYw@8-aM(I*l)a_-FPU39%4EDVCE$K|L`A<0DN@g z*5mvi<3HZ~Yb9&`OTYBpInMre;rq5K+`rxF zUwv{YJqXvo*E=s-oe%-VfkeUOJma8|$LNh5Vv=18=a+C0Th0R|m0y2XCuOMT zsz5(;f05{xw{Pp7=iXh7jpEZi3W|1JOCg@GKk;i<#{1p@oMsMROL6Ot2fI%7$AYhR zSJTnYdTYubuHa{UKipQLZt4V+C^>Spcy}tT;Rk91NP2J2`SVQ7M7O$|*KrxR*nQ5j=)Q z3VEtyM(VxFBkR233?{^llx@&*SSLf=$NqISm7|Gx?EAY054<66^w$s(roI2z5NV54#5uX$3ks2DO;op*qlS197^WqDB={vn~b|Tu*m4w{ouBYq10l;0cduW)JVf<}O?~Q*OOCa>CrJ{dM&5spwo?N5bK$j5CW`FHXlZ zq9smeI@GcKXuS$fR_&oS@xwrUs7>kHE_PWPGzj{MP~h9gvT#p`-e8OA8yI8K!c?BoYI%|QiMlg9?u*{yz&qfO>0bZ0vIw5)Pdeyetg(X! zb8B{A^hs~&W>*y+x@Hl-@^ayj7ak8zl$-|8y%*Md?txD}A~3l>|7I|J14f@-n^%md zzO*nH-tqQl$IAiJ_N?J+;ernS;zbVq7722}ul9Q%KOB6hbN>0F&FRScG%uqaEss1z zJo9*4rt3DgErITz=$F&YkMvTrpQg5IWK3Nu`@IyN4?p@~4FA<`beIzZGtp;xXcz->yWA<7#fq(@iKkjUa||m5in34n zw;DYwfhl&g{U-rUv7oHRvSvllDjLPl%97P%jvT9t_O0@K%K>!LR;(D|ne(ki#w5oG zP7q#6fjwQhF*1XX!u+%qPPu%JScMA)C&D~zMesYn{L7o){Qv&9tw^3IS>`u3KY0Ix z%@2S4;~Y(=HsAfuw>Cd6sWK;str(JPg6r@9&hKn~=@-5;r=5J`-FK(&jzpK(lrzau z>3fqPbg{4f`fUZtKal}K%kQ;?j3ezxeSD`=aU?mG`16;(``u9n@7%dNbNB0Dfh$Q{Iy@({Oy11Z#9HRHoyPFA8vm84}O2MpTY8P{EfenaIi}G zV)JH>7h-%rhm7OwDZ$2Jm(POo%orp{&laU))q(egZFJCyE?sRMTr7MsqpC{h(R`Ux@tZ*xBGj+ z{25^Iu=>}tzK%npFPu^fPOz>Y_3!GV*rHADXWCwO z{od6*-_@&yDj#L6Pvxhop6T;p7zg-m9TYQvtA4@JY=)QW@c#Ox@KOY4taW1A+Tt-8 zMzMx_@ESMujlw#8uKZL1mR0YpMpk%7TT7u_Fi&Gm!+~kuTy5|AHZXy==F#{b2Q!)N zg??&soFJg1#G5C!Ze1zy`&rJ0q1lYKDteG(Ktkp@WhZ`ghVmBgI$eLp!P}?#BLaq@ zX7xUHu{qy8t8eo5I4an@eCh8VD(@KAM;XR=^MDnO&gT?3UG&GrPQ=1TP@kM>64>4@ zT99GJ89#K9(g^?hHzy?3**k5^6j38bmlG4UCn8y7fWpeoR)7ZUzGKD1F9jF5oBIkq zD5}Mr=DeA0nbj*A!I-_Blbyrc5#AhE`h@qP3p}5)zUKd$L$=o_V=O7W>exkQiO3lT zRqs;hC*q+xIlLw^v^nElxrERgS>p!|IHTxVw%AeD_iKlY5-u;XveUoJcX)Nrse{7Y z9pn9^a^rMMrugS{v|DB6zpjZ=4*&o_07*naR9*7^ht+dCJ}BWZrx9AdnseY=Uw?P= z)|EE5#RuhF!FSJwp9{$Y5ho&eE;Mdua(wS4FO0#5-*DckNNXB65Rnhkf3qzB)X`g0+Cn3&}I zx$r3(4e#M(AiEsMAhEOU%<%f4wkB^y{Tiod^OVlaadvMY*`WY zAwuG5WzGHTMeAO>er23Y=n774$>fxL`<*wZ-d^8)?>GMa&AEp^j9L&>JgPpJ*PLeZ z38{O2`hK$dIeFE?Sp%M9(WALxv4P{_=~D|r9HrR=!T-E)x-<}wNR zG~9>=dz@Y&XBlT6Q8U#rjKXt`^nRtQ+9#dSzqtC);u4XZ&hfqGinhVdfwbgt`0SZE zf-cl&OnoS;=lGOM8~QolLk1Z+(G(RP*#$w>y|dNGuBl@Xr|&3r;Q*4Ssp?p=aP|8Y zXH0$5M`J3QWZJAeoDKhomrqoOG1v|Qxi!w*`J0XcPqh|qwY!oGvt88_UDoGqq1Sz& zH-T=Vv*%s!uRMz^*|}CX4t-r^;3a3Q)9(X3Pc(`8tHya_?*#O34@Hu^mhNDy0 z;IVnL7FAI6it3szaE${P;bBsj)Q{e%dy7WZ0X)}=_|fym3{9v%hx^)!%#BN$M+5zi zGDh~0ac4U|{oLtx7?iW`_>u6OqwQqhPaSREj(aWGqfgPfC;ODAZ_V{}A~1;Mx55+g zO5?hJxZ;i7AY7N3W|&%>W`Z}-gS?w!N;{>Wsh7H%P+&WJYd!e+@4sjmpj=DDOz0ntdCV$t z%y?pb*a+M%-fg>L|4K%2jOy(88iZ2pyB`|F$c|Kvv#2{fliopqEkzZH!!4I|j~? z^Tu0mq>w+{+_-VGoC+zvC2YP>nEp7Yg6aKo|D3MPzy7cO%bS1nJHI>5t2f%`{~)DT zjvPw8J>d^?Y&~h^)T-pQvlln_fBbPr!~fCdT#1_hy}$LhHox{S{Y#tw=YR8GZ~nWY zeJ*88{@cItuhrMah?1XDNPx@jbFnb@D^^HT%0CHaMg%2FB+Bm@_lrW*w^r-#x^v~g)Z=^{$p)_s%y8ULz3O0?=)MA99VXa z9~FIBefqm1d{$#iIofgbcyBMQ{oR~2>pmC<&g#_O;cK-=L0caU zkHQr)(0I8PUtf!#4_4+LMG@R0=6(N(>$9Ri@o0BrWJ-UG)5d4-j8asco;6f+k#z&t zxEt2J-S5NrU4<9Uz7(eFgwNer@9OxnPvDQTv|Wj0Q0xvufO}XEtXbLGz5h8{3MbQ6 z4~;uT)XzG;I&8{OQU4q-jRtH1;`COQGxKY2zS%aOjIHNI=|xK#qiEf49{6;L?zwul z1oa_KBn;z*((U9e(F8}kw>L(84xRqy*ybd)tqZ@kP&APoIvd4L#9i;3Qw(>fV%c_e zE@Sg)(Q|r%FI(Xp!?Llkd2Mw~j56qn!S_RGyX<=Emdc@mF6e{a?|PU5?5 zMRC-$h(FHx(;4@(qMDAv0mbNto7rBKGkCV()DQQ`2HSaFoonlB@&N8d)3`W+;eMBh z8El_Cxj7Ltd*vab!*Kl1I9Bdlyh>=h?FqBG8Gi9+{FRKLkDDE?;Bq4Tz zL5{JtJ#U?yb-J?NQy)XgDTB)0|Fq=u5hBz-z8l=tBXaZBz1!iXBN!W>F;wcOEkU*q zy_F;2%_4eER}bR{zm@P9_nL59qJ(Q5~%Y^4g{(a7aw_(ar3;-XT~DFa$E@W(~` zaB7?@y6m6@mEH>GoMF-CL{9`4$0rDlF{2lZu)Hf{d!jY)>$P((2hVeh2DLHfeYlig z;?>C(B`WsjwIY&&)2VOgFFfirPY$?pJhi~U@#RD}^NCYjex#=dwu_Oa8DZh&NJ2*b zm3lHx5uY_RbkJ;W+t;ILqe!eyB75iUYva_pU$oMVPlF}}`%#Wgv?MPOXUOpG7~_lR z-0FEY>C`<8%>q;XTnC>R3nzg!KVRi6o=A{%F8sfUfm3@TfXl~}uEnV%XV+}4w$*dC zZ$vdYfS%^y;DCA5BG%a=ocu^$eK7~-h3J8M7yU8OdsXX24u|bwuy|G=MZ1H6+9ul^ zXFl6cqIV-<4#|V`S|;at+$~lUEXC)g>-9XEHKxj&KWNax4c<61qqgLD8av0+>X++= zJ;d;Sb|iXfY+bawNqrG4_|!Ofq5#fb&OK3Ry6_@ep&yFoc@^HBJXnVsvwA#mRHpLJ z!T~(!=j6Jnd1b{y;4u>k)%TiGV2ojUj3eS_@ky=C-&4V3cLv8K9;;uRcbp#+z0@=E z7VO7FTT~vct3#LJ(|3*}e2Bg<&Y0R#WRO-la@L>oT3^Pl><@W+(A1oU1#i_)zwl{H zl%I&c@PWUI+><>0dCxhFM?TK@vpI2%>Ruqu_efy+Wh+=UR`=FP60NT>0cVY92wY(7 zyPDK!+`B#fU-LV<8@Lr-x;Ua6*&j^oU=CRHXp5Vu;1@4)lEgph6#C_KvUM$bY*<&m z+UmvUIrf*F2;Rz0?z-BbprAryq@UCGsj6StmG(!M>ERZhipSeR zdJ5fC{=|`@dyeOn7hPjsMu*3nnfOUaE1g*ZfEl5(=RCYr`)1xLA=QqfWOFr6vZ0Q-mfhdCM0 zekg@=O{Lyhlc?Ie-NZtW5j$oybXz&z-{%dc)x!mh>9=J#QwSZKm}q&`haiSAV~nfC zpa1>~i~$UH^5aA=l=|m=a(3*ej7!e>*5zwM%fi)flpo-r{moZ%zMv_L8q>8>dZMj9 zl(=!|l(_lT%aS0^mZ!axll?Iq<2QZP+!iRk=(jwMcor;nZPk%KCROxgZg zIS~Hn_kTZQr`6r+VHiA2d1BmI36^a0R8z`{Sikk#|MTXX-}v_C|MZ{!XPX~)YS-WW zFaGn*|Ml;L}$*u3|HKS^lxy!p*>a-+zeU;P(< zB_;hrC!>_dC%aP|oBzw-{VyAlgUx^N@BQt~ z|MdU=A2(lr~_Py_I zelLg8&GH#tZ`=h9B;oX({+^1(`No}pK3;ua;ThMoEk_$|;N)$Y^ zdb`x=`m_q~@V5%Ddkaq$*cGZSIPy0=NEdePeeV7B`uq-UHNF&RAZR^XqGqo3h9hN#cX zI=*R(D2ln0vHGHLa?Vf=1_m@^STv&*NGEeW&C%mXc*Z3>+b(AAkQcXd7`x<=8GhS* zT~SFV=F~>cFSPj}$1n#S$BeDLjs{=zK@>;PhT($KMUIwd)#cgwj8^@{C*XFJ+S;95 zK=CP;y0vRA83!^(gvZNxtnq`pQ{_^@i|`*ZgK=s*sf!_d{;jvRw2bGT%&C*3I_HiE zEy^_cW*Zf!1zC6`zKmW@)`pxZwy!c$Y*(h-bNE=qIMx9J^5#5B-aUyZ&t8bX=BQWa zg`9L`j6QL)+`V74MoubtKGid@KW!Z4ZX^%zT&ujc)_zn}79-Yty57Qo72#XA?@#pW z|H9sz2YHrV^_@5Km&-5rtjyZ0t9LEkLc1(9Ee$r>K-dfj8!%V~W5k4XJuyPzW;JQpYMGyE2~u9 zNNNU`Lw&1$`Fp?j?z_vm=bn4cx#yhgx}ufSwfTk6PI6&%v~tnXQE)#(p6zj zYaHjU6VG~_S}F~k<({HvIgaxTN-V9C#K#$2zjnccVFVR7EuvTho~yuYYKmap#GhDM zLt)8y2oo(wJjH8uhv3#ItyKD{Fq$OqCQ8Bul!|Uj7>svo1D8FNp)@T!<0FilZI2}? z^hJfD!n{F|<)ej>aB!|D3@hAhpz!gCdGC!GJQ_DFarhdWGqFgdchVTGQMGh+zA4O~ z0Ol2sq|?xUfZ>`YeacbY%$A995;M2m?``N$E55L%AvvJbuF$+ z4cg256(+TP=>~XeJR$XST9W~W^eOb*dBqK3Xn*6*8hIn&qHcs%V7^VcrU>n{Bvvc@ zwhZgjDk83Beky~W{U{;b>`}n4iXPj}ecI=o(>l{W zZu6d&UKZs5c_LQr6EAy!cYujRX^Gx zyu$DlN*@$<#J{8(RKw7w0hN&QG0;}lD-~_>5SE+y!@Vxni`JQS58BklC7%jX6% zI(Y6Hh(!9?n#WI&kb5$Z5>ua>By4f{Vh}A;Bn@Mno=~#0s@9MRAR=0p1U+rwJMEzk zf4hLEPTOg{`OkH|$h$IRwqg40BjxMvIZ5##;(ww~3!lT^_P0&xL;K@4-*uu^7}fV~ zzW)8*Z>Yp=O1{l4wZa+8=9$;cIC|%x4_?!ZTR&uIUi`|wr1=^4+r7s49|Bi{SJoqO zftc|ZjLIjd)ZEW=aAn6ugau8rUl6zSU{2RzZs)JzLIlwm`wc&SL_X+0C1Wam_9_{gp_$V5tn%9ZMH0k1tHF^G2zHp`xrK7+$`y{qoi3X&rKeEPP(4bEdD=f%e);3S zN!A7c~=2V(1?Cf%q3Vk=&Wha_`>RTZ2v6etnU0_E%8{X9_ z;OyypSQ-$7PTy6w`F;1puPP%)PL?zGK2Uz{L%%?52Tc5F<5(YLA7VZ;bG)25eYPBZ z=%Lb?nJ9~MSIfmKmw^fJLG#~%;hnp5xxDioZ!aewxTpNXfAz1*%qz~8x4ro_<=k_R zqXZ*H5dzFELeLTnufZ0VVX!qBH^8>Xy&GRiSQ}^INBCBD#~fbub6H;u)F6d>;7BdpFHr?zjUf z^HF!RtP>Oj2W52Hcf4mCW&BK4z_0a1QA61goctDtN&Zc}Oeup9ATP9Qms_zRcGw8Q(+0N}3Om>}=JZVh*E!>#)d0-!Dibbd2X7jqji}DX$ z%e4D$)+5>#_?wwFDP)Fo*j+F7N}!?-+qTP9P|2Ef@hB ze|wKm*D69DK&Bg>o2Fy6Lfa;wE#h2}w0PE5j1VW29>RFG0Vzjfmh(R9mI>7$^gb(1bx zU@Z_vcM}b@H&m6WgKfD@kbW~ zkGY?~JxXO^520XabP}ZiW6SB2&Iy%S>%eCdeDS0;6jilLu$B_K zb{ktrmovn*92>6CXbj4n-bOw*X|zOICs4CNMkiGsF}Awmtkdre?kU8W&rJqnVjO34 z!Gi#-dql;g!~|U2*ajYq**4{EX>rH(1aO#8ImtM=qYlMnEfS}>2h7(9guN^Cfq>@R z8l*4ApxTEg8AiFKOPlfJ`gy)eTtQE%k{{W^WlHxT-DOm$O#nmJO^6H@P}OQkdUW4g zzPUW}@efsNB7=8>?0Ig}zu+MY$=TqTvi31{QIJu0-7LZ?jd79QkRi$n=fC}rR|NO99*+wj zWyTsL@K24tl1ijuzL#1WCOhmBd>XzY|=@TJHSnMGvSc;s590~ z^EcFMT4*-2!o zQ^8lnJpPNkPAZ5IbSMa<3AhZT_$9zI$+$3Y5R7Z_Q$%rOMOIBB3+ARK5j%g-5K1t} zQc;zB5{YQRn;mGQYG)ET=Da4en|1{Ox#mZ$vn`Z-nH>IpIlT-42p>Rjch4PEPu^O@ zZRj*{7hF_aP~7o#hfm};7KNJriySYj-?@Q~CEEIAtDKo+L6UKG7u6jJcf;LzF>ZlO zw7UWdB`#d$nq3B-!oX?oX0HtL|MY@eH+H^^vP4@5)A9bOV20% z%nFQ@ZE%XHgQ(moGl~ zR89vud-|?&-(6?v?|NC5>0@`!mSpNCX{a@w3{Fe6y-oi|4 z7-zUAv1qY#gx&ZH7cQ1pzUDP3xp1H3>A)C_JnhrfZi-!Dk9=IBo`3W4kC*qn|69xF z9)GO-%s=@T<-x!5!E*G}v2yw19D?Uwx%ceZ^31vCF$-S_Grxc`=7Ia~FV~hvu1%wXk%>GPz=iXNPA?!95aTUzCi0_uB1~6mn7-m%@!&4_(rPquOWicQEA=H zS#D(h3a(^OO)mZvdrKQ?K`z>kjTMTwt)eG`VjG&rHFxvZdrfke)1+V^1LAvL=lw(Z zn(V;h#tP#-+x7>Un&vd^2JVOIlh3h0$N6EHkXq9u+&`?I<0SKEflwAHpde{YwXYMVz=Hi{&# z$}~iT2%EB$GUUMp7*KamRdT9)2nsNtvy|EQdtu9H|tfvaNf)vMQH>Ek#IQN}3B3g^>m|2hgw431Iq)Dtw#o+FPoM_057#Hw^e&1J6}&1C6o4j2r%;-R2bE45D@S)V@oOCc zW1V%&4MZ(nY-Nw}9^$@p$#GGjzIf#-0+^so*)|*8%QY z-0D`nyo&YAbxu`7kkJKlh{FaHpj76}c# zAK|w@N*FAVnL{QwF>IsON%BAnrYb*$wPUu2JDZyzDx*|B3IlOfzW?dx&jVZ5cZE7! zvPZGj8)ZyJm}k4(({d}qCNz@nBuQIcS3k(Yj2AYs2JeM}a>Q7AV_R5~O@#00gATP;T+&~yu5@wQ#Xrqu`Vs`UY&e@STtcodg!Zrckfip;( z;;V9rjUCH%Ml|)N&ER$D17j>L-wDmA%!%|-CA?!Dg%SPGVt1(!9jJR4KwW9ZFlH4{4t z8YH|NA2*OxxT#EF#CQh%up--I-hAqB?@LpJP0C1~WKG-1RTKC5pFFxL`i`-+q{-g* zZx6Z>MG*~8xs=&}A$hWv&^|4adMvChob0DB)Va`9Cp_w9+u90K0V|V|l53mtlRGJ< z87JjiS{K;0jj*2jv3=(D<6p{@7vdGPgJXvK*Z{(9t2?N21uI+r1%DY2 z*C*cuFcp4nc|3Ex90!A`i~BpwSCzCmEw1UW@461FoEF=HFVJDta>5vf0(Vwog+lKJ zSexT^%i8DsG9bPDPmlH3wj-rQ2f?tXpLQ<_FXsH6mKwJIPHR?0E5UVx1s5@yBn+5~ zn-eA0IzbpR29bEL;u1sP1p~*xH24*SiAGe02!t5u!(fQSw-`0+E{%^}QQ| zrm{yO?O?0z&g65#nxE`R$<7}pfGMrWWVumJ9=!bLYZ3wq5$?cApk+2=dJSXZiBt+a z#$(X$U#3ywr!q%E6?18r`wbKg?rP{-B~fval+pKSQC+~6IekVmZ5PuKc0^t1LGAoD zw!0g2@9G(`R@0YfBxYQmB*fZs=WVw?<*hTdKAX<)vtI(**bHkT)uQAad>28WxkHH z>%MXE0?cZstfBN!_7`{KrG`M1CJ>*a?&_`&kIFFaa)u4i-~(dqx=#7`-V3Ku_J0nH}FHC@9uS(x_dSeK`FHLyplBN6VikW=8_N((e1u zoX)sh!i`JjnBgneu*AyAT#VmU+`=YdEWhr72g_rRJzmY&fuqdsb*!c2O$=6Npl16i z(;d@t#!J&}nf?ySKJm|WOI|!v zcHLU;?cO=KM`ImxcP6D-3b9OJ*5RtOF~*oo@+Q`PcE&gzuJ?%>!@BF~V_Ju(fN`$M z#2KeY8rb5E?QuniL4ZyfLq#hTlswGce%vM_aL^9d?zl3LPtNn|%&k6+;H z%D8L&;Re?fl76Z0+ao;f>&oMVQTt`?xDH#5e z-DMO}!p`+g$avD7CjMIpr&)_=k9@3gi*yN03_Nga4Oi^-4$4Z_0~Hur2kdgf*X%5P z25y@ho|L&7fyJ@#L@ljZwBT`$#GS{wcyF7&IL)2!;_xh+1ge*eJ+X&k#`S3xKEPni z1}WX!RX`>)?+zjW!L5Xs6_0eU)Ix5SN&obQaQ@0% z*1Jips0_NUWtK;}`@Oz_@&~+`0H(pl&^Txy-SB_~ag45ouEZ*XvZDw@$}YE;|+q_$t8THqua(v|akd zM>iv5=-4^G=%wh&pd$tGk^Y#ffSqh)^&PLP^1^H1Po44%;5uj(IfrIZCT-w84k`%4 z{05d$R`8B()g@Icyj>cwhhonZ8I5_b6qi=m7lm>1X?3UqTYhAFONA7;Hb}qIALu#w z?3>c*U9Nk;$Bjtgp-T~yD!hxdt-58o&dKjo1(^4}<_{lbJNwHz>rcz2!_u?-rfy+G z(P^o5RK6N|f!Vt2ltF%-+c7Y(&*>Nark(@{rca%1%Ym-~mK5ELg1BF$QR@=SZdmLQ zpDPw1?yl9|`jml#Lz%Q~cAeo{Dpd-UCL zv6Zg1L(obU-70#F)+kj(kgd|ado4}UiLd4j8oq( zYp#k$n%ILjX@TcjD@;`WSe90q?FN@?t%oqEICPASi?#z$U85P;(uds9fwqX-1B}Jc zHdeA)u2CIfote+nJT>Pxcp(D62uqy>)W&DJN>kF|KI?D0q?)0kgaKZ3o98Dbu zAiDZZZ(zBykCJDE-St(No*};XY1btJ1s^3K>9gm~m2dd^w}tt>M68{=PMyXzU^Tmr znyyb^e*Y{%o4@xvzZ>hF3*{Gn?N`dX-~NvB(1Q=c{JLYPFwuzx(kzVi0>1SsQ#NqB zGETtO{u;Z?2#YA224T2f_4RL!@};*vP_7{mY|=jr{bOdSNvDzW4czMdcEy~bZ@FFaPWpK3e{( zcfJeeh+RtBM9wOdDYzymM?zc^JS6o@F-&iE6X0h#M;HvR2cAofNrbPhuh+WZEGu|po}vU&Gk^Ff#%VB2&pXRaaWa2 z8CbRlo${0qW5_^Cpp#lI5`P3HOlC4dD6kW5X$!)_0K&9MWpmsJXuEXN-FjV36si?2 zM>%oH`Jm!@7n-5KFo;rCYm5P0Kl}TkpUgLDjO~>fH0Fk}A3V`&Y>IiW`vNp~4(r7= zg1JirhvEp>&?uLgB!ix1TCw)=+qRqLx-QnoSk4%LT3$qp3mH_E zvLlo3kfSJpQPt{Cd{8hJe-(^W`g_{qsnHW@V}eH`D6h~~+pgu03LX2Z!bMmFr+|?G zbj2YR_0koG$ho7k$an+J6P5i`mKZ(0I|;3At$18_Y=>*WD&?zGiR8?fC>;7xabPSl z*9I*`)){A&3^~aMCDR^mJ3yMg;r@bx$&;Y8b~!~1wi8&jjv}P%N~K$$R$7j^*IJ8= zV23}5oE~G`EXO0*|m3;WQ`itB5zr52(l*L0P3`?l7m#D8PGq^kFuKC6!8UYV<$q_$x zaSKz`Vf*AqbU70zD7|Kz)q>&yKw(nJL(51!Y>sP1D#uwoBJ@=9)4#D<dtzDSdJJKuG!k`5|tyMCb< z;Uhd$j!57685-$v)2*^HgG)J?<24Oib0BWFz`=N@23)r-2Jtqe!Qc>eJ5J^kSRx_q(yCV+ zr!i@&wAI)w!mIrkg*LBbjYtc49hg#B;3u5;1s+tmFzA8*0D^jsw5Lv)!JghBtcUwO zU3G9Blz(oN1JahA0oE|Fm^bc}hve$x209GurRK4BHsiJN|H= zw(wnBDbGf4(>}Vvz=;#CX~W>1*0~0JH{=D2YJ#+{fuGVH0iuOfHiMbx#{5zNqryga zQ5ALaIIh|5I2g3t1E6%1GzJqdhL$5JVKL0Q!+Hvu&=Y}4eo+y6xra=$2hDZE%0TdX z73r3Hr=>er(E|dAQwH1$!hvWBz6>HcHX?#rHz#~1HUle><8~bg{z}+Yh36IC&*UJB zgbS{Ua#(zMkK!_6)J7{1Sw^2`lw@Omz?krdykU&V9oo#opN9CG7c7G;e9Xr^l}X&| z6NzZ2MDgYHG6Y;2+!2&0h=(eE#1oNDD-3E7fnK0{gUZ~)dY4tdD8DyE4J?hs~(x9Cx zj#wGw2(p-K?yunFVle>IxCf=v3dgy*k3vH8Ree^su!czhb9Q(JQNBF%N8VVrP{_RP zp1aE@fAcrXr$6_(a^)Jwwxggiu(C&`d*q>J@+vnrV5kNt^j(j||a( zA4{k+r%#v9KlNmJ-`l>Syz#ZKFBd=ki84=6P)+}(NixtfxY8?QiD*(U9_i{Jg9^zD z)8Q^~6a@_i3bLBaATe`rxg$;EtJ1wD%S-%n!QSKaiWf{x)jkiy$NX^7SFm$8=Wvp) zyGS=O6OVjenbkVCnp=lodVy;hr&bB|0$E<#Z5z}yKf(srmHDg1%6Q#Kt|j%?!M7uE z&?%lJ`;B*8@?5t}Dc@_y@|JppS@O`|Hf6nRegvjBwWZC)x34y@wdEXo`l6RIk@hjQ zQ--+T(m91D883q%TKq8-799kXUR#aTl0!P}TGC`$}N)5{x8&#=*^&NRsqIk+rcaqbds7K+%+W`Rnv=$u$UWA_DdLuSa+p>do`R;U z{o2r0IeX?T@WN$=jf6?)_@1;aabHj#&}OZCq;;bx{iPSE4x{i1?NIT>XUF&sv`NeE zF6)Et)2?|P*2wi0V)!svT2M$-T>G?KF*xwVgi1jKT1HQJIsR_6NMBXb?Ep8GBZPd)brbkgkFlx5=fx}6Qigo*G!`|wV{0KQE~{8k ziD;XRgQPO+Iu>PJ+=Gpyp#rK455f*kIf8Z3-S^#(f`d8qG`s{m?kbKhU%iG^4|7$4 zi+XLZZSs`L9?Dh6-o9$t1Ngzqx;dp~(j=}=9wjecIliD-Xus>%&Ndq_z}uwdQjG%y z%*f=Nb?&L0^Lv9it6N(=$p^e-jCFbQw4OmN8yE}csKTfNsS;!leDFiJ87p`+^YBpt z-bLWw0Y1j;&?4I%{nAIg9_EE}&A#gPwYJU)T_}6ju=3eL!FGh8;L;|Qjp4U|LzFns zXoXycpf0oEs7f9KWrsE@bdcJ$V2^h6#M|5^FZq4u`=+X}uL_u|3?>hPs2esulQyZe zuB0Bi$ht3V?Yv$LDqj??EuNxTOZ`*puSyefN7p*(DdS2kCF{3OZm>(kboKMM^;aC^ zeXGcy%0FbjQI{wD*+A#X9(XBjuzcg_2^UYWQ%RxsgmgBi)AH^5V6y4bL{HwbovA}u z@=m>W?pC;X&%Sklsj()7nQ*WlW(ESm0YyBYwaTD`0Ri1&EWiV%VqmFDZPpEO!*QW7 z-xF8-2rqsHmK0gr<2QHPF}Kme!c7FN?o=MAG_pJ!-Xmt0trKc#Z~9=nRn*im@_pMb z9q_(z;(tEY@Ax>!sgypbkFZSNoM=WX9GK{Z$@WJ4h4CNBah54&!-WKv;0+SYg6IjjIS8u)qjDn4ky8VUSTCoE$xgOTlQS#TpuJ%{7N1 zW_uDt{;>%|IRtf_J%+*sQ+m<}45Y`bkFmo&024mQ!v4Mwd~ewxQ1g?|Jk8GJN_h^Z z@K3zvb>-@3KaIP?X1R>A0=iqya)OOJ;g#O>L6-%fUWFJC$jVdeVRu^g4_F3^>$ z3t{>UKs-s{wmo+9v|t+IeK>Qp?g%A(pSkM{ft0V52kv_f3`vbg)DJ^pME}7-gg@r& zbxzGXF*{XOfcMnbJ%nWpyQ;uR>zf^vYOA=#jlrb2`@T4LtxO?k{nl^(oAQD0`~LFl zpZE{ucYf#7p<}!1T&~QNbl)@M@yon^eAd2{F)?RbzVGuw9rLPu@uVf^n{g5Jm6zUVvEk0X zb&aSnVC+>&Didrn-!`F__TTpMS{ODNJ-Zhr145wjoGe%7*7(^hkRn&{3dPln|)QZQYdC2|jP!5na0mMja2_f56LC`3i&xtqkm^b2Kh4C~33| z2;adRUM2>RNw-@SDzRXpT&-ZFC$>jrv2HLX6&|`A$*}6Cq(I+KIWoLj_n54&i$#PA z4c8|7-Ro5g3wPh!ze$J*FByug}*l7Xl?zN-}g+XYxLDnK&^)a}}2n>cd~ zNu9RwjzO-qVmSh=RT51wM&c7AM|>LGdT@VrT~wh#L>cO%Qs(*wN&u~A;&Mefv(QQB zwi_P`i^9&nsHAhfRM4HCWK72qc=#tQmay2|+*pBm7vB^F!6nuM0Lc5bpDRjtC_Y@{ zY||{k?-NsoGB;SSq8zh)`qPh6MGG}~J*~eCy6+q?V77Q{I~*&98KKtxD|Fxrg@Q}P z(-xLq{TsFe_;!f3hoWm_Oba0tHd-8k7b;*p1F&R8vb?qkaC>Lt$w*#Kp9_Ob@%InM% zPiyOfC&t;3ri}7&M?Y3>;CU5(>6?7NB?1F!OFcbGkAj8<^WEWH4@Q7ehaRZ>(#p+4 zE3_mO2R0D=bOG5!*|Uvea%&aEFv>KozQ$1AYMG?sKyhoWGh;(2H@Z4~L zv}9~Er%+nBqDU8vhg17dmEnWBFU5t4cD8+WEeH2qtEvSG?PE+KUQy!tzBnuHIQI70 zUU*O1kxSal4Q^Q0V@evX)y`ZC>7``L#^$w|s-JG=^=Zt-yrAwFTl&aNL)?V;fD>>7^fY zI(7oP)<^l~A^8?X2VVp7+^v6{Izrl_qqo#>qIvE!*LUexENNAoig$)X;iHSS_qA$_ zn;7lwf$!NsrgmWl#bVBbBb3@P1{mMMtV26BuCVdpG368)pPVEIE$4q|WE9RcE^!LE z>D*AEy0_)w2DnHwSN90tu*32v4Rw9f(%6j)2xIB35Fd~EpHtH4k8@kv)y1uK2%3JU zr5mgG9o8~UutNqNGZC-(?q$cR=0E zvP>DX>+G1yuD40a4hX zn=-I6_S-Nu?tW_l%1J6PsHZqu z_sws9OL@lw50`ho?^{svTp`N-cscvP{pDwW^;aOGyUB9}1D8|F|!$9(0cHy#+GmhYxi*OS7(U32Tk&H=1Uo<6O zGQM!Oj1T2hCiCWeRThZ=Be5ij=0h5|tAexde;F_WpGj#yhYZT0@)dZUzU>X!Km+;45mi~P2H z(*YbB@KLLG^Gt@d?$B1Dz?)HwgbBU@bMYl6&lN*%>RVf0+xEKSir_(6KJ(NY8IZU| zv0J{1TTo6Fg1)fV7grfXs04p?&S>4SqFHu>3My?hR?_l8Uweg;#K6FEN9IyST9-@V z<*t1MTIRd-*Q8R%JkqC01W(~Hgd!vWV?dn0*iIb^U0q{spwM;4+Hnv!H&LWSX-GNJ zOkrjp{e-r|xMt@N+HSCBtrh&OwZx(>q`W_ji;~J7kA+tdHddK2I^gF~y5kCh(!!IA zX0cc?#d3$bB82dJk3Qx2bMQ)fG>l?FLEU6KRD!7Vun*=F=M;)m?yCTSgN7#5GTNiZ z58`z-{Zu(&pH;4JZQ)+Uw`+?^8EGz!p)%luqCpD^UAFAMjIs1jR}8OJ(y6T5z`A4& z82GM?w-zIg>1o!dQ3Bs9jC-P#;U7T$L)(CtdA5Z9Gy`o7bn&$vB}V0Ecr zuQGm;qrcrncdhYMHQ}oQO=}uBQ07b(U>pPKiQmFr+;ly*ZwiJvnT$GAc!)~|s$K(z zo`}^!s8HD-OBB|f@zL3|b$N9O%Ok8|!5>eQQ_rrj5p} z*#!Pe>x(EQveg-7)g}rU+i$G95iHqU^8|~h*=_P!XWsM!1FfT`SkK1bIl6A5u*Smh zW;0=V?hpc|Fdi zmHdRXg6SRj8pzu5&7J}Rwx^p(s-?dnX;~1f{2{DI`R9DqtxtT}VJ^8AZ{X6k#=G(w zuCv#{W8*1#(8JCqc_+up`1Uw1f)W;lc5qcI0&!9t@nZ^&5l^{RsjsDg_uSO(i^~Zz z47ko+a`=tBA+1v`@tTjcl9$qc%k_O}rQgaTZGFbR@qLBvSd{@pM+3c6@Kg&h$Hy2- z(pVL>TD(NgV*FM1$U_7~sTG(hkONSiDDN$jm+Q5X~8vqi*Kf?n8^in zm3+MG+;Oxh660xgOFeoOaM+nRt#VB)p3 z(W0xe5Jd~~Kw7V5w93I&=5tdK1=ev*>+H6qEbEai zmB$G0N(a2QRpAZ9PugrBy(z3znniKObw4m}c>vpC?~d&=KQxpuUX;T|IWpHU zR-r>EuvK2Q{DpnJQ_>x)=n)x%QNTKct{Jd>0Izwn1WTe;i57^!tEk$Cm5(%-Revi4w&xh;5yeA`<`kQwfJi?}VtIrVU7JJ@tq}Rk8W1+HwA%Z8r;>D&{Tl{Gej5}=Z`{$i-XLHZc+;MDp%YImicmV zJ_*Ae9Xa)Q#P2-$3}~iuMZaMQuse7=#6(!P-I_GZBqF?AxOfhKdx8dI2LAOAzpgxV z{{s+Kf{PyElnxlq-~RNc%D?{D$I3tb>7OqD!w3IT`ToE1m&=F#<^NN*VT?ynm^}XE z6Zr5SE6-efK5+4u@`Ztz8^7_}(#}O151Yktb(Z-5!PI~Qv;Hg-~=}_4tgTD(YcAkpFl$ZL?|bl-WsalU zKl6o0F_XvIfN@*+-OrR)tvpcv=zsAD7Eq6t^DxT_dNL&Yz)1##j3~6WV~yYr|0as1 zEa2QX(4)JGH9`22q-Zx1*`SH-vm5tc?CL;~KqgQssVY!pz`pcN8G0%306TgzlQfX` zps9ZIiMyA~8p(04E?xE7d+z2yoxMzQuDMRg5Xwwyk>KvAsS05z280c%ZJ+nsl$|-+ z*{(63a`6?VCVK?EtUa`dj$4EGTw~glI(Xu@w{1sy3X>yKL@V-4KlQ`OdBCdV)5Eu4 z@Y-e^R?OU#ks^d&+r|o`TCTOUN`}Cd-z_t$i-2XCEI*qhj#<+r`(!eRyk*Or#(Iak zMi6!vp(i_=2++Kv5NzO0S5@PU7@)XiOvNW*7R4XWrAPhFckp_DA10CK&SPVS4Kc^J zQJThTO@^8|tAaor(c;G#HO6(>b{CQRaU0{EF74C8Q6by;p$laPp=_UW{e2mQv++s{ z058pK`*w^Ys)dI*vqrG?bu27(ff&KvMuA3VT%k>R6JZySxHIh0tOjx(MX8aUe)>L$ zaIKPN3!zb~gehoO8wgm7A>rW(Olw#N#mWPESH>&<6F~Dv3#kEUvBJLszqHQzrFEh( zQVBi`{G_2OtySVEbYx6{t!}X@VH_{54BZScmPM;@a$Qlu;&ImF#0xVnfg4@Mvk^{; zTJlONWK=M062nAHHm<=c@z%zm=A39llQnd?VT4rnc-V5DoAeV+z~emw4d2w}pY*Aru& zrh@Wq?rN-?62PAKM`hZnNTtsrE;-x+4mG~f7D_nROgf2jg8CzWg5hK7b7j#$<7i&5426eSgW>c3)h(gx{1_zI!PZ@q8X1!g`0Th zCe|+SRw1K+?>x6Yl}0OA*ytV|3n6h8dfFinb*#JyZavB&2j^BUu-UX4i#cORO#nDu z%B4?>#9`W}Kh9loZtmJb`XL?nlq}a=@oWWaJ8{+x5KpesLQ}pzE{v5T(gxQn<`4Q> zg}rRp*mlOmXVL+AA~(qB0C;ZvBTpKGr=m@|K-pL0ZfHd|+s_fsK&GFC(9K-e3KcyW zcfj1!@}qH)5$_e|x`KZcp`S*08sD1tsYeU0t#xsp%_8X64vL;Va6WF32$jYU><2b+ z+yqBd_NdfyO!=QwN2ubQunS!Qubhi<|7kJ?4|qeG?X4`0BB~1qzs;YGZ~El5eV0s2 zTlX5*6)m^LO+O0CQT2c`S}#Zwwf>gA8su9VYW)aAS$kc7oo`kir5i8>eL@$blz=vL zb}19QQ^849p?%_{xaEeMN~Uy&u}exvs3MD_xoSV0fU%}=a7fh}il_FMf~7fjE{Yi1 zp4;9t3;*o5OQmoS0?niPn;(wB7C7&QtDg?-VQC5tQF-Hfp;AYK4(ki-dCxdOb;VW@ zLw~^4xL4Xv`bn|Cj2z@`df3z{Y69oL8hURKblPSfL8>y-MyI*9+m86*hJ2!yNO@?t z1#J5`7i3MU_1a$_o9wrAI@i{g%Lae=VNqH4c_DoS4qAj+0WGZchyx_Us_d#rOCBGk zv#G@1T4$}QmY=$4fAA7)8&IJGeTZu)G-cRRw){rX;@adq1LDAjMgouY!3NSEVW%6l zYp`zFj#1)qQGQ}p@r>b2A6$*};8Azrycf#yP2#V;a?VRLKxW2@SpbhH-z?YVaSD_I z&1MZ_ZCi$pc2M$knQTMIUL(*g71Bd`OF*-PlxzB(mhMWO*)POJLwB zGg-ucCk<6Zs0z^|pXFMXbwlU-XQxb))h=d-Qz$=-gP=L17CWOdhRi{k z8I!m!UQgjmuc@#Cin~55dmH6V?|OTA=ePawvU+*0tS_yF@pnP*v04rw?0n0+zN!4s zU;gjQkN=~8P=4ZP{&AUu8UC9;{MXAfUwooG^t#uU&wTOGat%VNi^a+&mP80VkG%QK z%xf9Vo$|oF_m?N0e3Y00?D{uM#|i}1MPi(`E0{6Ovq(nEuK6YX)qz16PhlN2z(T!? z@@f$C%45flhQ6o}(L%^Y+IR$$w6_BTwz{xfERw~l)8l@1y^_3mB7{3&x`0UJ zfB4V+MdC)BEzls0%pef0 z8EBS&8#^dp823din4aO(Fat)*AZfaOiJe>Q0K!};5X|6upmo6f#j6?bcfaNB1i79o zk3IEdxs2lH(Jwq!=C94if@rEcUYW5- z{{}8Vwg)=*QcH1bz%533E;-Jt0=@;0x0SzlFfzLE7%4{{TzSNIDT z@3`jKM(+Bqpb7KM9T~iX@gl45x;{FBFD2dDMt?g_(ihw0-0<7Fq&+R8*w!gdgwZxH zIeqs=vNN`n#Gka6GU~d{4IZ1_cY4OJ0ym|3!{B^NAMKx3ciH_!c(W`|G&(hVtZWg` z*A#0ZhA8yZVxb@AGesf8x0WSCT4R9Mtm3KL_sl0P^du$gScPtS^2R8q3yxrUun!$n zI#h5JCgact-VT$eFs@*%FscGZTvL!YPK+fFV1};JN#RPmKZ_D~o3=Z*mQipmFD^y# zKX}AbYfzp5Ov~`7=5aOyWd1i0#y6-(m>I7|g>-xJ7d?+)VIQ^Bu^+nVRO=R)U&_?N zKr0Z15M92G82sOjFq9sy4HF3c)~UP98mBr6nED>u4iMEkXBFzIFeCj{__H48or(@! z-?nJGd3E*J+~hcM+XBui{ClFAM{ZBxj?~-pXzc3FGLEuEg---UV4?++ii#2F`YN$b zZ0{!V5EKHs98KM&>0yvZGmkI@4rm%5E z$$?S^7%X7nqXOS?)s0!VBjez?TRV!|jc#(jFa6L$WFK77;%Jy%XO)r0N76zpE&wX& zzy;gonr>fQlLDJ3hS9L*YL*>2((GP9tzLbHT3& zQ1pKnoH3@28zfQzUDd9zwx1%#&-nDo%;jwqc)Wt86DQQ|=|sWC&o&*a3rPAn&i)1y2H3Ggb^4FxbNa4);1>?+&}0TlRB>_1NV6s`#gJN`(*o zqdmHK$kR9vox3Wrvgt#94@OXc(`8b8)nb1D_qR;~d+QKZt*DHfC*7!uY0KDw*_Y0x z{rqnhFl)0qt03<7cm*Q^f#{N}iz|HmqpxD2gKaHQ^9I z^4vM)T=2Iv)&5o)r1rJLZ&T<= zb8$UwXtKl&j4{%6PaJlfRdU5*4m@*%L|IGawd+AFQYb=IqII}h))mtgyjClIS&pe* z(`J5qOB!$2>{r`2H|yn%jm>5(OBH(3$H0XbBtmcf3*%-zm{KW)YUt{7nY(re5m+ zw`gAQ%V)x;#;esrvMP8+2)8iWWv%0*RbUr)Eo1grM~%(oIM=qgiprY^A~%J64)nPP z-K8PU4f_gFrQRw}rG3C#<^BLXup0;_Eim_-xRmK%aV%vqG~2|`?6HB1Nl!Kgg96}3 zVpmx&R(V6nV^gf#@?zD8rDtcsU9f2T~P-Da@!Kxq(;&GXI z0j7tRF#%QVWMVN1++~z0i1L$?T=X5xO4us18D^S#L|YMBg5@F+(@m=q!Gk~yFnMHr z+$oXhXueBfnapPMr|ptJq}{YxvX^p9|GdYOG)BZ1StrMk^Zx*yx~b1T}}hR zzf&za)KNh}t-egU@e10Q3oaxH0NL!!E@N3@pkOU~Wb}f+w9%ci;3D;Cg6~f3>iROf z#XIqJcfsDq%4Zvei;SRVv&QIQNh{-6;TRWS2$zLuxVh_Y&Jw3k4Z&ESoH|x6%v}Mw zJLNbhaozp8`?zNJc7D0cp|rU2^f`9(Fmi^NZ&3EciDOKTegbdKl^^<_|0i6YCd=Rc zk-x=J>X*vj{+s`6`M{gLpVO``m5=`NFPAgySnIy_dyhY!xIouYc&Pl40b54N zdSb~Sc?a1w{UmXARFZ8Vh~0JKG{OJ@rmdH8lEKz2S8EyTmO)hkw6%oG7mM2b`A5q$ zC|Np``Nc=SSl<4g_mppY_j}5Re*M?V|M}q$mu&+!Ql4Y1Sm6mBs|a$}U}$B)V`0L$ z_rX+cb3BTIz!<-cbFuRL1?b%Q2p3b6S7Yh(_Sd|jJa+N9^60ZqXE*&b|M9aCK+c>z z6`|t_agU^XU1BUm2!z3P5mZqnokDC6p0s#fsZ^!9b&&+yy~qRqe0lhH|h>!2~#QEPRiH8uO>fV?R>2rOFL9t)cH}{ zbGWUoVkhM{ZLDp+QRJ6%(ahTV;Vezm~PdoJkTLESGw!B0_u;OuAM! z!Drcva~D;5F$P^Q4vxlLnu1#vt2T37ms#F55A-t%CIm0rVvtz0456D^To{P4k2&+u zD_=!FP#i$lmarDN#{3$`DrK6sYeD3@x}wQ+D|~pem9cw_MWQ>v)Hs$NIR5M6;TW5w z1uAJ|^tH56i5{gqrtP}iu0Z3pl#w}(+YW7&`Pa>Ma8l(Ef+p9}Me|vXiXJU@Oe&cC zt;>}#GB%F^{&!HUX|*FxD7e^Wlk-lvrCrb+`=o_WOCuEgjJacS_joSq^6off{? zpkZ8g`>+sVeZ9`ww+y`Iap6``B%L!r=mwIc{nX7&ml%Uxi{GBFrS;S(IO<$i4cyVK`U;y4S`sY* zOKGi}BI4l`N=4hErAR0V`K`}m)WreEP-U60RA5eQIdIIiT6}iStRjR-Q^YmD{iyhi zbsjtIDqW(eAb7A!nr+q_Ie#ruJ*`ItK@>f~MA|px?2?cpo z&X_V+`Iax>7jjKwU1Qml>v(8c=p1t+M0%sLSfz*WDrdEdo@@%xpU?ps@3N4O#3Xgr zank+Qc_o%pY^8x@bdFdjf4DDS!Zo?1In|xlEj!Ds`LekKKn!p$?b>19sOX_8-l}!k z|5oWByd3}f&upv%=|fd;*21lTmQjauOI+?}ZYK^A15o(}aDg$bbORoq`BB?c`E6;A zFsGNmCZ$$_>e}N!%I3@ew8ieyc1a)iJ?Tr@t^NVDV6Mw|GZu{7Hh4NP093$BEmo}~ z8@}AP3~8R5EP$4mfl<@b;7@95+LAWZ`kSWM-N4*6(H}zIlzts*tHq=|agZPOr;a3l zeP*XfEj*$OqcF?2eJXjBMLfAr<<2N4_l@xxRoOFyRSZfV6_fTm3QXS93QauG;@le1 zs|B8(w#7j3%p0_W2VN=jt5A*w4`XNxHdW2YP$QrIs>savV-sl>^Z&0>~ z<^;9=X0e38IPrE^&{-*J*;Xf^<5tY{#F|t|3n-(aVJiCC{v#p%YEu@CulD7y?!Aor zSNG~|-_m5TnzDjR5I-3nU9lwa`l+g!R#WY9s2bHvBqdG~BAHhe3w~VCjhAyBcQ1GJ z+^J09IPg-2C{{Ono}e`nAM$yuK6>Iec0`&)p*|Ak z^vcuEl)vz&znde<2g;9ds@C`Z#SfOB`thGA|LtG ziW0`>qrdpC%jEBTiXG3(#1q2WiLy?gK3Oi$&6m&oAO8dwyV>&IuYY@X5mwhR=jOS} z9_u<1L8MnMT)iv_3!kAoMh|>gWyg60OQda&Z6yHa810|CdKEX9T^Q$O%;7nW2zZUK zQ~u!H_mrofe=adJTx^~`_iO?}A3HXKg3V*z@hzqgF2e2%4-*%tk40BT*hM&tBlWu| zX4eeZ9ndVNtTZ+)^!rAVw#lUZe#ts&g0hO#p{^}6)IO^@6fz~&Ez2R3Dfvxm$j6O3 zY2`uLO`by;eb2|1p|Bz@hY@ogHtlYeK4}*3w%>&INBr?ei zhcV}dYnXFcT(V+e!{hu;xjeI6%aj&*ogaR+6?^YCsXot}fnS^7?*n)??;SjA3vcdA zmt6nEY2$X--ZrHz-J1!%%!}79u#y#5-cMKR_Hx0CC5uPdnFf;J& z2>5OwSdabDwMOMr6)GSHP1{`0wiplNze)S0jShlBp#hy&P&`83aULXPKeT#z$jQ1DfxSsko-w(;~EAJkn%dg|)af!gD{i4OyGl z3f|mTVc^>7b9d@h)MQ84p~uo?6@`@3PfMa<@GmDcpmrv?;JKod7H596q+MYH zq0`xBT{A#+4fHFFw)PFmOa09p*xEIybG4GPbKM9vu1kQ*2|x{6S31X}MY_q1p!A=h zkG6RQ7quM@HrSMggP*D?ZJW63NsEJQ^Dnb8F>gK;IEe`bu7MC`g4i&+t1h8v$r096 zYCS4ev`lhWdXjZsg_}avIvhfR_EPpPJir=3o&hssuMoyM0pGB6`6BL&qbQYV5B=R< zz|Hu%XUntCKEvslm!jmkhNYUPQgu2UO-~Xhk4)mOht-m?>DJd^#7P6dcNw>?wSl^c zBVC+fO&o(a@%^MajS@TL|eA-FVr45l^d8=lJq(zUsjme|S zU8{1qf^D{Ltz5Jufu^}m+keFnFcPaFg=DaU_PEA_0V&&!skr4aKNZexf8-;cyWXe( zB}){4R^nU`?_4Xj=5{^OB26Jz3oSPZoqLXjw4s*oY7KWuX78(DvFy6m+f4exnAuk? zr5qpL<|Ey=F0IEExao=AZ{AhFkL5V@Nee35Zu{DPxS6P8yuTkoo4G6KTzjm?`Ilh1 zbgo@r?Mt3JkDw3XHMl1%En7u_&z*;%7vO;us}H z+p;3-P`Jr9r%%l{#rQ;PD97A>TAp`}ZRR|vuJYCufbh-u?F)bEBdhqK$$HjLU~iqa z&X3AJ$1=?o4%Wx5T1Wl%o?M>oay0VaHu|srq$CqRl4|)u#AL3ptnF!D0Dg|WG}yghoB=zCTE~+x)5Wz$sq@x4mgPQtV*7^ebe84=Ca;3IsFWLlV4_aqwft~ zG4Hehw*A~K`_Q=BMCub>AdyRyebl9Ligj)beqLI51q;bFd2Hs9Arrb-x(i#AN^o7K z9s6o1!dT#%>PCq@>1Ujz|5_A}62_v9>DNORbK@jRB-(nXC9w3)OfpR&764qN5wk`2 zN)AP4z%JwuCw@y%B21f`*Ajdg!Uv6s*|w9o!y2RD8m&0UXhs>A0f?l**mg9-^Ie{h zJ!ZOnm{Irf%-uil$qb|{e(2jNQ<-v@d%-8yya{EQoVa3Aj1en|1d(i@U z_|?4gLS??>vVh@NlG=sh#ooC!Ps?Dv*gLm-c568itQ^E9iH8!?my+D2Ho1ViWuQ=V z`SEC|cVI@Au%?l@KZ0Uma^eV!IL#D? zVSLyC5it@5AfUUWm@#7oLr6;m60Qki zHiS#m`jv(9$KU!$IrF2(%isE`|Gj+kcYkO3^FQ!`@`3;6&zGP2`#+JBppLLRICAs^ zZ97tyRBo_y8Z8uKU<@Ak^Ds=k{ZV#1SL52S!LwaZvA$5VKxnMIu1S3wPO)wkURQI z2urVg=r!fud+#Z~^xMBx?m2p^Oy{h-S8d9U z9rwa?a9c*rU9!+v?^CDq)%Ro~tiPpA&Qpp{YQM$wHu>E7r}EUc``dPDNg`81kvEm) zd**3V`;OlF5I9Gsl&d} zNbB+4Tj^FU#lO~IN=?8>Myb75pZHfgU=k-?t7MMyhMn^V$@}f-WH&oN5?Z#2KJZY6 zQ#b@3yw;9S+e7PCn6xY{29{A&8dO#RNk(f7%RFJ0-E;d+=Qk0OcMxiDkV*jJz=b?L z@ORsjj}#gZbbU`62P#C6H8z*05eYA8XEtAWIKv5i#stxQ)fg-g8+r$$D03)$fbjtH zZUV;e$n3H3jGkb$!M$-@o=%)OjX=VB5ALWKIB|Ri;fI(lB;AZ;AQea^U<6fQ3_>>* zaJ0%)>Anp;GRVGxMRyTi4Dj27-UuUIhvbW8;KlD@m`-W=2Er)>=sue&xD2pt!hb7ev3Jn8!YRRyI zwb3d{0qN=t!Gk9dCOlqC2E81o&y4*M#&->6`NFluC|UYZ?71m0&|PJ1!~MC($w?>z z71kE!7YW2^JUCsTfgL;21MEVp=pm#~PL$MIZIkivD0Me)jKd^;IFp1scR)2ZVHm$Y ztOTU@2||ic*I|uTh}=fNSjHlDV|%?EpT@eEwN{$c@q{?wrZtZiZz@WvFjZaEMu3a4 z^u~d4*}WE6G7%XM+Ovd!KIzWvJaFE5?pzSIP2R$N6VVw z;Rp&h$A?O4%iyPgop3T7!~nRKH4|7u7K<(wnDUk?8&q@++89A7 zZm_QQv!L5H>7ZcFrxwLnJ4UVI1$dFidF~ji@V3q3i57(_*uq2Fcg9nfIg<(#YG)iA zA9xCB88|2Xr8uFJJ~;MqqvTsUpLc0YRXkO?VICVOO%%@^L;GKY=~9w|L496SD`m!# zcU0mVr^USCXXux6DwblDrSd28A+%Kl6#pDp%aHb&qKJ~t(vhlorEJ{XZJ$B7=~CI) zZ%zP&IEVgFmKNp0SEWLi_6|W~28ao@+e1+*u0i()u~HPr?X|-gUWmD3e;Dp0=gKDV z%UqyO!oraePo)`-i_S-OUyCFa|Bi=!Xx;kkRNiaGRhgoWJLAZUuJe}Zd;EcpgT37M z9cg+LqR&SK;nviKp_-968FGcLarN z^%fX|j!tw@G!y@l(Sv@9JFM-%e-nCXFmGKk!8+z0iZ$^MnxHafi?tx=CvMVSk2cr( z%Hd5sD(44lf*U2ql5?Y5OI(%yDqn3fBTYOYXdv&23-*TPDC6oFxVdXxCP!ifn(jur zb3tZf7uqZyyNT)sydf_JSrhNHB#pZxQvu-SO9rN8R)eGr zfZr;6yf1;5AT#`wA`&_Q(IhjNA_v$(ei0sm1;TpEGM$OguS}L`SwRp9sGfxW*DTV- zM;}ZlY&5OpuM^C?AZtqF=F9188v@`}Kg1`dq$n>8VmStL-3K!-!|%@MuEYsqq7~X8 zM5+g4xPX$!_&h3lwBAu5afj33nzJZn6p~fYc%--A?ug0sOW@sM&}=csqC<2+@Wf+S zftem9o{LO)O#V@jxXYY{(p^v&KM0tM$T-jT!j!_0P?q=iF^!h_cL&;lgFTqNlLU@@ z;P{yYb51LZO-Xv217?K>bdeMB}^5x=d5uQL|@;Ael{ zFRgRTQ?OvnWW;6Y_K2^caP0|%VukH+Jr7e$oh=X1FEdUnxd+mC+^WKpbT4Z<_qMRGa0fE(Bw-Sbg(Cf3f?gP7MoSnq`_rUlm8fU>jIz ztzp}p^IGL8uo#Ob?&{PlOnq*@26wSY#>D9uuELJu5*8J@nJ6>}vx;ks9(2>d(}R7u z)Daw0cQ51D$TLYl75=n7*gy%gjZz^;O*3AOU3QtFHJ+Muf_lVj+tVGkd;}K8-Xu*K z1P6}Uk zkA)r@G?=vYxenqIg%wWli+**>>G&#Qy=^5NXpoOx%TwDNGjVGHWsu`AK2d|6&M=;~ zNde8&p3bDWspX!NUWJ@i5#pmlofb8^XoVhtHy!S8v2)P{_i7;Ds(_NtDj=^DEPQ=~ z&j|PcUxSXXjO(hcbq;mnMNN5Z5P_3D*JmwpEK745DII+icE(8Rpup7C6J$rxgWw)K zVeNLOA3PC{9lPKi@X>YNV87ME8mltuY}YTxKASUaR;w^p@f)yHhO~vjsfs=0xiDrj zJi3w$_G@tnJh>-r5%!*V)zU&P$eTYROmK7``l)rGiV5+|&2O!Eoaa%tfh*#&eF&D( zh6L)Re8*b3DRn}VoL7#geWjH)(`j7$4m`4QP!CUdRzKzq-Jn5^Tl!|%^rwgP9Xtik z+l>bEYemR|yeIv#j%>!$OgH+ZHBmZ{$3m3@J8R`A`b7_wx4NuSDPx?C1n$)uYdhR1Y87vJmjW~1z&N;T zTWxofqMD~IrofD{-8>NmBg?WW7`~0?j&sIQ*!YiPvdNOU=ooXO87CJ^njdtqeU2T! z(*IgL&$(l=HQ`5Tqi|8+4enEbR!wOw&m1eQ$h7F`XFh0EIXOO6PR!y70PWN@x3>p< zAJTOTS1#PPIwCh4IQC_iwST+6uIHdFFm-HLZ^>fYmCyHBXQUVXu4m8`H;e!Xu&MA9 zPox9NOAeVzIoCl?wUvfSC+t@g{9N014M4oP*YKC(jLIAzO8a!N?IH`we^{P$PzR3B$LDApwoXKSN(9n-kLaY=^T#WJ~*;l+>@L-|YN{xiXL+7a}f*o)tog z^D9i2e``7{DBb$(<==Nc1Y~j)sG(*MAD9CO#L?NKWfv3QtE*Sab&mI5?!O);XcS8f zcemWh&=meSv2LbN3K?g{UA+!F)&^mg@R%h2?kMa8pQ~THN*{$F@mi%&KlrXdvcAbq zZ}Le1Va6cD3J%%zhoP7vj(~Gw73So+j490Q091C0AdbOj5Zt&B11|cMze51#1zeTR zpy+w#@yEl69!0pBKpCO3M!{edcd72wKv`r55b>^TT*JKk(zWuFKl)?kJHPJ(<>x>0 zOXZ#Kct`p6AN-!uhjL=>3tvFy=@87Y?y^cu=TRIjqlDRJS7DU?_ZcJ=%O&Tdgx?+W zQzuRm{{dxG=A`#=Z9^zKdGdI9@^e?1-wTbLo1u5M!R)McEEdWS_ z0+#M__4((^Dt$Eo=>EmgYUWEO(EPl&hE4xJvNJ?2}YiFyMw&%V#h~@$lWg8EEHUfJ0iY_TjGJ&GO9hs zGqkQYwGy=US}*~po1VANUgEdR7)_uJ(%bf1BKoCCvRG*^FoV!5$b?qHXx>P^TgTOV z=^r$h+@aCJR7ORCqoPTr@8-3B+w%WMs_meU7ET?mWYnChvfFs5QrD-09Z35~ewmlb zg5JoO_iGV1Zl>?{k@ZPtEWZyu{(UZOrzvY?^DQz68;g=f67B~(uHJU+WPJG0@CrlQ zotJ7}H*BP3j+er&OrwelcPCd?5d=|Mnw;zMT>A>yJo6}Ng>7+9D*$8sxZ7@kL}6r{ zk9BBMG;4@-Q~n7{Z*E4#QYs8k@)YsgNsksWNAE1$qHJnIx!l6w%Np`z~$KjYD8= zpd>emnZ~W@!$K*{JI{obN{K~;UjqZHL{mc6=OrW{W4dE~@{id1O%;LAqtyU)62taxrsj%KaNcWxs zxcH{ajVZ1kGU*7#)&~)!O)8@h;0dnmJ8o8J*)oRpr^*RwPzRw_Jh#oB2<0xR&@twV z$7IiXWIgTBg;%;~JFIuYcu2rj3#3U-icI_=#=$_Qi?|ZmzggBmkJNUjR>4l3m0w%O zie?$*opBbZiZ)<{MLZeXv%tGr$2j&1=Sys83=ZNZMf;7_Fo6PPiW9G#A1Zu2w%QZ7 zB23`&p(~jdOS%bk`cPm(pM`ynh=&933}6upjF;E0@jhiMC}1sX5N^joc+&I;+y+IS zf-e}r0>@1O+n>`SsYB(6b29FDl(`PBZ-6__8{4&beHk|`0|m2T1YA^>snpW4B$hYi zajYD0l@BUk)>vx|Ji5i{V++?&E&*rz?a}rIwALzO$+ZX9@D0XfkF|aWYu9-WBe2aX z7*u*5#iDE-Mc5{Aw~gYagzN9zQN$TtOSCqw?+Jx;ztUa$skIDEr3IA^JBPUn{Gyc3 zuSyTJcoMFT1>Fq{wF;6BhK^EykB-I?hI#O$UJ`kDct43p*C zp5)^~aoQEndnRc-2$AyaNgqto0EbizV9l;uAEO10bJWdK*GgZ};!x2lWihx}R~c~E z5!ZD4y+_*=&ZQ0FoXKxDPyi4w^ESVIF771QF28M?^UAqlat?-nLdjb#rdU@fUj*{~ z%6qt0`(j?PX!=fm+m;76Kp8vnHXjQY zN6pQPmSHR@0PPrz5V&6VW8?A|qM{FLbdk=$%UYj74T2lis}+9h0VA)TaZ#a{`9x0c zb!`W0qd~gm>h|E3x3?(=pxV|JUMefaWjafrUAN=~P-yxd8yWH+uJQ5KA)mOREFX)Y zOaj_N7gY`pgX3M5b>z_{Iw3Q_H8+gWW3whOm!J>BJD#a`XC-IS9ho%2byA825KBe*i)ty zG9?nKM9P9fw>V2UoIzPfk~7p~QEhPX8ko;-u4`FgqUbq|#v{;Pkj z{MEnl*URsI{?W3=?(toxX3N^SXEOKix%X`P0lSNG3qtG89?j7tcL61e?p87(S_OCv z>nk39m}BS{%9W=U%Nbm&x&v4S5d^hMESu}(AH|hmgq_4=$B$Ma4Fc}5>7GiW@}x3Y z?ASrkWt3H{8B}$S-PYazkG($&(rmj9`_8YjDyuTH=IPD=Eeju$1xQfC?J#%Mm*4r$*=G-Huf6tK zYp=C-^Z(V4eR}m9zw_Ix-+lcXtKa;+f3(_dil0eAbTOq2ruggKCmgmH6nvvCn4j9X zoF)*1*cklwYhUhnoro^jB_P-^5#{o6auc&@S1h5jelaV?xY*%F%LthJmGK}2)|jU^ ztv>V0CsuD2?D|fM!sl~WINPrG8!4W+gB&Eh5Lm=9*a&wpDS}qpfiV6W0beX`{Rx%? zt(aW|5*hno^3Iw#e(>4%`CD<*l&9}&*LSM?X@*%JTKgxB0^BQ4(=$KTmmg9j1_@Ip zx&9}a)=b#?TV3Y$vc4=p0Cvwc#kanVVx-^w|IXYr}^}afL*6(5Zd!ORPxWj1*hP#LNq8+(#MbjuB#5URIe%S8n)=4o! zqDOUzTh5taayDUza{5B9q!`k%&MnWtt1>Ae2n5M>&9P!|H2=*Pg7YxymBsQx8_ZEl zHoCabT`nA1groquQd`bP*XsVbW$fL_f`-LlIw?DiVy+CcTtRc1Q+;n{PaU^z-5dqG zppDD??TGVYqBx!Jaf19ov{}3q){_{l6PjT(B#; z2r`sPr-B9Mo%R~xws z!o9s{=1$Ka*8Y3BNf89j}5V zQV7ChB@q6sah@PP3X7ys>m?YUA-uMW9bl%+5w258iN7<-$i5j{vU<`6Nq!`*wHnJ&UrE!qleID59&}>2H@VU2MFI+0z_fD}X35;mA6z@zi`Ax;YeN z;nC&6e~duiwe`1dP2Z+}y|f>$ja5(U_K3~loLD|bEQ50wWS!s7iQ)5zMUVBpayHYx z?&bb{Xg!YJ?BxVq?V1`c{_tTy^Cj zz?GA%oq_?J7{NXmm}fIr%VQpb$4T&tj;Rq|pj|x*E|je{+9=x4xyCq!!N9inv`5mZsIL^_9un!a{zz0`mBD&xYhDBm>*4cG?&T|lD04%&tGnit?a?@rNj^}B_5QG_lE zJNS+cH~>Tpr-{?I`e%W1cD^zI^yP|Y;XZ|n5YbIJ)=>4Y?ecoB_$!u|$49AE*0}t_Q z69QnDyjd`lKzTC(K+;Y?2#L9z*g)Mf7uGoA=O3m2eM7+FFuTcy)mA}jzcTo!?d=Y<%0=HZ0)iH{J`5*OgY_J!FARgRrH zI4bxgW|P~3le)NaU5$x8ifE6Cf4FV8+b$t1q49BVH9VVuwX>ZMZHkKrDSaqZC<`{K zcM+_z&8htx5z~)U!>GJ8AXz zVxphCb7%E``m29^_3!=r|NiQCUizrTvjbEGTnCc^Usu;ui zE>PN|c1F2&*x|R>*`;KD|K)%9rPY7?cmH-Vey*U+<8tM_NEkmH~IW@6;W;CvV<(w}8~!tJgARPsEvB z`19>l?6+I%sP@;t@y6z6Vsf9$yqMH>)&CB zf(1sWuVJRVfRP{O=lE@KQtJm3MnmGdg%Q%Ox-e~DRtE51@=Qj5Drl|98G7xlDj$I^ zn0+wK4joGWfnq+G@B8_Ud6N8H+H1W54~m`O-5|^w)$?JJJ?k3otkb%m>)-Hcn6kNH zom%qhGyGE9(13y6d*!U(|6bqTUD_6YO=e@`-W2`xb?|zonSea~vOen9@z8~fDX1wL zdLKi}vTf*8IIl12bBe{o+%?4{AVd!{+Z@oR;ZghEODH{8%n^zq=Ga3Q>n;}t`F&4& z%S#y|HjLeL!pfq|wL6_qIy=R+wRuNz#LbEj%$o6L%7at6ik-XIIK}X>IHG*`%()*( zXiYJdWefT_ZeO+ia*7q`XT0p7v#`;=i4)PiyIJgQVHhh*8=8E>Kj1A0*@sV>*UiI6 z_rxSCzD7_2SCOi_ws1Mapym3QnE6Jo#9Gu-cjN4sK!qNd(Wq`Repd zc|xhAUD~01Zd%ry8Snalo~V!YhW4=@BIKOP%zn4F+{*&RiGd7~h6%RY8LYi;Ze6ME z;=N*EW+@Do8dsb6I!I*kAfu&9~LrVB`gX#Iqh+`_zn$x?MmQIp^w2I1z|{S zO>GvAl@xx?iHwfV<@R+Hj@)>d3vn)6moBl2V*OGe>%q;Gelhwx^%*{oaxJA9Se$81 z9Lt`XF@33CFlWJK^X6{K62ZL*_siHV)@L?mtjm-;54sj7h$7GCfMBMF;V4eoj=Vz~ z&cT+}irDLh9Z{pCI!*~qtlB2w8t-q304%c3>0xw^>5ZH??wbjdF~& z6<&x%7R6o78@>pv%*p-m>tq31Z90K5#gBO)Q9Qsi_&I$KpSTdAdvi>5gFIG9TK&Y2X>)=;?0qKq3KizO#N zZEvTbuBg-7(WsO^loxQu=cwPtg?s}4kAi<;rdzv>N%Jij3l6_DB8WE@+N1UOHGO2Y zkcSF!V(x?OeEK?tSusZNxcc;&MbpgVV1s9y?pOb~@Ky?i@re{yX!B;4NhgBIxM5AP z3b$r@R(mVaW*&tlda=Z_(VxnRevjN$8+bwVYk7Bs_h?){SzAfg0BtrNNMA1x->_fM zRD%8@ln1lwnDs805WXp*Cg^dmh_6T~rl47;;{%*>x3s_elsdI_QBW*&Xf`R!kM8tR z&+s;tWvytur?l=H0~`GC2pqu%;Lw04ZO-D8@b2a<*d7(f2|pvhvb)nepw)2n-r!f? zeh&n~(dDYn%jeng^J&-d+7DOsTU|@JwWE3_RqrSW`yQ*MV1;HM#+x6cc%1P)Znv_v zpX=oUvHDxNcs?5*uxY`O+S2{wpZJH6XDA2kn_IaQv@S5g=LOnvjS!r(1h zXN{t`x7oq_K(KQ|;W<7IFE1`0cSmc1Lo+BNyQs+I190hu$GO5Cq-Z@6je%EA(j@0k zfMo5DTFu5ZVpu}!SOW;oXU7z=_Jl*RvL;#NMQg+^TMx;g49VJ0I<2_?AT&V-9H}B@h^*!WLLI zaP3*6A5JfbcigS+^joH9Qb;*Osuu--8m+&IrGI~zvVxTf zJ^x^Szt`{hezweKujN|{i>|-#WKr;PZ6Ck$m?patT#oLffXN7O^zQo#)%lGvs;9h82AEaa_c#aK}B%+_1~}D zM=3p=W&)=zC>Y0CM=~>K_N|{7OuN~anOq}a5!`RR{nqNmANleQovWPygU3ou1sUyolqYlv2-S&2#N+!9mlE3rhO-H{Zy@=hEum z`q%!=)xZ7!`G2qe!~gI-FOM?jW^sO)@}FKxEhkkl z;bnnfe8kk)n4Nw~nUkHQb7zE>%a<>X0E`L#p8|;R(cYR zG&bwGPLXDJ4jDYkEMA=yeCn0fJ5B0iZqEwKJe~Z<^+lqGIamb$nCRw=V5Ed=fdsYR zwWK{7Ef0ZL5!wUB)`9PoyfaG9@WI-l&6G>b>(Q@ux7YMSIj+6upEM-o^1@*MWAHd)7vi)YWmmb=Q_T>dEjX?nUNH+Po$o<2Oj09)`=h_X(F&%m;- zbELnXg6E9iaXoAL=+y;|zGYn2T+CC2ghzds2kMe}2jD*&xmjZb#McU=GzG&OTb4y5-$GD$s&ERh0 z+1-NOqlq)Pxs_1>X!z{#r+_$}1t#YC;r;p^{JDx=>F8OJa9?Sfw;wASa@=%|i> zKGsg+H9#qCW|1T2n{u{Nm^`U2N`a%J;9I+m*`50xHy?9ue8(L*_!>u>7w39^qdG?b z*U#wb-P>7JrEsf|*2SzP8m~L`hf5bcdz|1x`A^{>$mUkca7y^SJ2pO?sMh+cUD47L z(P3`G6do6%dxVLBL*;HqLv2EspCbUHU(H>+!IW~WV4422_@2|U8gJ{3dS|?bCZ%w% zooCOnE{)zd)@OHmCmf~h;cmdH)~TH48llyCv?%Obi>jYMZj8c&hKU8}VpT)gKUO&1 zDK8KdEp90idamKvRtCTDa3L(lx0=cu=m=tS8mq^Mz)~NST0&Hjf(vvMHXu3ZtjZ@5Xjd8Lf3_7A50B z65n>?Vw3(+!Uby}pS|V@Ya1IxTU{SEE*s8GKW!tgG7#!%S@bj*Q(Bq?)>*hAF3?z; z)Guq?2-@CIe;+t?QZVonS+DgDc?AwU(f6K@JkoFUom`71l<_Ie8waj=hIiAy`qNmR z$daN=QwO+cOOHSyFi;L@wYG*8^KrlT`z#hntxaVbg$6wGW-p8|-Z!|zS$vAPNZKJk zm`}Tp*GM(ijCi47Sl+|nD35xV0$cUt5uVgus|gs5g-hckKIJsg=pSdFQ>&U^)BgU5 zspcKLjd?rnd5yU$j*`~lm<@x$&+>yg*%3_!+eLY`th;zj2U*yZT>7Wnc!?}WW*i2# zwY4!DNY)4Zg>`1|P@^gXz8z&MGVq+vFYwYRFq(C|Hjdah9c&D$XOvHVYbPif4`up2 zAMYRcrDuzkH`w$sJjeaQSJ_xbH{qUjYx4L~d>`cv&k!6W_kBRz_)TH+-Hkdl_@|!! zkJ7C7o|fUg;E(S6pfgJx$l9MWhxO4BALaV2%~oG@n^o6ginYTw2gY+C*hSf@0RP-G z<4k!~$HesO&h+P0YvjpjVVIZFh;m8rv)%z~d8@ce8{uzXJ8eUcKLOD5OiyR9Q=!$ve~^WlZnxvOH|eYDa?ThTKJ zv=AJ`V<9{XQVo*z+>wWkVgrhBL*OP%xa005WBlIbax1JwJ{rnUBpfxv%Vi=mb5+h@ zZhvW(AqWDYAbB8-G=#G6^`!G?j&4j7XaaRl$3^rK2Vtg#*?fBBbJFF*I<>eD~)11WtvZvDCDZ2fW4odA$_1UHWg78)E0l5ZEZ8bkDd`y2m{ z)fd0|yD?)FQB6d`LyN$*0)u|*>t9=a?sGq~dcGs#pTB->_4RgloeXm`=HFO=>G^~V zyU>pxu-U1NG4`_)nI+P@F}uI~>%Te70(UITH?E?geS9 zZVR0N%or|kgQs@6?J7{3jipa@IWp8UyYAj$-9>P;n~s4Z&{=p#urtn$=cQn4NB++J z;2X0}ptw`r+*UsO>L*vPzxh_KE<3AtAKzWQ6YM8OPYd9c+(@`Qtm&P_A8y8@4~<+- z2qMsAvhE*Rc$}8`((l?T&^-YL1LeD0)5dk)r{5UXUU_zoz4u$M-b)w~35{M-<<@4j zyAo>4GT)JaQ22f6>AOAj%&t)_t6lx*p0+RbF5l~ykE>k&{I+jQ*4h$58Ea+EI^&ez z4{-S4GkLVjda?B2cnwi0nAFqR^*k3)mp!W>pErdtm&&6JkwI5yx3k0m%Sz5uM%XB)v=9*Cy_WI3~CI+)Oy-GD87uj#2JBj5ZKT(OQavy_BkV zQ^1J%g~w#UWNuTgUwxeGQ8Y`v8d4Kd*qGOpsWFdUh>lUXU_L2ySmjI^ts7T!%OvQc zQBF0o+q|7gJi!p->X~Fb7B{LzaYP`2d+L!!aD<2514jb5_ywb=>Y7y{IF%(gr-b`p zNZD%*GLDNo4dv-#4e7X9G&1ru0qQAutV!YuQR)&bSV5RO*1Dl-wRvI+Rf!XAMxj$X z4^xn+7)=D9N6}lbq-3#vvZete8wXrwY-X%o)?Uo2MBgY@Zl&OH@}T$)0?@}AjvMP< zmbBaexXRkSy-yKExm|rbTyR?p?qsdC8?Mb}LZ>3#Z7qF!zp}y;^heC0=dNGxbFHNX z{R7uvI^ z)>afkx3m7S%l@Q@*!SwElWy)7T$_Syf~(dCR>cq79JS4s*{iKA z+--u0TXp{~3#Qh>c!`~ztd1MEhgk@58(uaOvSkUUDWWL3z=WG-$9c{s2NxMQ!di8N zTPGufojw)-bGPrG_PZLt2Q6KrDHkj8GyXqR{Vs+^H zD3=@iu{f#E=)@A(c{(-q4f0g3GSClm9}gv=Bcp%7{4rwF=NJcg<7uZ~7Ko7pt544E7bU zwsxZA9y$vKlW*zcRH8q%fU;+_8v3uAU=a!~V;UV`HG;Ug{cr;S9XOGtj9#u3ZC7@74Bs=lDH%2Nqx=RbtQOIj&n9J?p==&AS1u%W2o( zWbGeo+8HY=~*kHrh-Wx3ubl z%Qo(~P;#||m)5T{EQXq2Eb7b!n*t%g@Q=`%mkmBOB3aGcuA_yALK*AohZ&4ZoEg8ZNY}ujTh*Ue_e;orQVsqCy~|cmRm-l1KiljpW+={IGXs7jQDE`;XEe76NM`q?~7k#b?WXHqW#7 zKkWJsn*YPf`Tg7?+HyhhU30WB!;iUdr-iCjo$B##|y)aZO;NcrPMlL7}L)m;&SS zrK>GwodS`df&khjm;Y8fcPxK?;E(leewhoF=lq{pytDWty8jIdFiFq&;80Ttp3DL{>18Yf9_Q9@C=Jb$gD(Gw0Ib`lyILfLa~BVo5iZ=;SD-}!3qZKUie-cWtUOk+qe54EUjT8O(` zXlex?X#B`kT4Vb-npCG8tpSzX_C{vOx+?90=xq3K$(PTwKp z`jPK>TnhobYx9G=fLU0lA?3B_y|%5NeE2u!htNlWc?t*M`jF=6^C(Qp^K9$ag82gf z>a&iNmi1*W`}2W<%VOP{KE1EBa_Z-MU0WCMUB5Ad&nCZ?-9#v}=N^m+ipZsU8+B7=D7xk)8MOA7hx<)OQvg(Mipu@IediCHy|g zE#}0zTxGIsd7QwtU5u~u32674-xK4e`X%&|1;n-J9s$Pq5$4~1=j~DGymb9a0&gxh zKZYwDTL^cw=17xG9~_eQll4Fyp(N2DZeWVevu8*vZPr`ar?7e{goJATa1Q z01gfo7go4LX+k**k622eJFFvEP|OZx&oX`=Sgh+Ei<*>B;J;N&81=1()@A}>@NS9| zN;8TkWp3BjE481J`R1KlQxpp=@OHdBbVOGK{ zf%2nEPMYMpr$5hC|5ge-R$rG}s~$aivo$N%Fp9Ox5=V_I5DOm`hC^E_`zTy<|4wb2 zF-~E@MOVX|VzrwxUo0pJ0E$HP_(2LmV@LtS0+K}|W!&9~q-SggQ=t z>W4g48cuTe;1;*QaliHQ@I(T_W;@3O>jx&T?B-%D5{&qLTWzjv zw4q?0UcY`d7sPX`D{WZZC=T6?ce3Q|xaT3*2!3k-65?aLxgy=lqLWp+0}-sjSKDzG z$8Y~ZYjERD)tv~15s1hTP643k;pqny>qDNiEofR=_tas|qenGLv3L_Y@MD20;msn2P zKdJW+^F>5=UH8kU`^ME+_=k(9l=Ey!%Z1m`<53DkTw)Ir0++X1VLi;Mpt;Pn0G)3! z+;6QWkZyH*Hx2Z|_^Itq$J?&IoycC+1ij5-B!$yC3J(5T$4B{cYO&%a7i>g-@K)U4 zPo_XR6K}*Kin|iUgSovHxDWcxf`EnKw3#40>tDU;+WKQ18~2s^O&PTGo}!In&A2A$ zhTHI)(i_acMuHQp#*7U`@}qbQGLbRSR)>9P>nI)Ot=_C2iU_IK2hSHOTRVCPZ&NdWJvIkLk0# z#<6_CgLNj@OkAUAGa>!Jc-FoJw!ux`%O`F zqcJ+ZkG5J{Y~GF=Q1GL;2Lb%|43PF?WJ<=tKV#w^I`j9Bn9hPA(Z+OADrF`v**hbTR1lhZhOZ)B2agL=T;I>@TbhV;0h;;D;d?8L30 z)EKr2QmnH1L}SR}_}uOISTU{yx!-TS=I&}kX8%db)he>ap6h#)1x`w#u}Zhg3I5GF ze8Y6BHjJBXZ9CIiG|E0>+I_KlSl!x`-Rg8i_Je2YU#L9uaIZD?K>_U#+k`qQhS^c` zM-BowK5?7L7%C|yT@&TxcwLe8Vhc1i=a%v zE(BJ&Rcc3|*QZ@-{-gAVhrpUpD#;kWmkhFTI3`Ln=sL ztY()pLWKdkdaa$ontv+g0j0`2H*TdFZU;AI$$D+K3wogwKA6$N(Z#>L-O32(8BA&n ztzga&4vY}lH3LIYgh{)eiT~hjzfZ?x+`i2MByDMRGn)qs3+=g>i=FDf0!+rFfBB1FSl#&6>#Lhtp!|bh|NF&%_*)5rcM`Z# z4#mu4N-m_-+DuW^Hf-Np33N{;{>Hm8y>I50_2LV!WLn;7RT!{@fK!<9;BhKbTY|tB z?L_7p@u2Tde*D$duXl3R+wa~8_6viA!Dn$Gcq=B$k+2j=?mtf1#!bqR=-C1c!_KXP zyVhn*6}S-unU{X-rI#n5=)yQGOo#=LkO_X6(1X5N)m-f;T}vvLjs1iXt}STJpm%u) zG~C9xZ&(tI@9Bgw?sx=Si#x&BeQll7*Je>o8PT&l!SH%8zxvQ|mc=~?3vLM19fN15 z9Bx%jxCpNoMhE@VH^L$YXHm3Uz~4`EF`eF>+cA0b!|(UaP311h?Puq(yUUzg<}do} zw+|SsnLhc3VH!HS(EO>ic78CKV|HySpu%1Fv%CYxwZWrxdiHvK?>l|Z+R%eBBcD#c zmusyk8iBt)tkbh~X_sn!k22@@;WE}Wsl+WY=wDBU%&wvwlFQ)Z?h)Q9M)! zVPW7O%qc4(8-v#rqv{ZpmsJeq)2V3T2r4yrPLFDiY@B>tkocF!?Z!M7JMCTygmaYX z(Ij--t{)`@#jSX21h@xc`6RfC2S%_VFqorGhPr*TcEE}H=i5m~JI(Pc(J8@x1%!V4 zop(UI_AY%R7(*{*T+Tg8tQKyJ;s*&@%<5??Iy86~OywJzQyG*Z6hE`;9n84f9#4?k zwg`6S9e1K9PqN~Rj)?;%jr(XceJ{bEbtIP@!VZBlaWLf&bNNnVO3Adi#R=@2BA~H2 z=-BFMNAOLv#H>6eL`qay<@rRg8ytu?-Y4?B06&-R4KEe$#a}A#2+^1@=Bl@pL{#jCn`!-F=kc zL|CnV;tcJiSc?c07jIF9*)Snw-M`T$LgPNda)RCEOPv~8d&E~7zL*lL%D5onU6g6v zx{%fG*%Uoo$has=BP2IR2+1tnX6_A)Q>cW?V2uu;4`ZF&J8%~P@6kek`WjbYyf#>m z!k|aNe4$t@sLSjf6Q`Tn?xK}o?oVMuQ_DN!mLi}EQr(R38mK1kZub~Qv&=b@vg%9| zT0r&(^=Ut0{-BH(&J{N>+@|1T&Bc}QL~bRNgCh`Uc?f=D1KQ-LSM!PL%*Pd9NOW%<9;Y(l!Nf5|HB6~Z=O^pd|X@fSfkJgO1{%wFK`ToDP*Sa zRcnrb%amO`ELW#3-6uav)&l+4One0eOI=*#s@S(>RbR{F?@Ot&h6QDau6aj*Wp+twF2> zDe}?!T3e0Hmre|HfPuJ{XN&h{)AAJBJv1N%cM2X>m@Iq5?Qz1^rbbjYh1Fg`^C`mj zqf3-$)*5Rx>rs5c(57(ferxBcl&KXz^`F{kU1?2?=q#V|OgyOO0T*F?!(DH;PVdzA zo#w((^2WoPw_C@uY83m7GW=fdrCB{T;YT(=%eI^2J1O8V#K&JqCi$qPkF27HfDw9y zu*V41ukm|D$6!_oE<(0t(uQOq8f1gqOOEmk!YgaJgD?yZyH#Q`i$Fr`VI)oL?pqol zFOABkEAZz8# z@3)>03ZB2;_x_;y2;_DfBt#uUhso4l3@Vdiu}KiL{oR93O$mu2M1&T}DJoYtE{B_$ zT0h9LCCd!j@Ry%^VKBTIlckU2S1r7hE!?DT$CTb01<}vi7U^K;4!-n zwf*Xao!O1uxsX*+iVWtCb_!oknQ}T|#ZJanmMXvWcYZm>JTv)s-(8(JzqR_Cf9vOB zoE?uI++r&K_-B4_^<$s=(bfO_cYi4+_EC)L&gzAiUm8DtZbukbg3c_MP0L5sVQFVV z&7Am33N=EEpp7rT{7Q4PfUYt7BOqbQTBIppoN9CZ%DKfwAwq7~jcdh|7&gq(m2;OP z61%JC&t6=;_U_kW^ootti6|6L%<8}P`s=H$m~#s~-)>Z8%==q!2~R8-2m=J@FXFW0{d3Dhu~r2Pt=xb3b&$LErx4ac5p1{@of+7 z(yuKv*ZdpWtF8UVG<~l$J8c!Rp1TX}{`R+E875bI8ji}JKXbiSN>s?Vzqr10{0L0U z74^=p*!p?j!}wK(Vm|oHTJoLJFkQ#}ZU~?DL;ht4Y! zccL}t1bIy0XGMVF9hcHsqbsjDD=oo2>q~gc)e>Wm8Qo3DQ4icWQE*nuki``#Mm>g* zHSX;CwuU?@RZQ$Vfw>Q0dgM~kp0}wrPbr;kR42Th1T7P2A@kAbUB*x zFlEDuwrvHP73BVM$`;HbrN?eGeJ}cTfiedd5g<=B*D>^x-{KjG&2uJt$;#nmf@8et z^kp&~|1l=YrL5)x%mRnoEv0~RC>eaCQIa)?QUhJTmkZ9_K7`D@c3CH&^sypkiH{(N zHp>>4J}jc1#OM~&C@UKT1@leuKmfv5jZ03WH)~yWUP>u)rm+LNJ1J^xG8oqp;wypB zzgS{%U)~u76<1aaw>seu#lLuMPVr(HV_XT#>UfyaLV(OuG5_59xHLU(J*~Qd-&pBX z2BkyInV2!G$anXn7vaNxCs}FxZtvW>v)dY)LMprzxcg+vL)Jw$TIR_2Gv})#;iYjq z89#Zi7(!+!*gDbjUNG0bS>w)!Ya0pn2?-{V^#rrvE&9}twC*T}Z|JLRb@6K+! z-DztD-ocoT+immdbi2-AbL+M;+iXZl2#3sPYoKQ*H)~IViC8)zZ=Y4`^X*wbLa$jb zSx8wcxHhtit$=cMx?LL~PvU(kZ~$psB71ZYK67!@CNbVN<)eG4-BH@-(B`(2H0f@twguVI`j#p1yk@FYnh0dQ-OMG;6TxU=2G-n{6LU}2!5n# zGC{5dYkt&tj%7}B>~XL;x1Hce!NZ;Iur;?&ooZc=kDzD_-UL!>tobs5k@?n4f;;#P zf^DDX>EUiPvAkR203|$|vO{xctiYrWRW|E2c&dYx0Rx+GaGHrW|9p zyxrg0=_6^YHDh?u=GlO}!gO&)?Kq#j%X{uu*7S3cy^1S6YnD9vy6`IQ)eZ`g@)}PK zQ7agt8?zA_48VF~?JYIV=GM^n^3}4QY5RmIcuOOQ$ELg9cz1011Igmc?FcGf3hizs)iEgxb*TUvmLuQ^e zFwtYm;MSvYbxje}b5?`gZLOC($xjqLVqlWLS#CD#vK&r1Y7MP0V~yWn>3I-s(SGol z(>ZG!h0VFvd55ZO;~m?mc+fg_J4>I3=w2?o<6h8wKk8$X$vopxFf)BBIEiXAJ-u z?D9zloYoXETC}Rm1U7+`PuU|er?#`bo;;X2yB4EZ2%%;%*1npl<`FciiB|Q)k~*|k zS`!l2-$_~V>~ndf>EdxWLF>Dv@BZ`yZ-V^?+HEoTVERsN`e2^#_WQ%$|FHYtUeFJ z@7CD_n3!5ex(Y&yz+WhS0K$n0DE9~z%osN!aNHT*YPSvXzmV{NXcCqtn4FHA7bz=t zvX0SXKHz8CLA6j|vhVJ;L*33O<;-R$Y@91zP^7nfS(0QSvmbn&B4H6TPo&2YJ}1ds z&P4&UaG{;ntIyvb3!J+td`=~V?9>lRic>K_4+@fcHDTbb6ghYI?yTOv^Y-fTSKAGY z!Tq_v_4BLW`qJ;L{*`~}Z>;{~|MEXuedc4YuKtz(%fGbx@BiojY4zHh-&}27YR5Gu z`_)%JxjOgjzrOnIuYP%T6r=yeo8MYpuC3Q{N&Dd+_{{3W#bN>1)rg-pcjrAO6;<&k zJk!Z8F~j%8^r;UI-X#cRQ8Ua<%%J{l>?VAjZw#X@Ei~uawYV2EWjEX6cj{3j7n(L4 z4NQSpTOis6xLX{9D+w&Gy!g_%eT)S_G>X|iMpsa}IoT#0xpuKQ9lcID#m(bF<)W6` zF_o87&>iLWcR!}zblAwJ?#^C=4w^S@e8OFZ6ZY zyxA$P9p;p{0+OkO39|m6GYa+Yx{P&D=E;2gXI5j3EmYfaODLM~webRnP1kK>;c(FOkf=>)GOzq-A6Z2g;1ZF{yH_X9s zea1b3+t^ja2tES6Up$|pJX&(8AoLfmbPRa(??x6CcN3IfdG(W7eV8XPd4b_#3REsj za};{PBX_eJArxOvphZ(zhfo&5lkMoh@DkAq3Mp0`cF!FVen=q^-8cV6sEY~ZQn8sp z2=~S4p;$7Gm|PY)1i+I?p)JO5iv~zH`d@MPP zk+Scm#2U-35M?L#tTN45D|hWq=F~x!o)X^d+Lg;QHm6TVM^oUGwZ0W^>E6w8KRWDW zHfzVF=qP#x9&g{gk&^1pC=Ir1i*<1`WzU0smQ1TGE*$T^%B87OA){?4V%9cOnjJKT zrt`t6HeV|9PO!PvF7BQCPCMgzRAA#2W>>EiBs&EgtIsXMe9Aq~4)?RDte;24jc{tA z7%p)7U@zAxN)K=i?@y%!cw9_}M8kxe6ef+$iQs*|ev9|CRV+0YS<7Z;n-`Pj4oK9Nep~tr`8hQ#_vudLF(@T(%q@;LrdYK%3PWij^q! z;a7dkvX(_}7?{#AMPC5Zw?Noh(+P0l_w9HDc)1^rhK9p42t?}yF?j3{gA3v8z3|Lw zT>=)vpL@Ce?aw;XTGoA*Ulb0kizsw#PF2;cm)meXToWuCK`|dfK#lD;?j@K71I_QN zZ?vqr7M<}g$})Gn`$Wtf?$6OC@AM|rTTFFcX9^s&y)vDYCc z`dl&1iFmKz!_AyR`b6b&dD)DX84$FVVrJ+++Nzzsh*mq?qpXd`0td91;=u`X=Ex{; z!!?S!Ia#xJDV-L@f%OhJSNl5 z|MCIufL9wevJt4sMXCqik~oM@KSJdH0wxx z(Q<397-w7cgUc4T)BQFVc2hdJEml0V31yLZ|={fMPz z=SMR^x|!*4h~IkGaJ1nOjAcS8S{ouFH6YTq{QeM7ulw{N zkH6>bnRYtxk3BURo=Fx6-Tx@2S&j|%`_i{Jc3CXmm+9Mn4BhMQ(64WM`iHw=@tX+r zEyOg}+iA2nQVg7INAKQ)+wBZ7TTHM?Lwq`hP~b*8b@$VFOP5wDTw0)w_5}NkKv_sE zc;d7?Na@4hdg4f=3KxGzTnI+12ZsfC4jUayekUz87px1XuZ-!a;~94fKE@*PZa$~m zXH$eW4{m10{^Cn7#gt%#ElQOcF$5#lM-&9Rb}(a%ujj6?6Qh6Y&37kI=0S7jQGy#u z>-x1yjYq4?F-UBG%|Tp`1!@+dEN5<4|4~d10%$=3(-Se*c1azj{$_Qg zh8cnmek_H)@#Z_Geu7s!@WFMrF}atc)Iozu5lM_Q0#*w3Wsv6Hb`te-iqvsN(+c8v3_6St_K`_kKIi--sbZiwIbhM0Pml1hp7+%sqy5FMQffDA3-WXeuU>OC5&C2`6GLwOKJV zkSk5Vn|>o{Gyh#kzABHjFhae>uUj(>0Sc*rYH`>wpPgaUjLqU^?d!z{pDrNhrf&RM2nU4bOXT+9sIu2d!1b9-U3h7 z1O#p?>4})1+0E6YU_H3deQh+i2&Ukmq@^Y6_AF(4*C&J-=9chwW+`Om$&&=*N6nGL z6r?kd4WO~vi?Q1*28Ol%e6Fl#5+ZM>L>BN>5a2V$Gu-2z6J!&7Q|9FNfB#^y%6Q!I zz1%B+ayMo4l-)JqYNt8M#CvwR!~N4|5_Ua{t`a&h!P}jFcocKGRs0wmN82emW*4x& zEGKNGbO}xr8I(PaaaY;6Dn%c;1c}|n?S;k0yICUb#yql!IhmrCI~4^#OAV)iY(|5` z1!2*^mCK1(=sBgI{^2`7m|NEf%h7#_@{*F-?j^x`wHwWEZc}V|52($ji)|uA#A@Oq zkU5qd11)BaatW7A=eY>8xKORQZN}L;z(k%~^G8R&002M$NklF6OkpuG)dbTfruc^22dXmFjkNq6Pih2Rv=*xWtwAXlpTxf$G8{W!gG zKV`?=6j%3KBOlh5`;FV3+Fg!OQlS?Y8V|z2Vqp_HrAW~FJH@VH4F%lRk&`5acvQ-? zCnt-A(>2;F?hY#{?ml=%nv^yiQT{ zICCQF%tGty7iw&An?`CFdeB$=)^c6ht?Lb8N3w^x-N*On1H32H;{zxev|;+*xQ!K0 z6>*c_yzY7yc;D#w?SIu z9~Je}zb>^)y|XD%9-He=%W8BRh*4|IdfRmw=lwHHZ;szgfrn&?;2=+h5G4`g$J#*hllrv-fc%uDWT3ZA1F@8g0#NDjVST-ePsMv zun|qoTRV%qKCT@S&|DmR9RLWaa3(9(3n}BXgbm)iDZ$@tqv4~LKC+6Q#e!x*v_Vfq zqCs0T?sgUz0&xsdbAPZdv>SpF5)V;p)_OL7yx;SXiT2e5z+jmmQ!b<`-vnhou5ZxT zgowCWY$U|WJ7gdW6($0Lo1_xrv$RcJbF2DiW-LGc=Gy4$R3l z;iJXYv9T0K%;24Da9HdCT50hdPN#k57rWg~xML3O5MI6Z+{E0mBXqZg`f*I6Q)cYO zhx8$Tj3YmRW<*aDy3VYwKmXjs)OqKPZ>`Q3H0_0A1N_(j)qlDAUw+}|hgrQ|3=6>) zUw!3cSufq1qi#R>>Zel7++6+kS6>@fCj!cgS=PMq=G&{^{_^jwUj5j|R=Ypv-+D~_?5+K=59x+#}tay z@b#~Lef2wE`t8+A#p}yM+mnFjmWVFdk+ll&y_p{#-fc9tPPmA$bO8?7Ag|JJV7 z5`7!eq5oa*Utii<%Z|%7PHXzIu2;F*sDIXn*?lXYS4L2%YzhWt;R)qgD}2VHW(w$q z%Fxs4_Hlh4eZL8aKbTt0p2AZDjn~uh?*>H#T#)7fgXYirw|moGK-{U+4fO1?7)5$TM`o zQNiPiRGxzbY{ESE9IiCWiCS4;H8#f1J+4z=Ovn)Ylmf^agRW8LXg_{vQSz+W%-V0) zwSFkuNlxYlDkPq!^pI7Pa0#Y+PdA1!HD`@US(3Vom!gz6O>XDGTc#Tm^sM)?OBt z7c1*(IB>dPyJ#t#7-haOZVbWRSi%v!<4l*{zt`H~^u=)-Yut>*UI9!gr5+zdTcRtJ zLZ|LTa}&@mpTANt@RS5u0dX&4dGqj5n=5tgR2wha!xE0rXl*`|_0z`b`V!2le%cJ? z)~`K2NX^|jnK5{YC1b4WZa9)fXD~9pPAGcRx|U=!dw@pv{)>;$KsELXQyH*RzaWH`fZ1#cpT4?cR?R9$G}(iXe+#5Qx^ zF1yolPOWYqZ+49`1@Q|OLmyp7gDYy*AOflTz1=8xFETYGr~w#3dh+T4@^nmxYS*c( zyI5_SpT@v5`PIFY&|J5c29tSS>D-^^#6G-$aZ5o*8N{V}e?RN&EO;M2N;%p*bHe7< zg=o>)=v9>yG70sk;Rd?zQwf$k3dNzPRXwhj;kY?0o}PSZ-D{KHwf@%=w4es}zcT9V zqOj`L<2s)*(D+Z8;VMMbM>Jd?xyi9=Sj#Z_40<(g4g^6n!~hc{9IZn~7q>9=flo@J z+GIOGy_DcS#&U#q<@K>C&RSDHDtzA6mR?zShZ&@L)c3Nolx;?Cd4Cz}p4Zn}S39(^ zIagn1O>N$t>RBTiJ{nJPohW)zaws$W1e~!N3jg$J%{!d#{;W^s8~7dfUt_Fg>eD>W zz-*nW+T9AT?C8X_qjqVVjStpr;Cs3WrtdT6!PPe~fGHPI_3hmSGd zaDf~=_&og`2u@q(nSMtQ$~|q>B&}Sxca#@iE`gqZ^6JaN`}kFY6ZHvbmy*iawfz2r z!~*ZGdjWX`Tjjt5?cd5;hd02b3sK^rNd^`_gaC;>h$AMW`Fa__?sUDzJ9D#TK(i zDfuX6C}Hfzy1r=VI9NY=fR-h=wLnpbV5JH{W@uo#>XS@}f^H z!6>va)q>1Z01DuI)5!wmL2Crc)9;s`e|~lUYp;!BkYxdUqQIG*))q)Y7~kjjCA4|y zn)#$gH^2y;^}WE$JL@TjNtm?$I)0!nKIVZu8-1ocnHO?gA=Uvu*X1uoO*QL0Zj}#y zVw>k@&0}?QhaeRS4g{Af)`%1h#)jg7AHSfyk5ep5ga$FP-nx-h z9U4@dmmtaEa261!TBNl@I|+^g>=Hg7^;?;&YR+V>K#+gfe4sSgZr)NXu*ww}>PB;)7&FO+KHn;R; z2E2DJN6%R2J&MP{;Ii=1p|kDwpHHEG;xNM67;LAASN;T}4sH{hr&w!Zoxt0Nolc^^ zqnN6X@=63Ti5dO1+;t?!kBi>|=fU!P!uJS1JsZVaFqnMNqdN)d6R@)}r7RwE{N8g- z>0^A9qdk%~Q1UnljIx3POM)xbq1{~Az|_Zjpgwcl2}{OfGbIyuD)XN*0!`r}cPWLu zfY^dBPanJ9j2=?FjKC302+!P`N8wffMj;(+N2qO`vo=zM?1f+An2m){xUGKmaf_qy zxn00)V?|-24~u(VIA2}XS#*%Xfmp4Lj!oAe0<9fvR=Ni%jEu2(D1-q5`hI;sm5U&q z3Cch@{dq5JRBFG=GWB@LP%{eF^$ZTp+MS{J2DGjv0K3V z($y^Jau<8^hSR%JNHu?0_c|oONK>Tn96^;?y5KjWaNVFVY3|id!qu&mcKW0YJoPs^ zaG@cXu|PYYYl8^v-)yr74W9tPDNY{0eKTQLP0jCKc=`FugJZ^gFVoYZ!$Uu9WT8C$ zz+3fYIWkUBhd!P~r^64t(4@+v>^$3Ad;Zedp%v)RgRIKLu%Xo2%z8lJ?@=UnkVN5f zQsg9{*9o7K6VoTSfOm?+*%p9wwU{fPmE{1|#H#f?26u#Gq>P|UV|E6E{agL6z-vsqc5W}6m`t3M*vJc!9 z-)ghI)YEYzAL|-(p|ZdUd?_ZEF$}-!pHq|$`_N7)d#pqH3AgYMJ6T_I38V;q(tH4O zik3y*@P<-*U4jK=$YBaUaX0y$b@MJqB z^ma6EvkhTmkEhmdblM45ZCl6ZbIp!#)xYp*6pKxOGrcFa&vqNKKBtm>LhV`a(W|4} zTEQIW*Hla?+nU%v0iT_8_~e8@^MNKB_vlf4c&!^;>ZY+lGfqTTkHY1{==omwL~*|# z?z5=h&ZRe7`0_PRLSeL#f(MPZ8T)P)onL$7n9C5i zL(VM#qs(-B2y^dSyu^&yjmTkiu1{=;n3(e4eD|H%i4&LL#=-s7pZY8RVoIN#)t7$l zORN9u|NU>YJNIDqr$7H^R{!R|_OGw5z4YQNAQttVta+Robt`4bsrvBQANuU-L2)5| z_3!=i>YX&(PEZjr^>RmjpG*7x;!7{Ae&W-gS-tl9*H+hVUt9f&AN%n+spN&{UyN#I zqMfqh@BM>cS^dWEd~x+NKk<{RSF`Z>%GbWWdOdfq&wciXSHJpS{Aa6gbfVYh3rD$l zeP#8T&;H=*W4VjH-VVEfl9U5CvWU8$D}tCuThW6vDS(*2&%zemPsHrM@~Kx>U--FS zSbgCOUuZ$;X!ryeu|vL@QtV#B*Bx*VPOrXjJwenCbUSkiWGr%acRTh~e4&J#KsO5! zdf^#MElj|>?Obj?f3jE|*ISTwR@XXe{aXpBf+j!rT)VR|*-y$Ru;)Sb5jtOd<>S@= z#*7~Y4s!80D<+nUm-0Wh=mq#1tq%MVOTJLD5I_)mcI&)m7EZ-~jdW9a>*L9%PnZ{`;7p#;i-?M$0Otn^@ z2x3pynQ2dDROG@$S<8AZPtV+|APt}Xjxwb()@c+l7}NJ|tsngXzqQEPr@FqUJmb3b zOJVTHZ)s8LEbw$|#wIxEhvr)Fl(X)qJits~JBYzvVuBC?v%Co3DJ3O%J8mZl<^lHlpX}M zN0hz6m6B6E6r{I1!h1Vq>=g>`%5@6RX3XVFDQ-NcY++f)^@(}7BdfP!bXBVFeskYA zO+l1$=w^x^$KkU4VNt{+U3@it*{&@V2oxU}M+$_C(R^v>PvzXnLWp_4V7lj`34-?v zJd6&ouAq=%5#^pFh&7icW45LV?#oY+Bf#{ctihx&aW*Jf%#9I{T7$gn+Vg8EMV3># z7Apg50GOX0cQHXnQvmd_%Ly;h9tto=pU+XcF~-WD`G*!aerT__2I@h>)U{RYhD!qU zH-QD%b-I;gL*is*3BbJ9<_KJ)5VxMPig^$%buFkerL^%@uDNtMg%{T)v2x(rq0N=V zGJ*jClr;t=gFaE32qe8*jGMc~TWA)BeeKS7i8~6KJyx}w8&A9y3?uYRcgdM^)s{^OZO~BF)1K3QF_~Ny8 z^cySivBrpLl$1Y~$dtVL1Q!m1CxL9N`f7{$z>>{86i4u0R={xOENif6?uFDQ$!GbVlgP3%!52@An(B4mQT6K$J^fXI=bnw+ z*Nq9+p^1vC>LuH8AG@cW4)HlzU2p*p!X@&gI5FhLc(F?KQokCQR3EXSC`<&jfTPS)%C!Sb_!%(-x#-_A233!fm-6XREF`>ze?5w*}3 zYxg#zDNxZ2o1o?{I(9xQA#veOS?f~BoS^jVUn8!MhuTvOvIn&DaTvz7~=qunQ> zLr-#p97L5roR5oW4s%(g7>@=}$hA3(CpK@m^>MYPOddnHOowbHTf?{_PaL)CAsal( z+V^G$*W7F)2F?GdrH`zl2V;TJ9pVj-8p^&JfUp9xg$RUY1PBvl?rbF7LO5v#q`Z*W z6hw+AnFW0yYY!$_EXNuk#Fk8OL2R9twd`~#0U}qf1+v*gBWyC4W!f??&bv(~NUH6K zXaDSAFz?JkW}WSOeh(2d!#+akDS`S3wdoJs6k~jhBig8gBD7}QZO{f-Tt@#{ePD*aAEcNpZ=2xIJ>J~`L$nN zef}qZDh1FF#RPKAdVY17uwwE5^k;r>7N|;_7ezgI|uoK3@Ha zPyg`h@BaF4tp2CJ_lv8){1^V>>T^H%L#wym{MNXxef5oRtbXdJe{%KLe)i9=e&rwj z*6N*3y-fH8JGK*oxGy{A9p@Xo?cQ9Cv9(z36z{?=|GUKpdXzBu z;B-M*W47xifZAynS9x3(PR7tW+Sekr zp8#v|WOZ<^al6_sx8vRkmP~e)YggBTNob|?*(s0R^>@V~3f3dM_00HkCqYQ(w1Rd~ zFsN|FJiz$A&^W%C>Af|WaE-Qd17rS72$0Y;lvN%ibarL>woB^YLS50Wg}|=oSk=!v zJ&1N9!waK>iF#i$MbXfh;~L#d?`2TuIt}Bq{_%lt{T_C8-fgXj2E%u_hpBirJyZPL zUfxwv^RRcMUjIPCFm)3rHSky-o7?gZ6XQvF^nd+(U5K{N>JUhsjA27!l70sqaFhqB zTGqkk9$bj+R^_C54(3nk+VO+s@wfkd50n-B%;T=7zHYg#9~2DJ&N{R(=1YEUli<@j z%~*n0|7NZ1{TcV;EX#G65j@}&W(xj}yHdg|CBeA9^tQ2L)=jDCbzK`*la!R*9EFqi zVX(x)I(5L(kkwkW3EsY)4Fj6epgv;kZ#VD7(K#E9LZ>Jyu$cO0Y!-`> zg|54IIm^G{OL~_w=w6nPc&Ld%RDQvZts&Y*NiwnI`mrdB@jZR&v$9T#a9v{QauD6V z8yxM9f+x6op8`+b;nNyZ?orC}9G0;Vl?UTbDKUzXzPA(dH%}G2EXxd{TWs0$x>gY=Uz?%Zr z+H@*ugjuqL&nN_IP-C9OQ+Ty@Gh>N67KoLFCOWx>OW9#cqA5L?!)*$UJMjgS zV0W`_btphHG`eLvvTRX&W1FIqi!oe&;p*kpt1mr2heA-Si8FJ)G1{-4=i&t>E>vwt z%Cvz}_G_KzG;Xv0QX<0P@(OqhH;>W~n0tH8BmEnhruxkd z)?lY|H=Xq=t<+eugqt;W=84&s_%Z_V%-`S?noRkWsw{&E*5ouW+^e4b6j+M|O^vLe zp%b-dlu!e4eYdvYJHzW2fBUYkSs%mO4Vx(a)_H5C@tsYe;7t}gnI>q~ z;)+N2vES>Kh3t@>lvlkS|Ot@JcY84 zU$5-0=lAlhP-UV&;0-R;geg%Qr{vllvIK@`z;XTcwUR5lTI;`dC<-u|_V}GCVcIc;4gHxmQCb>nbnCcSd7q}TA2)YyvDnl1HJ@Z$2gQ4r z#RG-l8LnIfWxm*HlxI`ok20{1QsN((i@{{Gyr;;^fDj&9&nd%ec(Bj9vo-S4rE48h zQ;34vfFAEvw-6KN6UzL!<$;iKqH zb)4_Lt=3y@<{fmRc~1dk^ThQ*a}({>*PuTApUsRckRHURvxF7X(z^0dOCMQ9Pc&>9 zmqE}4aZMhP>1pc**&FbQkE-oL@o~d(?@##FMO!K4nXYwlp!7uOlURKT!q$%;eKPK1*(+~=X z`CXDclXSH-lX$tDzfYe(bMFs&`R!$X_cr}O%l{{t|5@Q=U5|+R!>D%ye4g#!8eW(q z2`+pvJ)3|1Z2jK)_jmiYNR3d@VhKCfil$R&Qq&^XLB9kF9>>2Y+bwYhV7I)%jxe{BUktSF?`UO=B+Z&2JQ( z`LF$@e{uDH`23$<{g1!&iFlCVZ;k&0;@%`RiX#(UoOD@Iuso=e4h{{`hA;-tNThy`5?2?W~*#ek`{v zVia{;w=A$%5=sbe`+LQ#z`WWihK4PC55f~dzhK$oc1+OCn8IPUT3}{8dM>^XJcY#= z7|#i5+E3wJMcuc{9n8zeIzix3X7$MAmB!~rbsRJvc2zeLm@$yzJq>V_+h<{JB0d&A zU;dQPKBQ~hfshLgnMW<|G2Gt6D4~Um(j~gvyqH22P?E>4PZV|j&CMwzR;OoUdf)eP z!OOc#_0`>TZK~d3rpm>Vz!_B-@rNeVXW;; z&MsQ72*kV4fQ6x27^Y{-^4uD>EVt;7{FFP`6g?bT<6iL5ejV}`9Oc{jk65G4gzUtt^artZhsGy>iMN4d?n-niZDrD$oFey81Y@og~pl-*pd9(Ah5iRz~KJc_BKC~yMa;}h?$KJl3! zT3st<*?!|osdTe=UPqanv${k}DWlG8om{=}!t*o#M`=;}xolA$VK_(e(XdmRP$C%T zwKWuF3l}bm9`txl>w6>?%HonmI0LVlD-AGrEAe%V7b}Q+xzlI^;gnk)d{|qtP^wS} zVN$tmc}CgpTe2}+*y#;n6veYNSps`mEnyTK!C*aVjIS*TuX%^(jwjs~AeQ7_G@ECXUND~?UDWX)rj zcc0=Q_;4X!;`E`>3vETOxagQ;4{A?ZpVg(-SnGpC0f;Uvu2GFEMZdPC@St#^FwF*J zwf&$NY)5y4ZR=|JH-ZOCH1umZUGwBvHXYRtIC?5AgmPFrMe3S@1@vyMx?i6*qn9U> zssy8c=iS>AB%AflR_!`0CX$$U@Nc6{A)*g=V#<_HfBfauPkio=wQ0~PG>r{Qw1+8* z?ziSq%)WE;R_{7tthK8-Zr)Io!WGskaBGxF!FF7|YAeQCfNNt&A&Z8b&w6Q(Vh0R2 z#l)eoXl?|tlo$KKUmxdT z<2tfJFN1ymyN?Fq`_0YP)Rq_HWvyVzL+G_;(E1qj9t~|cSsW5^Ld`!yBKkw=$kL7# z+^Ie%TpUJi-Ob9lsXWuQtH&M@P@0LgBKQhTYnpS_u zWa~}+zz;ORMt-rbQ4F9vXpm&>>#>)H-#n@>%IMALRn}hdOKbN*YXO`ZoP0{o1 zaCmW(D&LH8V^m$>Z9MUXMXuZcaj+4_F6@99fEiw-v!E3MY& zQz0k1FR@&tfI^qiV{4L=bh$vEF7M;4bWX+-z=j9qgXgSyrhTndg4N@LjW;*H!|3u+ zF62k%RdizTx@)il1M?2eL8~t$pE&s8qm~xV^P|?P+5^Xi!3>fFz;Uq(AuOy<8Z72? zm`<8mL<*4=-=eXZw=q*l5vFKKzWWRVZ!)g$A3sJJ+-rg=3$a@mtgb_BXfYJAJaR67 zd@sMX6}j>8PdRHv(FD_iHBVnsz#pZ577##mXRMC#VsLCI+CRnC3^B*NggfB{0f6<5 zQ#V*>3}KB}+Qp{C8DH)grq@%Xh|M#*Z!x$QY6}r1jJOY&RbgW*LjT;^EKXuBYr}5B+rwLTV*FyXVv@e{)vr{4yDZgl{o@_k zo_72JMkeC_{PoMZbiFvl90PVWM(Z=5{?zJb{k)jD=a2p9pIH6PU;X>5U-`msc8b;M z)#rcwCsu#+mwusGL;I^={mtK8{kfm{nbrUC&;5nf+xh$d@TY!Y^~XQ`>C8V*u0HkB zbE|KB{VO97o!Lmi(-_YT@6Sfe_q{pUN{UB>IW?d;%FRpQV+lLgG@2MdE>_F zg_zO{353^Dc-aZH;F$3@d;few&*otZOtj-+jO(SC%2xLk)VpXse+~sSHe=Aa>}kR*a{hjmf8G`T+p`bf6a-G=1S0_9BVz+ zW#@u{6JW%Vut1K@oMpMZHnocaxr`EVAjAjh7fa5-OKL$y65sNY26%c8zn}!w^9n2K5LpYU9#@1 zGndDjW4_{rFdOT%w)ioQ8UqLPL;gYR$+*0*RwS$$(RCRg_PsE-p~i7Lv`@Ka%yhdK z!40gZKEL}1whMgTU$SDrOW&sig}B$3I)Zde8rl&o6E>Ydqb!2fg)9{}qB(bOE#}W? z92%)!LLcS#)pq(n5zTlK!^SG%iQRWRLc$fH1LKcj-NYo;X5$N1+@kaY!!1$5C>Jg{ zbx8o#hbew;Z5HUZxrFf>_mRek;H+PiNNcz7L-B*c1$3ISLEm<>nz)f=ACvR#=blgS za+G$ktc-I796qykaj@%7?G|X5l2I%gJNm{8jTD=Q(!^Nbi2g|6ES}8znnD0QkO+q8 zIr_dRj5-ytcg5to8@*ulqaWt~C|#<9Ym5ymRwa})lqNIAF|y!6Npv!|SNW|$lIP2* zS)JsU@<3b@mLvkfKFO`*eoAt1X8lCT0)EiIpVdc} zH)j$IxMn>}nSuUT*H}_J7MMbv>lVcZB@X)cs61fhU4gKttYC7w*gJ&ujr#B;I9oR# z!;kuZp?>c@x}Q*h2BjnqIUm)wI|&+ha|ybiHP5aiD39qFnnO9ky$T*KI23~){&J@k z1a>n;?!Fi;;qu{8^gLREcCz*fIaaqi`n+Za?_iQ1NW7K9+&Rv-&e=HGW{JDYGNN_k zWGB_MF_C=S93jv)5BkA{XRN4V5I6|jqwFfLO&?^Rf^!0gHovZ9 zb!(lto68~P)9KbIZfB3$Nm{tqV0k`wi<3Jk6I)Mi29L(udQN?Hm|L0{R^}{=3YN4d z5+ZH1Tq@4Z5BzWJ-T9McTXxv>o0VCaHIHw8-Reo*ZCN(5HCUm+UcO%^ zfwik{-gD2{XAf(yy{5hP+MoL5>XRMNaJ9u0#}qS)Q1;$$@y2_2FKWO8&cOy3I<*FF z9$RFoo#x7ZJdu$Fta-}{WWIPN&6F!oI2sO|Jy)CB-X*~EV_T}=QDgG7aYoPKA0;21 z9@>LWpi7uU?ZUgkmbV=GV*$msQu@Zo2yI1Yba@=FkOqfQweSKx^I86a#zMalyGm|s z(g)}!^p&0o8jrUqzXyGmx6uG|%N*zRg*WeqQw)C;*wbw%*$H16GRM$UU+`l*`+UkT ze4Cgm6~`9CGmO4c_YsczP07dKcwgWj_*rd~KO6}N(>^@L7>iA`97B#imap=76IJQ- z%r|Jj6}@Hze=r8Bg&vvDKIjJeYYZ!>7vhE0NeLGx5;*8f3lE9+YIU&KzySL1uoL+FQTpx> zScvYD?|GO#fBoG{`4`K9&{9Oi%-JuP*wcj9TX}q7#%C|=q|9HNBVZBtYgh8}h%iw) z;57GVLdfNe5QwQn;ZUBHA+FX8Jp?DKZHVD(J65BulpUr-bUH?-eVrueor#EEPKo;C zpZVPC@Bhy~*Xr-()xYtV{^IJt`p^H%+{cS4k#Xc{^9$odlyOVb=Tt%DP8C<+xVj!C zES}uY1BGx_efM(#w=#S!L)2SuzqPt_tr#;2m{wG+ioaWIoj-Z~`WPq#rIgH-S6_c) z^=rTUPgf6q{NI?sfoCq9S>4U!<;!3D+Un2!#7`82`A@9=uYdll!%+U--~R`zzxJQ} z^#ZP)UfnK;<6&M$*Iz1FX)dB)`rThy-F*L@)uZChy!nHlU!A^~VI<{2e1p$^`qQgB zUwv!!UPrnU2nBijot;djc-`7wswiP&4swDsyoFJQl ztW~me2{bz=F}4E6U3vZDu%X*2tk)BG-^##)4p{xZ81Q-WV2E!PIGaKF_U-MAJqhjT zPlE345%LJ>DJzYc*aS!(;g6w3lz+k?Plj3TsLW)Fp^}uVjR62|QHUI)E*{VRsm8j| zLx}`2B{beE&^F`CUP7f4$*j^^EyqUpIwfIY{*7z08k{2_6Dmg#&?tBNirURJVbQ!H zND(ai89kgw%bzlXx3r z#_`$f#!TI7I1lW4Pg^!=`qq!7)k{A;!zCq(664O9j2V;&II^T%q*HQEh10fx3{Wdp zd7EVQSxs+$@O~{j$;+v}I* zkwz5`3kIA*U;T^=csP8t4Wdm%t5=$@rx-nZb|-@^`o;)+K10nCI23Lco9m@()t3P& z1(;F$ULGh;60r^I@MxLm;Gu84Xbz*mi(M<8r?>3zlnZ5bFhzUJ4ZJc#61 zH^UEQSS&Bb9=IuwG-DnT?bQcDzQo{kKN$Bg!%Xn+o6H4w%&X~7zekDAP^YX5(W!+l zQO=uJ-l<&1feUAf6O#uN?;2^gYGv@D+>OU;-y6JSd^oecY^?zcZFgk1akVXOuQr-3 z=Te+cG7jA@5c8wvyR9^~!`--fdv!m<21QU1)$LJMThXn(?GqX0LEIqt6N)6{ABT$t zAk7nomyoy)DB^aj#9(7PQ@P52rV}Dl0*=EqG%rPHYbQfc3k+8;U5y8w&+{d^o{=fD z$&uop^Qk<4ttHbN!Y<(a@P?*er)X#Qbr`O;ew(%Ub$btmSsYdbgS1-RZ zTiT8uKU`f6cdxc+burwxV8L6I7tNL8|GeCy&iU}?#*LdZPZ-&6Cc{{j$5SWnQebgP z5gy_6H%3xQo_WZ_ND?!HXX;^dY$##)X(4C4X~GYNo&|p>{&Gmygz|o_iQ%1(azWT#UL3jXzt$2S%oe3UfN4qFeftN-0?uFUY(OEp} zIRhw}5Tn6OX^{_5`F_Lz#{#SS%jrIj7|3;4Jdf@22u1_EYI0h5+WJ`>d+Jwd` zVR@*T3*2A)V@$%_VI27YP3a@H@-?RnHKGzf0$FVhMfnEv-nUpD-AUh3YIQ573H z!ysh*^q~Sq?m!TR6F0{KOQqJgVC0{?v%L@AuFGGK*yPi$NgBLv$Yx<}p`UK++XA0; z?)m!YU2s~OI>nWDfpqug)qa6-<3BM%dt3YT{X~qgaBZqyn%L8&?J9Xz#{gn!>};KD zTx@q-3_J`w&70`dEPy0C&z98W+#~$JcGlovp)5igPGxv+zMiVy<9PRzCqT7It}lnK{2Upt$WY4#iV&M{YAe*Qu0Hj-&o4YCp@sfbBS)x-tFN|G+*aH7 zR$njv%#~K4UKVer(>t7$g}7eJaP&&XtiAJj2^G_T8$37Z8+nynz4}s(u8(<)oNm?o zhw|e2dw=&AR$ux0*H`y<4?Dj7<<(ce^0kS{K#_Xmjn`He-}>_E&;8k-S)Gs2-^=sn zul!s8_UadZ@gJ^!pp!eee;#*2$>WSPzw`?~zqqd;^SC#znmA8m3YDqkDM!w)e}Y* zv?S(bTf?PdBQO}8ug|vG5w2*RMbZk!&DFkHS7 zRJ%{KeD)mNh97;GM*SX-3EWdhb!tl-Z0Mq+cmdD2Zj$SdkJ0AY0_c9}monF5qii=R z>LJvu(0R_-a2So7W3?0L%rQr=dS5@m%c{MEo;^%qeD$SbZ8bMu&HI4i)HX-;Q1~Q9 z0DYL1>~L6&3LbQLJ#PXl?1J4&Jau>q-Hsl3ZmZB{OB2Jjqu0;m34uf~uJTZMkOF*! zE?0&ctsu_)$?$G#44i&&aBp=tIx@->*K>FlE{yS|t+9mB^S#e#bT^uKE}DF%6HD-c zt%PF6MDukN(KQ^M(nUxGL;c;1JwpyyQF003aK|-R8drihPn-L-8~&fo@NgUqpdEU} zGl*B=l`DA!RlhB}45MP{=p#6ujn17!3&jCph}O4-HU-1@6XTPz!H}`X^~5&{W?~*` zFV7Njkp&hJ5FfnK4TY{jNw`T>L<^hv$fAy?X^e(CJfq) zD7F!ZZG$c{h~qWyyvNHlx>j3W=0(yeiI3OY3s)$o^*hbIpBJ9gMu?p@BH9?i*4v)U5ZCvTT?@yxwW zcB#Ru?en&K*-CY;zM;(m>I=LdpB*}~&{*{;=V>y+!xmU>-nuiMSzt^iZ~`Yo+TG*Z z19P5BcQa(&?{g|XRhP%x^L#jGq2Vw^nHStaJkFs6M_u2$yC3Y^iWtn#wP?fZ=22rd zG`+s^lHhHM7XZzrwr#)4ySU%Cizx&*j1#=DfP!8yfDX@UytC+w6;c6}+~AC1xHiLmZRvllk>R7{*}#6CPk%H( z{%NM4qZcpzT-sKjdTty5%@|38t1^^4V!WI0YIV2}SZo)VJ8fhcx9-hch!@W(RP$!# ztiN5s>~$qm*36&sGx9j}WKP|RcJn6X#b>*yI-3Nw&q8s1aUI`0ZoX|tySAI#38C@K z_o-%?4&FvB=wE0jfvap*`hvDmy*28SG2)ZT7B;SGn${p9ogc&e?fXubGXJPPru zSr8~+Ll@s;uqN*|pEB^^b8xrHlYKk1=0OXf8Q9O&uk+Poaoq{HtF2DTt*t|^f(2WF zIk%h6b?Mxt)$7+^TRpA37cITGhMouobR8lB(F5ivQQWq06heFhI0UExYV1q?K*eRE z4^Ea18DG?s4a)Y7c&&dD$OsSw!%-wEg}{c$5H2AeH!sHwE_p+?+BC#VwadJ!Y7=^z z)#|=7dtkdmS-30yVW$m|eZJmdB+uXYSL}L((2tG9qF|wwvxdDuWEjV{0_R&4(r?Z3 z9q%j*Icy{Z7K7zsE3vbp6RtTfk?Zi;-Pa>R2^xE?P-g`f*p~o%IYoojSv#%h%(ku+ z9fCZUT!Q|C$}=wnusq4&fI(m$tld^;cb*nEA_C~>Ppj(ZFDE1=C{c>V0r;)o{OwN5 zc)d6}c@JS2xlkS_{O)e&I&f^UcbyVoh8^yJ# z4X?iR@~rwj*)4c!t9MQXGk<2jRgE@0=;+wb-ljqm*B-yojth^wqcC zTK&|Y`ctca^w0k3>Pp*|?zBzq()sPxFaMK&QZVRit51LOjSMFpT^i9oY47`+FI~^8 z=gX_V`qMwN`lrA78>`R%@DHyZ#~{wcc*WZJ?594pdaM3@{jIOJeeBKECp+f-cfR(O zj8Jz*_?!T%S@s=gd-g&KLrM}Mg_qPp1|dv-FD2;Vez6-OCbnsu#6SpCk0uU7Td*=L zz4r1;1wC4(uvA+EY25Y_fMqk#M9vL2qk3 z1~9fqwatp$EYJlZ?+|ba<>;Vt+|&<*lmN9|+>J{asH}?aKW^(tjLd%OSyfGF93{DY zVk?}=aQHC6mzRLSSLehxQ+joc=-0q>IpebkJ#c}bxX?X)Gj{TfkTuuc3xO8l1r6_g zN5J;pWL6#oL;O`VX%ULy48w>RE8~(MKOl^I_ZdC*+Hms)US3znBF-D2%Ed z#iPn8xM=@YaAL?`j7f`7Y0DP)`^;iMZB)(gl{SVb?KS_Fc^Yi+72^_*uqy6)_*iWj z;dEV~HZ20}@_g7^&+tERm4=?d7yrtyEO7o8Huj7FtF%&O4NSqa4}r$_Mvxr9$+u4U z?xZz57p1@p-ci=_ZIEq?*lkdDqZy2W6wEmgp-e|9YZOSh2EKzs^_fwCvcp)0pE5jv zAA<)k4P0NHy)rGC0+P+_#@Hz9!^>C+`-993FTS;P-C4 z-|1UdR_F4PpagLT_na3dLDZNDbnBxXXH(j3K_O%s0CYf$zgNZ+hIg=pUz9V6aLo9E zCwK;jA10{!%~R)~_$$)Oc|9_ka0$Pa!Q)~4V2_7K1|e-#=EXUl{P{U@oH2rdS77Aw z)Gs&)I#tuBSTMB%PD+dbue|*77(|?aXKvmu29UTsjN(>Jd4Ujc^<8_-6XQr(Cj?RE z!C7MTFDDwUgIt1x72{_vcF%mBn zcw{^n_;(&vQR8^7Q?{;Ozfv5vD{U=&FtFHbe*t{Jr}rOGY6K%*t-Y$+e)cd!7X2mo z8jJRd#IrKsG!~Cjc=kGxMzB(GdmbOO!kx@`B^=%DIO45%?&0ISbK7FJ*WwJr4*KrY zEsH%222^fae4ZRHo|*7TU{ScmP{~V6|IvBv=b`$jKJhx?$wL{Um@qyviX1dI$R23x zjaJ5Q-W2fL30MUxjqe5S)yeRKa56>X7E0Ie<@?B@gbjYRUY0=A$vdX-QO+d z-QBjuneU7hV>E0I^G=0NU=F`{!=Q2IskTcqH|kF}q6@rH%T<2yfzISnd{p4!!|3x) z^BCDFR0KtGTY-wJ(m5aTqQwANB4s>Lq{i6o>@{k7!x^m<(lpPMx0ha8A$C z;uIVPABNw(bPChQV&H=ZZ8>ke^?^eCIJo0C50Y6J9mpi3AaCl?FMLg#!+G$ni$hO& zv6*`lUx(gPJx8@WMz!!1y$g@VaNPI-hf~p(?K4Vbi?z>y#5eSQghc9x8;_J!AM7!TxHrx z&a&vU@aP35&-i_?n)>xeJ9>-fw4~irRz<0Yt~>DTK3I7^3u@rKZqU+f@AZO$mASt5 z44+dv`sttcjj?8AB5+l%+9l;ELe3>Ecn){y$>}0}=p^_A<2OT(#RD)=P*@2-mC;x< z$Dd}Lw?%8Nc?kO87gYp4Em_aDsZg-8n=37nm@LQfChfr^dE7ZgP}>-^AH~o1(=DFF z1I$z2_7~|meav58gFbq`We)Xxj%w%eTdD3jJ(Wk#-HuA%|M2$e0=fI_h1KcwDc+%% zS|E539}-`T{QWpuN;f-gUMDkNKHtFsc`v?b>4i1)P)a5?P7`C03ehyk4Lr126z(R^ zJcXDITu3(q*1(%;CdUwyu4jUH7pC@Sj742~Z^?k{%`mE`TWo4eg6Kpz5a;OJbP@s5GQFyqp<|*$idt*-kY>+ z!9y^YHp|B#vN7yz@)>vc5G@2`QiS3eR5yL@{WbaDyt@7l+rCX(KbC3vX?3MGMxxmO z$35|=J@2gA$GP=C?39NK38W!t#+BT(W3e|L+(_WFa!im8%*hos2)z`g<;&BSft&XK-Ao{Qs-+)_y z&Io^m`J7e52;yveO35P{zn!`SY+ zw!X6ZYO!a&cH?FzWPNh=4}Sfhtv=|qjn7^M|{n+QexO(rM4_9v`fd26> z{>j`sKPYJPZpNQHpiU(q*Js-i#PqRc;~?h5ZIi;4sNL#m#~gQ(4@Slyd_F;XJ0a^E z`?p)YuCJ{!?%qv^g_|v>Tz=GQeO@|NtKpeoaz`1Y1}Ed*6w5OStoL%GpVjk(c8Zey z@(oaolhTwDc<$mne=o(r){vycQIz+4k8(>Oyih*~{k$A*VQ>f z0{xX@Dco+A7X!4_1-g?@c2hvaXWCC_GR_z}gSTXwSRp+{aEi!{VBY&{k*zkEKm9Xj z%un+)G$U&;?#+~XHo<_slyHmtwt}AYx+5Iw@QU034(gaYQy2Cv}WzDui zIW)e8P&6sX%WFMn>4N_h6ccmGkDqi8ya=z!upUBp zRet@MadJ=hdr@9>!NpBl#&rD)e5aMmZ`T`MQehuGt1#{Be=|Y(!3cf?t3==aZAHgA z?V_W82~fspIUy$FQiN_NBVZ*rcf_g5qa;D>{{7s%%eNo?KS=3)dYU1lKC7hWqHB-a zNum1;=$O zN8nVRMihMgq>%Ftx_$T7(EQzKw~u}>gfN;g+Bi*YKRR_Ru0p@h?qrV#QlAef8cLm(gJuDMV6l_*q#NfY4V{cT%3&6nC>jxpqzSEr+8 zBil37?Jc>xQ3%5@OM@Fc|8&9>(>de_IeJ-u;++J3UpEg!?B)omF zIvWl+hW)tw+g7s^{*RI$kHbSW`81E7j{l`RQ_`=rNOL|97_pF^#zt(Z<5A(=UB;~N zf??py?v)I1Z7paZC@QvQnUYSr+HM^0wZ-85TlYFGG0&aa$-8vB@jK4bbT8Obh%Pr4 z>`eD|!`0#ddH0~ctqPZO(%fh8If!>S^8HNsUX1EFHS$)+xgQiY-D>;8~vE z;6h_Tao^3@wb#~)tCwrz#TGIw1=Yt#!F~7WRNLSltbYB=zq|VN-}#+674A&%vW@Io zbLzbtH>$U|ftABE3q5`N-S@)Hj5*bHJ|&DtkhoVv&ANC0{=saq!|NEl$VQB;_wT2G zWvmdVOCWDJHw&!2GcVVFJZu!w@DfeuMP{X5e;$Xk!_&fVbnLKrUGuw&L)8*KLe^8R zmIB6NJPi|j-Y2?;?($@S-@KX4mx=q*0DudY-K^s15hjL(O`y?t8q_g(;8k8Vf3AG>!Ve$;}qQ)YR{np?x8y%M4C!@$Smh7oCu=v8*yXRh(3 zx`B3}5&AlFKHNjECw>tlPklsBY?URyj5h>Y9=>h(27Hs0%V;$SIK%=zF&=@Lo-BN( zB8N{lL~{=6*Aok80HVa=d5{-v@^%w7(F{+T@{H%?S80!@{GMyXH|aCV2}b%q9#g%G&QB^kF;G(0$N|Q~eW{|| z*M$Jyrw2=irp~$Tj|}AtsM9Z7u%v-Q^bX$|9$1$3lWD^knY}k(#Vs~Jd6-c-8pNP|<=Tg<>kQp5SVA#g$iy1U0%{UIo0PS5nF1RW z_PzczNJC_rJfn1nOe5e!rgE8MrnCG*77#GMDu3=YBTW)>TQVoY<*A!xwc_UD)IUrJBD~&d#gpZqa)9VEAUMtB zVsqkrb54v1@7`}~*wdZ+t2aOMnU#ZPd7m6ckZVfE2s_ycp@r))h1x-|6Z>MPaxdlP zo$NJjCTs|hCNQ595Uv$F;9QtHV^Ixyt`rzHw-~0>z{_6T? zuC3lFCeJ$=h0bQc_|zvpx%%y2`L)#-KKq%~Kl=HfUw!2pZw2d<)fYei#e|(K{#P%r zZv9%pSA+MPpZbB-!7u)NM0;TvRzaN(^g*uWRzV$IFUAk=9Ri%yCvkJY&#J-2Q3euj z50jo7H}nG?TLK@(L^5QF9Yjzg++wg%hw?2nh|uJoaVOjn?p-@kMi~@h`0%KAtyXRY zdqS!`(b_35LGUC2kO7A=gZuxPy+^a+tX}s8cr_;kx)c2QY67>$-pVM)#dQSL-X$!% z9#53UiZ|KQp7Uyuc1Ut@kTIw>t}xnvLKdZqaEE!iR$q@sI61aYyI-c1)T1T&UC}ew zx}>-_1f?Y-0Zn^+)JcH0QZw_r_mne1pcT^p5$I}{a{NZ$Cv`7?j<6d7_nac?UYJ~k z+RG@k7-|;bb-W=q4X%(4jZxl)Cd@c0dwp+X^d2F%d*;flW}7$Ff$kXZ;alO#v~&7g zL;8=GNc!t)g#GS$&$9(8ljXTTjRHZYr_bQ}+*9>FOX~uZKNYSYDBHbf<@buFOq&~1 z*Zyo^@I3wW*5)?_ph-Kw-5a;?@R)#zZV?WBj3q$~t`N#7yVJ)t9xVYL^Q3EzUCkJB zP~JOvlH6|vPrND$$CKK_xW}MLDd!=_qmhx6p-y=`cLW_aX9Hm6CA>vL9~Rj5FnT$> zJD{0IPZB;4D*u(8P8!V9jI5o6VP0YS%Dd9`C*DT8&38eZ&EXH*_M%_gr;4``Pp~c1eDuCkx@<+_ z!Fj9gI$_AbaW6y9qQIq0<{3g@m+-uYZKJgU$t#T)kgZs}p>_&JEb)9%HpAE==;t{T z4VT>C+l@ax>NKa`mCSMTV|hl1UltO?!qEo`0C*bnQPz6rTy1951T!%d26n;fwc@H# z@~s3b_Z~VHZQ|hrPt2n`Z9Qhdu{~h%lq;4(^wiurjy~=%xC95Z;A-21%|)LF#Yeh* zroTrr?P5_cJDdDSpKOK=>E%{g_bi2i{x@0BMW@`5J5Hi04n44~>EijztE~%dK?vrLQe4pUodPXi%xH8eFI{Eh zx1s53H$J{syBGp&c@xldFQdKo;OVFG-a)q?2cw6<&~}8c7BKk3j6A$eU(X|!(G(8X zn&?YhHW=Beh}Q_hO@U>Eo4Fdi#NWZ2x57u;sv68OmfQ~}#u)uSs9lU^lsI(ZLdK*w zUb()yEuNs%ynYyOv`y}I$`3{NQd`GQchJGt;>X{J9}8$~%hl-&LucA5&g+JO$#^>Q zm<&?mDl8sYyG<7B|Z8J;#Js-Qa+>85HvWPB*o~dH}fgz$DP~=SXKXt}!qdY^C>& zW{+1xc)%odkWzZTIDjE|cA5G`A8t<4E6{6O;GBS{j;W>k7}Lg(QvI&A06*eM_n7S1 z;-LxKlw%Njo0hpI2?Tf3J2fG6)s^N#mV5FZ)=m!H%ob2I>q=P-C~ z$|aB5>Lp0GJj466n2J>stKMq^ON$(*Gk${W^sIJ{G*EWE@$s4~d~3JKWZs}5=&Jds z8S249!Jr&vCs)+B?tzv%$AhJE@nrdZYa8AIhC98(fY{eF-EE$0TsS_4G4%yca$>KS z*|;t|nB;<==&Cf&YYiGTbu`zciAPquz)@W_Y~F!G&-2!0q(J~b?^l<8%{z0~6MgKX zjZ)t(8Q#?64aH9d+TKS!Q;xsu`~gBH*@u@alOcv2dn(*#kels#mCL}t%*FE8j~O9# zOvRh=$y#Ec^Ihs@GwNTwFwOzC(he6@wqp(SVz&qDK#4s@aIs}@j<<3W8!3&jXke^YPf z2OcaY8FV6-f(1AjOLM~MmIqrIkTWE?CYO7kA;<}W2YDzyZLtFG+}XX=p)en;&J=(2 zMN2QNp~qxmFqsuUBiz71Vi*% zL1~C`#7ZDgjOe9Y`dOvV<@p2OcZ&@{$fW3a&sHNV!;a-$;u&-@N+*d>_<44zhw^Zg zaAwPpz`Yl8JH*75L%6YWBaX}a#laCMT6y~?j*}&D)~LAV1f;F41P?_2R0MGr1DX)m zt`%fDC4+0Fcs%#!UfarI<`|W27uV_sA?-mcw_CL>>b&}ypZ)38&;1X7djy&}(j60O z74CM%r=Lvm5-;FC{Hy=bYU};GtAG6Kzp?s>ANs=TFaP-(?#TF0>e<-fA}o!|cL)erypPbHwfGDeFhDU6RP#VLGW|FG>DDLWLz)FIm199`;2 zSPbNFXFpGx_Ti?ipiLJN^6#dA*`7dv5Zv?$W7ZIr_(tvBxUGV7e53J`Cx@1 zj}f3}BRur&c+|Z-1qh&Lf`fNPXz!cU#xRg5TYf_864d!@aULE&Uh=^c%*(cv+Zhgx zBgK<)C-%Vv_iXD2M#(#gux6_op>{kXlQ0N`+-Pw^#uTd~2bIU0;N--%6K?Gb{Z${j zicT(cbukju`Sqh{b?vRIvUR=CRFtt-rksz{MrDog77ja-b$P3=#;WcQ58z^+JQ180 z#ZhI-Q|?sa#-!)k*#rF>GS4#-USG-G55opqm-0II0mm;zwWcXc-dWPo-SFI8|%Ltfpy?puHiW3vfm@Y zxlP)@eW|NvRNu3wU7#VG`n?0zo9)1pud==y?Ni1WvZgQkS3$ws++Z{~*POCt0M}|_T89naz z;7((}$h6R3M_1R*@|kz$*sbQo_LX}R$d(dn>=}7&+uYtN;A2Jzak?niRX=mZ{LnUa zx^*x5vwP-hSIzn8qI$3A0#5iOaN3qhQMNtmI66pqUf{XJFjC$oIWbImp4&3UAY%)a zK2mz0c033g0^Ss~Wnq9qD}F)VPiQw~dVCk4)}nAY?a}zy3dQ?|GL6r`g%Oy&j~@hE zaO}_Y&vvEhYxOz)+_h7e)X!sy7mXMzU}2mI+42#Twb}bg@_K#KFhYzE7;lsIv{MlX>4n1ny6{8W@+=+Lb$>?%&6#dH^ z6<-tJlX1uPR@+%0=k2;|b#2t*k>)5^;F%UjwEJOgAZpIB^|f2zY#uL+JdOuH%$Q?K z4W%ac*x_+!^W0%D;rYTCf*xPY^Xo$1nT+c^chB!-^qIL^z0vxId1V%Fvbd9-_weUi`3XfLyTkqXi z-DvfD=2~VmulnrjwKdX=$-dJ2v^X(=5B4WjgGeqIs{i3yT1ChcgB0i z0++2r<26to-fQTp#E41ZG{?ck_B;kuIBp!2#lQzX{i*-qJi=7@l=Gyt>ayo9y3lWi zJaio064xqI(zLGupYd5}X>%sHScHSWwhayKsXbc7YvyjW?%+X+XNIN=c?vs`)56it zTE8HdH5xO<0@kV0$L7-jVl{Xmy-sG+V%ky5JDzbU7(jFBcA zKgKZc@HlR2mX>^L(PU|XFpY0Jrs|0Ja}7dWVnf@;ul8qct85qJKN10b5{`O zqdX`F=fXz@JSWthGS8cXXU3bSE!5%LWAfazC3tzaG8x@@Ao1)W)0|F*o^4^(yO$h7 z7CDNyY_$jkH}5Bl&?nBf;BcdZSl+#PWA%+2c^*FMP>5vdqfS}8*Ga=~?|x%-uAK!h zTB54GkZF*3kSc`P;A$5p(RQmKWP6tRyB`!YC&i36%Wi}-bf5X+nFMxLnjf^1 zeZLhp#t_O1ql3g_<$%k4^NbLONIy&{ycl78v87Wlb$ZnS;j z^;Ue9dpkk&LHP+MZxnRd(XBT#)?5oVf922qxlUT4G%P_@FV?r5k7CwOR=@F^zq$JN z{`G%z^@UG;wn+LZG6}$EI|3GLbL7kde}47f|DFGF^;iGh|6ujQKlp{!-}~i%xH?-O zUrnL9ks>y(?+Kw|ZkYGu!O|6m$D7AiJHZP%V43;P65D>xamV_qMwx$s}!Ek@u#hL5TI&JcPgiAjGb@-Z!uy<4M(3Y z<5K%;6L;(<9rJ6e9wD2Dk5hEeI?Q*kEidrv9_2^<^~ZQ0oTO}}WDtr@QeNh0>1vL` zJ*!Vs2qf@N8%FS~fT7vpl0MJu>zR%OmfG1nYy7FWg{;n3Og#L3=7rO}Q?=b3*h{!j zz-)6MHEdGl`WLmGa>j6BJk-~J^^Dtkeb7$rnTGTiygcV=F~*GQan0LDS`^E|9Ie#az{w-kfKik1|pH z;640jlV86-o)qvcx4-$u243Hkab4z8Uu8V^TNN8xIp4-%{qanry=T1rfNSQydd9dC z>^;JZtu9mW(V(8{<fAosHo6m9P|;t8ZOWl7M7Ex$!>>N`=}%?!X)g4RqqUdq zsr5Ne8!&?ka7es0@p&jLlwv0vJwDuTd&}jKIrK@{yz$J_tI39s%IKytsWLSm;7X5B~kDQ;-QeujS6%13t`QO)?I z|Fa#daY2hDyk;?yFs^Lp`FC}cX729{UE|eK84NM{L4GnWj9N}kA`em2eYAaWdP-jA zq&Ap`3HmSpW#qW5pRAEN3hMyG7AoRoZ+LHSpK|!GN#?`nD2vJ@$(TIW!vH(Bailp zkz;!moF&zXe{(-$|8Db^mxu)w$E3rxqddZRusCfkb5zgUrk8iuxpTXnXj$E1wK6SC zzW>4PkqKVDc0FSl&#Dxk;Lv0U-fcaoe!m$uZLz(_fL5E^2R4TcJmfi+VgjCENmhU- z#$Vfw!;`#t7dk{174Hb{EZ$k>3#5go2jTr;C)eD*9UQ=}!x6R{+r17&5VMaFgS>64 zLc&jX*nY-w|xp)+~liv=v`zoC;>WoAHEkYJ#WsEj|@^=-YV3hUfk7oxmHN zVbmk%P)NZKnLbVl4#3^Vt>dopN2j$1+;NU41(HU$dsITFWLgY8lwXFq)XJ&Hd=uQa zX*loH$&P|=VXohfwx0#F>K+~wjnW(VsPw*uR#KYHU;Sm&G%pyAHFc!0#+Y4dc){Rj zzk4#J)d9S7I)!}dWBDU3l~=ynXYOt>7Hea5bbH#4o-Lp52Q)FV7@7COeMU#Pv)%aZ zREJ})r^XFU4!4!Bh)pSI<4}7SYxs0AaKXQbu4J7#jG`=HH?(8@J@1n*ddFwoNLLH} zajTC$co&Zm>Of|A0;a>GywE+WoO0wN%QdXCpb59sJAA52;S5hJx}XZ+&o`yby{hui z9422Z`IgM;s97^ljLXLhac}aM3E!KBRh@EpYRyB&j@qFw%iz23|H4-pW%@b%$C%DQ zPLl>hr%YW~U|^i#K{ujzym`z4&t|bNoLSmm3&Q<*53Y9s7tv&62uWvN^u6Kji;=uJ zA8rhvG5;#eiCL$ECw`K&*|yde(Ry20gp(E|j(E-Zgow6sIr-H*ve@F(M8+THzWwM_ z67#fg+SnM2ld}Y0P8O(qr`tX%pWPC8?Hs(IZ#_F5KRBCS1CDf#TtdU+=55BE^XV-o z>0(=#3O})v@wj;grjO#&_oA-{(LOqWMV>Rw-*ZkYGv{jGoxDPCy#H>wU##@P8hU1> zJ6NsJbD&0HU=ckDAA^UvJ+3e!jz`QR*lHJbJ7?bHCnC*|ySjwvSaj#PL?R%_t;c zZHO>|)iZ*Lw4bG!M@D3HnB=87?do8*3|wyY@^JTYg3j5A#o)A>gW>?4VzK5WRQ&{b z3XpI8zn9?ZbR~+(8!x{;1ogP~m1XrXA#=Mnoh#NsdmR!06OJ-gOhDlTDsgI_9OogD zu)LL!eX)88+${f;1ATs}?G0yg=YE)=@;L3us_*qy}!0O$%-fgw4 zV|2Ng*PinU)@^pkwf@5D|Nhl~mci(?)dyet%Ib&y!WUMz-g$R*q1XhR`h$T2Ok>2Di{70`W*WfH|UVUJ+s9~xD?$|hu3Lz)$?UOtlo%KO18^5YmF z!vcmWkmbXD9yXQH7#VA;tKt+ma+!b(R$>@f<+WOB8{Y)btACjF990`b#BA+h*9JoH zaf}T^W*|CqxHF0d1Jr`Ubt!?1AjDYIOD4()Ow*A1JM)R0x2CV)sHgClK!CPK&PyNB zFT#){-^O^dxu;!}>k*vQ7t3c%pB2h}F_uUZZ(x}o^ZVJKU_EZiQm~CqSTp~n%yR^`T<(cb1HF?*sjj*v8ziM;YU_GUgr^!}|;=1_* zL$K4<=lLHaj{Fk1DR%+ibM2XW-3_0D{kNof;p2C`zdQhIWurIGmqDqSx~I~fm2Yxv zI`>?WJ({Es1C#Y{!#p_AQ~dU9O8TYd*ZceL28pRHqepm{0mJqxZ6Tl$-psu-tsI&s z3<*3Rttfx>-M3d)Kk>rU=pylBn94wDDiB9Xy&lCdQV$;h3v2A zfpL%*&5~@VGw$R462D<=r|2^X>8rLI|8u98Ep3bvj9bbv4h&rAt0p#_UE^3`s}0x|1gvibUi5?X!wNa+{&c8M<*PZbQmVLxuApJ9p8dcTk7wu=a6 zyu@l*)01FF!Hp_JBVZTfcM4Z+v=xv?%C&eRg~S0IVkbSy!;(RIOZ>c(R*z3#43G2p zipIPgKSO^Q7Hwe@KV#+!gZ*s7jrNBh=fg9&E^za^dDI&3yW!mZ3|)Pa^M*3Ss|607sjjmbs%{jE3IE0u8ugI}PPG_B zULHeg;}8c4hImC|74F#fg{Pqij~eSc1#xD4+HVYOryRMw3H~T%XP%tPi|NUejHvPX zCVBIWLA@AG;q8d_-Me!6TDY=ZFzAe6ji*UAyfIv4;;UXcMCy*pECBEi~>xJ9*WK z8E8v+(K^P{N8tnSw`T$KgO7Q=U7r3=z4|alp2}Dj-|?gBsXR1iXmcvQ{EI;)ZrG1T zv;xYj?Tz(38`{!y^rdl_xI5(^K9kG{&0R%u8>nE7;b4*WNKhz~-A$53~jN7hOY3RJOe5a+B)fs{DfwZ6*n|+BbRV2XsNI|X$4DK;JUfi^QGy^7U)~)7E&sD zaAElNWLM9UUq5sgj{A@y4GBD5@PpB)ank-W+7gNOqRnxz=2Rw~o(IL=C5N_XanK@n zbsptm=^z!cBkc%iPr~5tXx+{G z(Q(`R@z?EBc~vrQGnQFoWk4?vqqi8QJm|$VJ(Kt4)AN^B+m~DL*(uxwoR1*zq;wkL zVf{e+FSq^lLh(tj95>f1=W+AnK{Wcq4sUqT(hFFj-Q^54XD?mJ_??GOby|Uoln38d4HIs`^PoAQO$f^fUl%$xgn^;HGK!o| zkph2~{`~~D{RBnIaL}b2nHE$ay-Wm{rn?dGuUh zFpF_nEf2wBkZT;Oj@gP56WKnMFrA>aU!Et<&ut!Uw;`ATkFvU3(AdYVT%ZZyY6bt% z!6I-GyaiAOd~WnysLwQ}R+>lzj*qkjqlr339=J2xRe~S-g0{g6a|GT`j3%^=@X|A0 zg&4kZp=3Bt_=DU{DWS$Nay7Ve_vG%38BZ`Pp%=~zyjovc=*P^q!c914b!JY62o8iW zv`NrO!sDrRY?i@=+Noa8FzA~qFqK4+RRZ8)DlQ!1_ zug%TL6TVcYd7$sc3Cy&KLbD|J1Q>=J4eO%k)9$$&{i|&|!FH22y#%wNf89{F7ExNt z{!x;7lgi+orR=5se$Q3;v=M$Ae|;I@*z?Nvbn58&^rwEw1MX6nT`!fnzpSQ*m(6im zrtIE%5RRX|wHUM*C5*TJF>pBRtJ8|$*Y6Aqa7n{Rm- zi1p)coF_zpi9kaANPJhDy0R$qJq{%8|?U}zxNqib7R%Ms`T9zTeOF^F8t zXvheR=8Mf^+bvw#$v_US=(rd$=9Mi3%D+|op4*-N;xwv?UY*OMW+!hJZJ5BEEofB4 zc=8-&0F)>wmxA-fV(D1O;1Pzl(nAg@;PvN80gTVKz4_%UujQH4vG(`FtCRwQ=++hE z5Dh61|D(sp@%h)rVDHHJ4~x09pV!dEJb!vUWhRfylpluAF@VN%-}x{eX3>U0r@F3X zg-pgM zi%g8Mv1c9E**HbB8laKctAFI?JoC_X`B0zJ6APq}e$fDQfY%)SbPWc)-FVCDxIQz2 zp!E~4qq0sKFLHuyQG%hvzmZkyKf~YSWEs44tB-|W@bTfb!l;Gspnv9(?>RB8J~On8 z*Hq;)C{dhwxlqa@JwxBjpM|Hl0MoTOz1yi_6^v{J2)MH#+WA#5Ch)8I7NB000K976)~a&*rDa*Wb!T_{ zEXWd7TKuii|@T5vJ?Dke|-gvK6#K0dMh6m1A!`IpruO1#B9L;`sbT+(yn!7oV zEZYMEL!;X`#ou(;i~jS}e)tfLZ;RKh4{Gd-l@_V;#cI>EW+r}Dh{Y%f z&=BG`5QCf$(!6MvARZ9ca~RpYXaMo2B|n!_Q^mUsL^Bz@5<)D9>MievkQFwDfq6IF zV%k@3c`0+V@>O|LQ*9gJdK#d>YI)YCWs)F#^KS1?Bm4XNN#OB^mB8yoN<-8Trtcxa z1p+gsK%hW0HfbSV4U4f^r(qfqakybjJ#)RuE8E9^Z{GcWzCl#2gR9{pgi*KCkqG|0 zG`136@6Tx{ViCYwt5FF63GMH^`|j!o-h8t%1SjR9odS_X`HZbSXLG%7CGEr8HwV?; zeC^diT?85)Ue0*(xV@w&m1A}JaZGzR1?1x8>WSEaEc{j{qw0f%Z7$&yCC7i-#&9O) zqHw1s+!y3>>+-}sAfOOz_G-r`KJkeHlqGDI_b9=Rk>%FiI|XOFJeF_b@};&ZT&fPt zO8+S%Tq931@9(7moV~oW`ZGWMQ>)+k$G6cf3>p%UQ ztIxNZdjIWrS9dw8#HztE)fo#>CXw>p0k>R#pGl z4}Na--q*j;(el?jW$d+JUHg02iomrDGz>?Qy}OQ6q>xHh*N+bq*sQ!a$E}Q>Dh|U= z@d9$DYM>kKP6@JFY{i<4Wl-iw*T$OLEXL6wk06A`P=2;f90|#2qLy&7_a9d#VZ$~7 zN)N%D_kkmTt<(}&7?RXO8ROk!HD!dj+6x}8XG=qZ;N7+`P`W6Olri6TD+`PW&R~YA zp67v8Kks#)7a9XkfSGoovG8RhFfMe|qh+3=r*QqGtzunPvh%73niCGYxPr7ypIO{Q-yaOT5v z7Gu}E{+<6Q>{+{(AB%vvyygxNNi$yM)ffCnKSQJLFSJ4*Hho{-_~yUf3#q0)*WgW< z_DxWetbm^r=cDUW83TrQ*FP)jJRAjUK3zwx1YXNyghKg2GzDj;K;r8>S9186;Jjy| zE#?v!F+iyG*-2^j!8_trS>>O-`r)N{vyDzv5B%5vN8P*j(sJt3gHAE>`>azD35r9% z1vg&kw3T*74@LxAsb@;?=q~!jz=dXL%NTp6&c-tu#9*|M+pK(^%fo_FjMfw88KD?< z#yhC8m5ZJ+{3y>Hyq~efbM%TrpdEubwOyNE%HW31GekYC-RJAqc)F$lh+TFVO-9F* zb&&A=C^*2QlfhX4e0LfQ+^~QI_7(%c(OBGc@WRk6hNm;D zSMsPmp9SebbIeI_#znsO+ivwRPmQ~6w>lMGoNFsX#iUFvJjc8v7fmqJ=s&|*Kx+4x z74YPa>OG)ng=;*dC`t^)L*sf6P3-ULVG|s$zfz!PIP}KruTHxsPF&-I=J95^-*|Y( z+!YVVwzx|fnb4e9UtZoh-{Q+`l}iR9$7>^F4>%ayC$-VM8s&6`JY~0WIjL<3t?gJ6 z%;NF27c54Bt*k~q!z`YJ!bI}#0bqu?fwwr`s78Y63>E8 zRRy|{HB`HN8o%CSMAt@X+7Zs#jt4~s-sNH7U{q2@7LlRJa1b5wF^>oTt9L6PZ4RG_ z;tu^ltK^@ygj*6wFAMdxr+Hb{(lXb@M)@eTML7pnD({_e%+%N0`QL3$01g!yV^0q) zDx{|OxVEsGl>F9B^H#si2T#VxHRb!EDlqF)nKgUp?r!}epB+a7^;H@VvVj{OG$T4d zM(g$0d>R=zQ9q~OoAwf45~cRDy0o~9(@8a|aHFMh68{NS6D8MM%UeX^hi z4`drS=_~IegISWtAWkWjt5D^R47%Lxm*@Rh&(Wp3pxwV|H~QDRT`&K%vHn*NFp^yc z?%E-Tg-x&s$Kfa5LT_<$;ha>#!_)u(KmbWZK~$&}42H&++ajT6`FP#D;dN-v@XGc8 zEp|d6kP5ng)YjvtEtoKV^7Sd#)O!lP1rJ_l7A;TZm37$M#hb;wBzHNraN3*k?lHqs zg)UbB$I zF&_DXr5D)Hvlw`W@IovD(7-GR4qYKfhy~}uI7kOU{KNcAUXySH-;kZ94y%FWGx4MT z@D`Ow#O`;71KndYIld$--9a97v1X$vJ4aY8D zxHPWDTM0x?kD(kW-=2QuoNZO-Zt-ufn+{!MK|7 z!yeXC_N*qrzV-fF3Fs%Qmrq_B!S+GkM|;LYC>vpeOhsJlFo(gOCA4;pzp7~_{A>j>cdvEc`#kPc4>9JRp%>tB<;4J_N^~}rM@pGbBy6AEBw6-7Zd<+7Wi~L zciwq>b>&OHv-;c*{ZIigV@eq^Ub%X0^($@P`jbESg~@*-=NXC7 zmin8Z$EfqL)nqFGybS`~cv~H}*L^4E^rV&8vkCi$8PvkUQ9K1w+>IWb7t}RaS$Soo zIcZ#ZcsSL{5vgMSNW8bk11$8ls>?WM{CV;aL~%e0I=o=W8RKT<_?C~txmR73Dd{)? zjsna;un5yuMuTn2!g%VOx|Pt*6DnaR#^(ew^=kLo1XBkL$iJU5axdj6Vz|)D;DoL& zUMg$4Q#}L_blWN&x=Qhzgy!}St413aj3%h`s~`F_g34rG?vK#sdH)HL;JqZY9F1L` ztqYrco>wh7X3`i~dvAo5?hMWDJ^0V4u&huM_^Q9UmU8A3SILUW^NiQNk9ZGd({{8D}8babjMyLJW|bZLvDY%jbFl z@NE;-Mn;=!o#IEH5Z7xA&e6G=omb%!W9^i!IipDNs3_jrnYM;b-fH-Y(HMkB_WQTQVa;LM=n-ew$l_we%JeI$OGQ~e~$n*_g& z1tW0)>ZC!I>b#iRY9x7f9B<+&qEqS`BU|^?!Ao=IdxLM$LQ!;PfzAX7P5y^BF%nR=B?5FSDhw z=K^{U|If%%jg!Y*eHdR6TaQQ2VVRl>yU?c-+M0_VH;XXZ}VzSrs_B9ZHv@P zZE3cZ;?9G+d3%I!(KcIvDM*Y{fC<*T8!Uuqr#NIh=e8Sdee)0PK`#a1PV#Dd^f&9Um!r^REXQD^`fnt4wj#dzY~QCFjpj26+&z<@TA!w&T;eznuq zob8N^+SRQY6EdJh1RCq;FuiNb$-tua%r@=H0T@J1Ip&0rBr9IqVZ^|%cF3_lxvZD_+n@9|hqpTdW(YWAez!L^YtH3o}1 zKD>gSReg9RxGdf|^g8g;hkz2C@bU?{ps-MfdUXW80AO*WcZcBz$Y zFBvl!1}|Rxc>L4_9*wVdYtFQ#D`PX`m)F=+vT`(Pf4i-+a6cM)8h$qZWBjTk;`{Kv z-fJE&x_UTm;iYG7-W?uq4B{pWPFMca*f+oot*E^VjNo9~%>cZ1h4SHqntHcwmX>|N z=Uxj?cR&1~1#Nsr+)T2=M{-fwUWoL)ORj&&%|%!=&gud}2KtcI`|budQXLafN28*Rp`s26#M~J`2RxD&-d{=RXZQ z7F=56OOsk1h}<7kf?Lo8?#`((34;hMqCp8Kbly=@vV#% z0%Fb{*p!D6_QLAjl$MuYdnG1Z-!n`Q^d7V|=vD&t%P%ePur(|xd4gV!64+c*?`)+Y zyhlU{-e7T%AdD~*Mg_yX_Hyp#-P?;XoSu`n!g0hlrY9KY(Zim#2j4t;Vmnu3@M=fC zakwGB=1##;%h zufP6A1U}(D@8jJex2Y?%_^b zvBLT^#jIO>+uBr+!p6*)!ZX`M4sPU*TK(r7=PNEj{bYDP)!s1s&%^SLq*x9EW+ROWLf@?r|(9)%-LA)$6xEWdAqJi51QXqKl5$j9RvUaRl92AhUO0) zDBma~HFVSVO$9SI$ytrV_ez7jTlPuM3|9S~I!6%5Euc0SlSNQk@{Wg2bEBbNry3i; zW+hVp)@5vHnDQEmrPo!sf!)$K4FC^SE&ZKu_uTd=3wlJL@ze7S-|Oe3AToWKG3?GW zBUvN+tdk!-T)(w^p{2O(@_1<-nhU!O^{dP#+Y078KY6IDvZFly-o0f(+s3}rbHJOnwzk_7TkAJn5^1T{EWX7 zKn}!0$(ZxFd3dRE&U7*m7&(3GULH9Nb__ix z$s{B2wEOfYKDoMiw=GG{O-HfckFGJ|Ub)zbQ{sQaK(R|X-5l1cs6cQT6P`J$*SW3n+AkEdews22@o{9$xKb7z%1 z7;TI>+7n`rm*!%W&Z{S5nn8!BjLda&@SH~hBM;gsme9l->V0#-I4(+8G_~@;sUj9H zmGT&4xT4Kt(C(h{cNsl`k#_|vewfiAuuNO`gJ};VTSLJZeu>=YF`h7yn{-B>al4b0;3pz;mmuQnt=8##6+}$AXET z$;h-9?_iW?;p)z+PqazXqLYM&FrZ0)&x@myZM7lUv8+H3n;_|J>i zR=)id7sqLjAt|Gg?c)rPvppDYHa{s*Jd(C{%w7D5$I)`laInZxWGqIW+7?WzYUU85 z>TcUDY(W$Yj_2%|aLtkIV(1(n=hYW%!~oJa_@qAc{0a}-Xn~J$XPkW+Zk|d8v6W8n z)tPg_x$MJZBdLwz!qc8+9NI$%`W%HQ4rPIVr;~9gci5geVP2zM=n>rDxiWmPI?WUJ z#*4PRwyGTk7x=iu1EZW)LVS^7_emZG`1EXb?mcvNXn5aY+RPt3Uq}fwi9u;O8Huut zhGvirCmW*bO@65U#Ve>vv@LwV6&bF?0k3Mq8@!@xynXr4apKS~<7#*^}T24;%zWaw}=ryqUKyHk#N5G?7R19c_#a&YnDeEC@( zAL%J?V914f)hC6~r{}qMv}@|99m*w>c-J5g{NZeTINf0{x`b}1eHPo5*Sxg7YPa1A z4tKFA(r9*V!L%)x$;nR-lXZEm%Zsl?&tN`zdncpaPIOTDjG#h4G|<7;cvMY&8!QS>;kLNIwn&QQ zQ)_f=c%oeh`+e|U-iL?H^Cj*dxV$(iD)C~bAuNc^bIlvzZ;*4aI^-cq~awq z@60?ahdLDYFCoq3`aQ~7g!|>;Tt|pDN&VXKJvSq!hS+~EX&Bb`lJQ?K&!*25N=)K= zO^C%Pn!y3_T8wnd+k+#OhiE$mXOz4(VS8pt{$4Bke)0uU!oK7F_A_QYO+Z?r(T6(` z3bAMi)*GXxBhtm^!Mv|`47qQL0mXh8b-&N&`D69as-=EiNDv|*9p)N-SgZo8cxZDb zrMxm)0WTjf9H)6P8o^&dk;SFiO#r)erIoXu$I21_&#ivoGoN0)`@x6fB7QHgn}=;b zIRE5If(OGvr+n2Pgz`$BNn7V)6df%)N6$u7?Q7@SKOQ3o8Gr6RTs=%dy4UfoPg6dw z7i3xg&!qI7$}{I|tFU(xjs?>^b8=?&<3IMJtN-Qaer^O{r?K3+QBcaZj5(d;$A08T z5_UTp{jIlF?-l^f93Cc~u=Myro&v#oJ2>3C`(TLp-Bv|^=~sVc_39h1uDEqd~2tzPm;E8>fZ=&&xD?(nD6>mmli>`c}H;U2IYVNkLHZgE|`P? z)xM;qJqs;zX9EXpix=r8ErM+AZoI%ynw2W~@S}K;vX+GJ^(&Y@gY){ye3eaZBGvQH zy|3s+yx$a61>KzStv?%jI!(~wZ%IoV7Q@g|-i&|qi2x|5=2rAUz2Gxr5ngRpZY}Z% zw%vmN}eOR8VUIIK!mvD+ePm29JL4C7_+jFwKW$r!utJ zc8vyUXeW-%YXhEAAoqeTgS4%(mtOAlqr47IWvH&!guUIo9y9Rt60a5t)C07g2Nk+Kg@xkb9o;;`Wn$?b{qo{0U7&|Xy1esAVqhIIpX1SV?Q9$aQcmX+q5giX?K*C4#%ZZ9S zPZ|FAD)%r;3#FMs1dfdnI$2M@#e~|(u9POzV==sC#ETd1>|E+JF0p|YuUUqNn|T!< zodn0~7duAa<(<5Q84J)(RN_9SIfYb8uR36oE`EjuqCr|a@ujui3 z8&<_e)>+zFyVaviB^tkej0{!z+REX|_3jPBSVN5 zA`na6GtSkqpxR;;-K^>&XWuRTUTC5j&nMlKbZPqh?8W85GQAceVX}eOVgX;?`~Sk< zfpzVk{(XlAgUx0PHxLGMrF1RhL?g!BXlOCahXn>2_?am;zk$#p}70@diQk>}k>E31$K#^3V|c`+f$UJFPM^ zstmJlWtT-f2wN?E&MhlkhaL7_#19ZPjus zqHb09xc=U2W%sal6Do!nH-W5NV~(Ht?B~W9^|dd3V|CK1N+QOlsh z*Ya}uL@|FJ<{|KUhM(W~-QOvG(aj;~uczD*{H`UW^JsFMHv^V96GtuAojS=YLFc0p zJgQD_#RRsB2T{OSiuq13e%cs3Z2ZP;y>Vh}IqnQk-c1AsxJIGyS%gTmDPb*m*^570 zum(%QlZ3U)3HH3TD6;nJ6F@0UkH&MSEmy&iaz{Ys@_efp4dww_!B|8g*~!CUgvnq; zu%kdPC(@v|ghd94@H2&%@ed;;@Q>SVI0OfI6IE&qc@c^+Ie18jlDC;vPW5($K=m8` zjzOhw3O~YSdTJN^M={abF%ErvN-1K}Qm%Qb%&Pd#NxsQbJE~Wzs)e2?Vx49+D{hZ= zZ?@(>U-TH55`t{Eo3wuD*Yo+-#SOe&Z|MF<^b5ZVzcY58I`dHjrtdT779nuvUt=+4 zbbmapo@IY_G0!)DmNM|C4VC-WeEL3e&gP}f_s2T%v7bvHD{c(X;NBh0oAGVTCw=^- zZW_xG>Kg~w@XFYQxkCrxiq*eK$^rkrEwqR!fhLDpGdBc4+-3+$0TdMc-aW<$Cqvy| zounk9PiH7;mCtYh3&&`H-U^CI0&Kd%L-9n0me%IG8Lufjynim{?RGX{6}1#to)N=f z;-9B^jx?t!tLM(9*hPcUDWBs6+P31`NqYC!#vuj_#w3YS1K!#Xj`~Jnu9wsIt>_QW3Q0Z+O3i(i&>J2p zg7NM~Z^c`=oyU=USEF@|ZW8{1mX9GWSn>=qcIJrfSz`E*b&TT}Z4BlFraG+je?~0_Z*7iB``YI$%}T$y+xHG;I9GQ=<1Mf)*{RaOlVwglgZ-j3Nx* z=WRc$K8qhtO}Uas)!lI8QGAOx#cbQEUHVR$;JLM%XU^jmD~{Tdbw7j3Tg7&}bypy4 zxD~#Ze@@-1t;PdijQUM~(E;?3=f|ZCIs!o3B6Gj3a}y{ZEf1%7#h$M|^bP&v+1hKR zB_0y~ebG3y-H`Xso!c4L>I3|Z418zJ9Qe^ZuOt*+X;+5dwJ>{#!qb7 zuG`^)ZF~>&NDFq=hNqY}WDT_bFptuOI%HG}|4s_VPws#Nf*M~rod+v}Qjy2a5A)Lk z6z^e&5DXqPF7QWUl-*B$DO(ARZ*-e`y;Q%A^umjE-n-@w{&g-xmlHA>8ISWo3u1#m zyn-3&YIX}-m&X8!@7{|KIZaIe9$L6EFT0QTjgC;+c&63*leTY+_f|L-xQ3>axom$4 zb>^Ni8_(uo&j4$_lFc_f7@fqEc?j^RajpV@G9ptx%@l@$?Qm?*%Qg5^Z5c12^1v7U z$87eh<4JStu#Y(#$DcWIWE*|3B3m%N&Gzr!F~4Vv5$N@9U4#C~LDf5ATrgyWF*nNl zBqW>D!;}>a%v}np*frbL*TkE4fVDP-GGovcv&2}yar0xgj8+%f!0ALo6T^YS5Sp$t z%6{MGw{17zjz-TZp6G64PjQxa2g9n|MNe9QM^^cWH-RA-Ei_~d8D$&V?;e=ee#yL1 zrzC&>=eax!Z&Jb%GZZ3@P?_YvZw!G)Em>iu4WM=S$%0@e_eoaZ*Lg& zz^E78)yB~~<U)#GElXW$OZ>M=7hSRfktGNf}1ONUA(MM5V--RTzk{rMd2%@zi3{c&>p>TufdPxLGd6r4Eyzhy! ztsFFFlMw!yKK)VljPZ=KvX=K3!L$h+LXKNoFIP;p^-p=dH;A&7v=scE{vuSR1)cPH zY4?U`B5sq`{j9dhTlGz%{*)i{u&O99m3@+)DbZfXCHGQZa@U=N@~e3Ry_6va5!Mgy?f*aA-D|U@SyI^d zU3E^KI(5Hv*Gx}O&rCB2<^mXW3CkcX83RHnEXfhJ6^`&5zqK8X@C$zchdzjH*@OZ? z*r7LsEE$1sc#T zuFPDSxpL)-R?h^}7eD>^&AY$%&gRPAxy>8F`j7s_FU%=mU-;CwZjK~`4rXIU=-w%c z$t=1igtiGV%!dC8)(dzRA~x*{IY65+wPjxn84)tuMXhP#7b<8E>m zEr7nh{MMUe>_DE?n3uV5h1bbcjUc`tkIWImTPaWA^k01`o1`P zKGbm#olnx-o~RdoDfW~$JoQ0-PF|;*6Y`^g^*JkX^&d(n;U2{%yt^^Y_3dk2lqLq} zT=ki*L^-Auun4Jra6^WX<7BnD%o~)>=Z>ZMP0mz-s}FMwx>)m4H$DJRec8&Z?Q}A) zaV*Tc-CRoM!prVmb?)W|;uOxN#d)nKJ@3}cgG=0MRGAm>F)vJWgI`WpbD@{OOF3MI ziftLu{Lp?QZLW$gk9t1Jo$Y)0*w9JCz3AY;&Q)I7A0J_od|6#9N42g+XI=Azpy1CDd4JR6UTkOAkjwqc$t z(%|JHZ_ajnYuvQCRg&QMO1^mQ+RKeiUKBE-w$5KHF>Erf!5A76`EZaib~dBcyz{7j z97eO}I#S%Cf)m}qlt>;07azkbT-ZW;zev8*wY|G`boz2poh}BhhzJork2&}&qD`D2G^Z*V z<@)E6F9C0G@SMqEVf;Cc$T3a~(Kw7Gmnas>Fa3ps>S4}iMiP?H5BbV0IN%k$X{#wk z{N~-9p}~OXb4ryDsa9w&$D#pc?j4jqVAM&^fG3G;1BvM3>Ww46fZlOx&5 z?Ac1wJQEQXMcOvjH#=_p&ZFdNvMTz6_Lp+t%(mBL+=FBp2fF#g_GQs0*K&-#{@N!d zsqj(sbK_=P2@jjkquqNQt$skZN5!HmUb*~<^2=Rl3^r}odwZ=)w=H=8W6@A$=$jXt zqwf^$`Xr~#@4xX@FduE+dgn%4iQ5WYQs!$#iouI~X$R$%3(RociCRTM9Lt{*{lK5` z5>fg@vhV1~CFwKmccOml3!}#P*uLeU1qrHcjS!JzVS~d24^*_d0uAQOX`bd__54z} zkr4ge+K`<3Q8G1hn*Lejz~^V0Q;e^xvJ9+9BBsJGZ*z8KHZ{ghKl&Mc9P}Geu_@XXf)Lrp!w72 zt@@wEC!80?N5f0EY`lwBJBnxaBF1^vbL|dJDmalTA#fZI(F1_MJ%0fT7JmaHID5Xk zrzzLik=q~aJAE9zu;+4u>64&jbKA`QQ_nOt_?c>YpPn&SAF2y;eUe1mc1`R9lQG|p zRpnsbQRe0miy^1`)8MJLQk^L=u|HAlb_q~#*i`sLbNI}Cwkv*ol>ECmgKYevSJ@mlBM?5E2l_)v+emJRjb_{16j>qRGqG5B0 zMQ?KUxP@A&C!R!$lSuox$RO=R2ZNTt-D_U1#okG0&Vi?#2zC*HKJ`UZTtKy$n?tlY zMBQK((YQ-GzL5OU_?N8uNM7S$39+Nt2jO_9L#&)4m~|LGlA*x%M8>9;xve%A=Rh%m7=3dlOz9bxmfN0=2G?`;H1mUwIGOgJfHr4T(zgeGBY zbg-VQylb3NUi>Cp;2=B*B%eD2=3Y5G4kFa(d?H4yL<+?ok2(Oq-c_GIZ1fKz|NrvR z{0P^8cz?yv6+wYJV|UdeI9NrW~i_E&P$ zm=-t?9!Do9_xp=>Uurv8s2F2ma%61{;W=H4;@k@0Vak~8J4by#Xp7T(MF`z!pZ%4W zuQmaMzmAKo;&DK{+9@+ukgTdQKt$VEm17vtXz=LywiUFnHLIJH5KbXmGY(pXv`y?< z&#%7xQbtG0eUUL76ZbO|&bCeAPrUxA&COr`?cq0l9wRb&z<~hE93to9-(83(1M8($ zeBUYO&)@x-pWgh?U-%1~ANz^Fz4;IS>R;P@@jL&G&2Rnkugq#U2MpX(HYxiInAuj> zHj4eCk&ezcu|K&pIFmT`M9Qj2pOZq@`!1s9Osl=;TP=8)Jh&6QpZ(+~Qcx>fpK%|V zW6~p|T*e#?!@*(4@!Fn6O#p+F#6}_N&YTQXpVsP83R?z9)8o*y@uisC%JnGRnf%=I zE<mNDmKBWi^FC{Ae>NkEgfFr{yOG%k1yqT;QFV0C4$^B9E zVI>@W@Y?cJt&!_9_{yobc3%{|OBRQB?QXA{9EZ#NTvb&?Uw}3p=t^swZ2bvAE_sL+PC(8cQ5`qZabYiP+r z$ABHqJk{Rws~gkiR+#M!^?Ba2Rre}AGIZO2Gn@Zh{@vc5?>kvbF?WC0Z*!oF!l_=4 z_&&C^bl+SY98SRiv#lDpZ{;jZo{+a^ikLA6!xdik{zO)cW3CgH+Io3DI#oXhrY%Y4 zWm~5}=pAUnOOF@`(Xqtm?t{@Er=Kk+qA13cO#V)^1Z%N+?zJt2!x*g{wAG8_L62;! zI(a5*whQl&V;4`K9~#!SEn3F!adKb`)3&BgK93CIOF6LNjKk&ApL{J@o|CvCx+6OB zz3W3)vuy>k8biGJv1{cfs~_a1NJZb>Ki{^lU4J-I@TPu>MiI@!pyqHjE|Th-6PEKO zr%7euMm>m!0eM_hqSF{HYKW-e%tw=)KE`CoKiEYT?dGL{CmBheDg#H#8#m51eZ}Ko zcZqreE2k6LVB4y#O0~W@kyGaBv*Z++8+@G3wt+P!_dF^T{#l;oeoTH@vhEh0QA?}P1l6m+Kr-Du8pO|G&qg%&MfWQp{v$cq&H z`@zVmz9LYHpsD|layY2pb~D?vF7BiW0a&VAe{tnVxIMkOMPfjFrsbv$L0>E%P`e5IaNHd4y(}BNdbR27U9AqMQIppYD z%KERjqY;qj#H<~%1Mk=po}aEmFM3ara78}PSR2pY;k+IDBH1ZYVhqec8rvnD%r+6c zs-1-E+S1mqaA$#G#z6VDNA)pu$H{X#+3T4(7;VgHRmn_pmENE&6UT}ghL7Y7L&!Cv zQqSr`IAs*$E%rhn4NRlkB;?jT4y~de0?Hzqu~5$Yvj|gtBh#vS98uu`tawk^ai(~o zYvwCox=(+VD1B;D4o;t!3$elGG$y%@dc3Yxd_4LqpZEyTCd4>6!2I zS6Ajf<_(9lAVGfwTlJ&!V;-LRK5Ne1(_+ocMfUi<`^3uwy=KQF)B8&1H1lRT6?(jS zFm+5lXo;Lpju)+XCYf}u#XixfdzC-e0_Pc#o#~(Gb&@cr4}%AUr|Fo<{?pMhS%0qx z!Rt4!H$D#ehzCnRe?EfesEqhb8cnpk$KH>=N)d4?N7BAIpuThOiPlHeBF!zxiZT>U zc&oZapPq>xEtb-?;faoE5yBj2tCg{q+pB>KtvQD1?>S4^@ean}9HOtvvBXg+A`l%{ z+1$5<`*dkDE?%|}z`pLYXFD2%%8$ymEZdI?G%vt`@k43Iqx!82F%C;K2#cR1V1v~F z5XM`&p2FT=g4pCu7$HTBz^%QJw~Y=3!%Aff0%HJp z&Y8nO0fvd{NvJUXOh}ikFelhd7PmWw)TAn6hLTKSd6FU_l$}8$_l^1}Dv~auTcP(8 z2A)8D>z%j9IPvar3O@qEI~NmRPkZikD@wEL`n%UlNLm77?G~zjE#ZuZZe`S5>=Z65 z^_1n4DG7{+JDdP1s*az%U#Psg$2pMFK$)Xd?58NdRK5jA#NJ7{7gkIGKKsPhvXY3N zEOF`QzHRfl-}~LoKl#mH-CVD&hj*WB{?vDTF(;Lk)SH`oIhurOqXGii1ZXU+&%b(%0O=>QLAI>Q1PJB6) zlgP^AQ7eWbeHbPr0EMw(8>8%LhJpRwoK#k79nX$dIE~~O;UJ@X+^=nIGSD8S)ZZ_{ zVR8y}PeQ%B63{&r8Dwh%L!cwsIRUH7#0m#s-(B!nvANK^-~NUV!@)R1l&=il*f~h> zr&Sy3-@I2@_!;>Y9({KS!%gH4UyyC?>-{pos?Q5U%H5|I?SOd>g#~sKie)) z2H+mLNU(={`J28OkcabYa9Jglqmuya?FOX!OtkJ)$1Bu>|`?78%L`|yS@$} ziq_1u-{*^xZ`$nn4(HRq1=1?tt5#plo$A!~^K0r_fBctMzMF z#(TccyA{|DOJ#evrUnPo@#PG>(n&;;*WN|rbqLLH4%o`-q>@R(+>2l(3rEo|*#$b4 zyzx|YajS>~C+*$7p8?#bRX+xGZi5ud;7N(JRSB$QfgV~JcdE=gMPYD^GGHY=cg*@) zFyW|JPRo0DA8am0=cfw2Vc=dZFUny~QwGeV^1IwBqEK>j$9vByK_Qo;mwbkY3q=|| z$kBWA_RZuOXGJnK8ki`&>SM&BtFvc1Nh&zxCVMzJX3A@poaS8NG=2AGr%Ux6e#vjT zh|9JdN!uMie^Y*%%8;wmpPUmSH^@ z(o^+o8Lf_oPqsA<=*MD%b{JOR!RJ;xjUmUq_T~Q}yU2Nt0!}##Da*;50$$zPcgZQ^ z#7ES~0Y;J=6V4f1hOCfk!!}Zj9vs#p4&*q3hllql?;lN6kMJ<&x|?)u~DX#?Tx`2;&V+6A~hTMCn-kh;kimDo@X) z{+d~n_Upg{z@IAu>L|_}n%z^nV=eaLdTz+^P$sTaDoyo_cv4l<&1Y?MgT$FV*LCEW|K0 z9}fNAMJtP_;;?~&SSbfdGAD>_I~lkeNY;RPlx=V_>v=9H` zd8ZG@PAW)W?g1mQsNDb~H2YW%5AtQ?ZMP@tvc5XaiQ|PM>QOq=Z1Vw24tunZ?-{aB zf*-C3V#(8$H&20e^w8+IriPDB#fRudg7$HqgxeX%7bYJ(gTBYHTsuKL{DPu6ON@|X z;_^UF66}iX}qk-l80J znS76aiS^X z1y8%>;c>)iZN>)P2E_1~E&~VnY(O+OR;Rx@2S>`xf=s`id}bbePMgt{_vWv6W;=TC ztm=(;^$n7?4NkMZuRm}|M>0RFWbnWysYK0={yClT#+2=!2+XKk^y2^&=_z{kTqo!r zr%6Dj~ zZK8aRn=_sy|JGs}n#{g&>Sc2oo@A$!zjAGz?ESNLEd($p>Wyq43l8Tid$GCy;)xtY zEmAs!34D9`kHnTarB7b9+jr>4^)1KMxg7Hr6htkd@wqs?8ZV2V4_cKzRe#Y6VRpHB z;o8M3=`q1t+hmTDxIStZp8ZH(2yc+t=LppKTm%jM7%2LlKLW$?dc$&v-(Ny?Lah3Z zaa_ab{YKpOU<%C?%HNaz++W|6t>zIbi}|)N#Le1>FSbmnR6fP(ocHHF-?vxgC)%a< zzPVQhZj%J>^zvHf&5XdGWSqII@?Zc;4N&!I!TdF#N*Q{-<=|@+n@2Bn`Rf(^W;`d{ z{O|gD{rc*@W6W6*il8PmIouHZ6q?aWR<+--f7gVEF3@rlS_+XBNfb!=vC1<^IlFg1 zVP$)kapQoZsEc@!)4;{qKrx_jZORQw@s&)?BqPUfg2^u_Ai?UHm7y24BCd)*oL z9GV2(oQ~4U@F)x!QgThm0p#KkFhPkNdX^x)nc?w?&wP6Ga?wI}Qj8fvOOaT-xs(Dt zl@N{otO&pQiB}Sa3D>QXBl#mokC02Nr=nO+*RH|b%ekTjDpmADItc(}B z1hYtkM~&AcT%DGin7Yq+RE8`Q0pUC6&f~`-ch;(foJS&Z@Pl#_dR`sSV5OMCW3}1X zSd~tFUE>QD@VMlf#QnR~mpJ>Pz>%tZAxJ`=7Up3uiUt`7Z$H-Ecp zSM~g^UeBDY;u5KKqSajdt=uTceGfr1wuFNAl%7S2+FAGY4*hk7 zZvXZf4AFx2T)wYpscpS;cm1PYRD}iZ(EP|!V$cnM{rspJsaOJ zcqf9gXU2pB#2Aq=vkKgODlLP7)8w#sM1sgq(xd3Uydk0rqR;)_aiYy$3LmBX$bKmF z0ACeGJLX}A7ekjrLyiSbSEuT6M0?kXM*)*u3}J=hlhu;io+-*;uNBOL3s*<45CzHT zsB7_6j`pLJSaKj4fq$*szM6bNtK_2X1>;aH!t%_=a9z#xE`#c&w&se6;Rtyrr~7>h zV+tjw&?*N%YkbK8hVRbNqHg`IYjc5joG)XkiUwGV10wF6g!0PAUm3iZS0p4xcN{$u z&YKI!6ZqnEcUseI)n$x#ojzERM#fM(#u$$?ZXR$L^d8tK^Hx=D$28^~Oxp2`fhX!@ zl3sUDl3>4Ajx{-s;3Rst-HkHdJDi;y&7wt(nP?((GSN-uqB?)roFV5~e&~r>v0ckt z&hbn2;Nr{^xpAflu(L(MGQG#S5)GJ}$kr1jy>_bB zJLOcnRb-9aLF75-qxm9g?ES{GcNkLZ$mg5|;dl~g=e&2~BKo=4yA!36(Ii>(gU9#h zv`cg%g5t)FqNQ?Vi9X|~F%I;FyKP5))cuAuyy|2R;RYNS1PlDXS9H-Igrl?R0H1p8<8x31XA$S#)0{^S z+7fmzr>^UQ!xF+b+L?ujzN4{PsNXrpB3b#bR+b|2aXDDv?@TCa5aY@ z_(dF?6MZqaNcb!YgA6o(;S-TS-q&yaoB1mqIQgtGsvZs>3n?Sdk_ooj;PW#j**WTT z%Tp-|_?J;Wxp&}=4&NUq$T(sPhaid^B`>;j7CZphMb8h*wYy{qoi!AUT$ygn1F~Bm z^~|XBZZOuBiCRwG$cX=2J<={ozY@UMaG zRd1&~ybc-Uz7tG$NWtTV)-~@}5q>g$#xr3$?U9A!bn|NO*zUX6A`%@;Ke{^+e@-bI zy~MlK1y;BwLjf{F&_DBgU|%*Bz8Jdeox$aT8d$4u4bb#0IdnpFP4#N$xIg;zDu>3_ zcxa4Fgneaa45r++Y%ln^;4qHCs~>O}EYGYkBZ}cst~budX6lMrT%Ob2a^i4+%wNgn z>Avek*!ZKU(9><-nrNP)UyNJ8jo#|;7u)_PCusbRN7<$06sBuN@1c8m9PG209gnbC zmg~Q&z!2_j+Y-@Z@y1+8$2;!($>yrl7C-P~^*yY@yV(MM)3I%V&IQ%q;?v1JmJWTt zat@(k_vqurb2%-wT{$|J!(!k9&ZS!z8V*mB|IB>W-<&koTyVa{AI=nxzjv~YK5ExT z*3sjL#GL{OX_4W0)h0<221JDk9#0m-r+6R;^h-!%>WCSGOq33$=sO}eU~6JR=$fVtC=a|(;q>rT2+2CF0eB8DYf5r7}2^f$f?88t$0@W00SU)72g zW3+*O&%ZH7>tcaMT5MzIgGgW0uKNH06+jqL_t*6 zNeRNQi_nR9D6`CZxTu^|mpi+`tR*bCta_U@ebu1aU=r#o1 z>s<SIV zbwlyI@tiy1B{(Rmp4+NLK|N>>?c+|Wx?WxH-pNrCUaq$N>M#G`59X*iwR!WMcQ!{U zog$#*^T!cKc`O2(eD)PjGsUG9ber1ey8fZ{Kn;06EAIk_J8}i z&G-JPKePGXKmYxkzyEiCYL1$vm{M9Uw=!zQ{oNFC2AqCTRIGrSoQ%hlCJPFKh(UZ$ zA?EOT{nb}DA1lJ?-i?&j+NRiwDxi!SD>8}VX4S~aC?_%w9u!i~&@f?w-wNtDVYY&V z;d@+|sjX$|+mS1W?^2GEMgGI@{bRcun&+rN>sEVHABH93E_axYaH-&cXEv0I`r_b--53c(4;v9XkR^WIV+}pH<$Q*K7Nv;OOBT>z4xU_Iz01C zWgc*%_GiqDhrUp-jD;i78O3;EZoaSm=eeXWn92Hp0`++!FIGb%kgAOkon^EPk*LJ#P}lk z?kBG(vD)Q8qFdk<7uj}6+u?z%7PV)TfFc-kPapVjV8R8n*wxtVml>SU|_?%Xf#oMAZd5OAzRzDGg9jXF&E?UT)%Anu{#3HdX_OBu2k&GG zXXy2|DUXvlSVew|1ad+gr;>R9?w*yLf4`%T@fai26=*$|QzpCxzXcy{UA%O5^X6Bs z&wNBraxoTd`y&&_Az=QkkH*tN)@&J$G1Z9zWJehDs((<8o4u+y?b3nlp`hWZ&9pZj{EMn?$c)(}2W+@~2_2HofmMRV(97wix z**eRaN3M;`iATnMNCm9x1H;;a5{(D@)8;J4rOy}yI6htcWbUZ^I7B$GayUgB&~?A**vevo^X=doqkoP9-5Qp>d`E&Rk{%fbmOkB3{RDxs*=*%&+th&^ zJfdBa#rPM$nVU}M+&suZQj7gpTggq`Y8G8ip5(u6TX^Fr-Bw;sj&n{Cy5jIe2%*~m zZ$Oa0$WdqE*?eO`Kv!(_9%4^kIkl_Cmvg7KkK-*nRZjFhh8+7ug7c`msGywXM{QlM z-X}$#(?t$~*|F^Md>I?5FPt7pFFup4yqB}*85vp~$H4)|bjFiyE1wS3hjEbCkH^_L z#|LD_$x=*|V>BD$%GnE>v)Lpcwd*76=pkraJBC4&2zW=Vco#j!HwjA$TLV6Xjbs~~ z24tsfE{DAuC^!*fFB9aH_ES8UIHbS+9f5PI?+gjTU>r5>Bl-x8AY%Ap3Q7vk{eq)^ zBM5aEcq=;NeNBig?=40e4(ju3UnWbJ2#G)HdJfa}A?BxTP#`HoYgMx-je$3?FmscJ zqo%()QIxuN2F|W6lb-A3)8$=fba12)P&?t_`QWY3dOFX(x_=nttFIAEUw!lI{PvDN zcrm%Yco|O$%g86=Q5oYh!mfhGdH25eb}%cmwy_bgAH?%0pG`DYiM6>K5AXg_eIkrZ zJ|FIiLTV12iKSLUQmPYFwmoU*-Xr-KN+Qf?IVm~e>J!6)<3R3$i#c})HEGn z+Z@WDk|8buvOyjT&jnF<0_BxQ2D!nZgO)27u8vX$ zc03BESy_rFT|0iGfD@>2#7Tm2@H+maJTLi-N8?Khul_Z82g+P?FPxxTC*nM9551MS z&_Za<=Qwq8C=E|y53W zIGLlag;%)P5=ZZ9d#+VchD(M`Ouj1-il=>aHCJ$Zwxi!3YHG&E1AO2zcNi-dc_y(f zWf?7V=(sqUy)%Z_OybL6y?-*pKN<9L4vz~~xU1)Mj*JgYHpUW9!emD#!P9bx`I9H-YQ9|n5 zOXT4qBMF z8!pL$r^(SXoF^PrBNuHMYdNAe9LKBA=K7;fc)8Ggck;=8q4rK?EXpw+MtLZBwv^Nc zm_%jG%5S*mptctMQndAQ+l((x6qx9T@-F4$sz_}-n{9k`srN+R;31LKjb+~)r+)vg z#hN?8Tx3!-a5o2==!5-mC>nJ|&F1iEF~Xe3>4o<=D(Hd?;miGrR(jkqP_|#AoiRG= zuSCt}R|dMRapSnDJ>rS8^vdP7oaLyw)xr=**yD%&VQ$ zK4ZqAso*e1^a8Nb;>i_`HG*vBt^Q3zbF-($Rse`o<&<7?h9^gD>oEStHGe^VnQHDk z@jAJMg3W)24a&(*T%!ZhLu{osZydC+Qp&2qg@qo+MiYY*RVJSjXoA=EEE$A9pC)7c z*Ni-$^!=&~r}`0W!6UN!^qJ)hLoeokV@PI@_i)X@J+de{^Dv&8g~7^_>&9J#)ab^2 z!V>4tA^Dj8!v+i7!I!qlXO2vHOIS#BFwPV6H$6eJb&eajBac@^9JxmqW%OG_>v^Aa z4E-D2SMQorSD&Y?XnX#veKff2BaR5RUGV#lGY5DBQ&_JYG#X>{Vnn^i3C4MLR-)nL zfG%0gvz`4+6*vL#Fo%z5%zgMnBlWLYbMQoV=mR>NYuZ?2wk*YUdm@hGZFrh(Zp}sT zr62mcpslP9c8z}2C#NY#q5sgtaSknQqJw>XLfhd?=k#a#IiVn6qw~xWc^qc-z zZxySY@io72GKvyEECKW()1%3jN{4X&9o2KBfAGPZv>*yhj2^dW6GQNm={*8j>*_MEL zKj-Mw?epdLMd+2^61;cES>BA(FTuwlKFa1WU5c1@z~fmk(MzZL zgwhB!Kej>B@s0k6L8A={$zclB&T&IX+V10+X)9~CDNS7JLh089 zgV0ge9A&8=^((d*f`d8TSzt5(aU%k>vDhZD} zZ7I4{I5}fpdlyRLIho{p=jiBSMu<@E75Y!^R<4l%9I5q!FllDWj5v8y_M0 zZxxO7$xnP@^J**Kws7s8O4#SLI^Rm|E3bWWcnH1yr62frH^(WVzwzt8mXF265M5V? zJOx%=Z6mrb5%u<>AlshtwEg7!C7$N2VpuQ?Oh%5Wee1?MGl75k&G$C{;OBlO-haIL zp&$5x%|G~=pPhUO*D|z3Oi_-mwkP*yhRvhK5#bFFq;!q39(^%Lu4Ht++mZJofWG%T zzkBo68*h$cY;tj88HHzmuM^Nxa=mZb8X7y10@~QC3tlJ`evc!r-^!T4C!u3ycE+>G zOnC`6$FOPgV`w_f#pn_@Wvj?mc5WLur`MvtHc+@j|sDEUp3x9iW2&!AV z`}44WJOi_Sgi7kwD!VNJ2TLgmf0fx?_)9y0V5F?|+PvFbGtQw3E(LVWU1$X@0Z3nV5k$NC zo`0&R`piRLeS!N$XAc-%a1M9x<9B-1{pku6y#K6Mj0^n`yo2N3mzQOY)l~WXA49pj zPYy&D^N#I`)iU#uC`EK^%hYTKNx37BT{E{tdthMrKkYpx9tsB21k?^j2F^8SZd-Gsp3@^3#w5#$XcaS0QK6on$0u z2^kJQjBc=ufgSx&*fn#z6Za%06=|{C{tL$(ZR+84JC4uIDVz(OaiWOEX;VG6Sbbd40b0;}s$YV@2;~<2%GK)UeG5yrtKvI zCqG`okWRhkvt*TVtgxLzwj7^05lvLp$#RNKj>F!!^+_MWDp~bj_D32>hCLjGhwd{f z;ou-A5WQ=UBZge41Ou2}F!Lo|>a|${03$$6RN;;kJ=wzVv-)zfcIc3&q8qYKOA*Et zHTTjD$WckzCnr_)h=7pH`$_y`Tp9cD?v$gv9@P=9;+xsxpKK-*&nObJnSlc%wF(Kp!& zZzsbsJi*iy49@&tU8u9~!G5ABDb5v6&Ziaj9{zO#(FRZDXWSQgZf?xDAd|V3Ih9_2wRsY6-bo+eRP4oIP2SS)^sqKX zcRF=;P7bTy^XD#Z&R*>Bg7X(g&kzkLCl7QxrIKASG>c!1ReUKb`AIVIet2}c>$#k? zmn{&*zaOBM?v{eb^h@bGFI?FVR2#dS)B+U?!S1Au$9GUdSnPlY`T$SUH;ALk};hkCa z3NKg7SMg`R_j@-#^>_c?=3}pSd~A*oym3%y^t~c@PG(SPR$H~+`a z{qLLq>lc1$a}ZBHZZ$Lx&VX#Zt4}8X2?uRVO|y8@sKX~?Uw#_fkgx+-mP2r_W16pT z{`7Z#=jM$!zOwlre)1=BDE+|Zd%o+tHrL<2p5pmv^VY++K;E9KI7!mIao7kM&KGWy+3 z^h)tD?#AbF3L{)OQ3p-pL-^*rvG<=Mi$0$om1wnl-fj#qh6H0yMRhA&IO#9Nzsa3Zg@%?A zSdXfXF*vjqOz56rI?7mWxQ4&fMc()PeJ8{`e*Vvk_Z!aWWnS)|WL9+=V-cClnWO1d zzY5&nIXnCwBxtPv2S^{=H^{%;HSMIxYzuDZj#aNMCbR0~%?dWm+wsL)yS}ZOrpl_N zvND43ImKaQ&KB?B%-PYNrgFuWj{( zFLJ;ZBnhO|DJp|}Sz9G@zHpv}H+6El&uVbvad<)WOv-%GaN4f2QKBt^ah{MF+%9!ZGnVDnK9w_A*GL{)B$97%F9J+6&gH;Wtz=hw=UbmNw- zW#v^X0_RDQR%G@4`e%#mxyI^jb$+}#?(+FgtW1Vv1_!r!d6o^Unl3~}i+E`(U=E`D z(b;=h_UBp!zi_%FpUsVr!jZ(?H{ZQco40Z<-H#{QlGlCPa2ZmU%I&rih&SKn#N_y~ zt(%ye=$;Imb8XFjMn5^xDRCV}@K%4+0fx53$jZIfDP)oN(1!(uciw(C-Sr@Ru4LSo zb6lQEcX+GqV)w%#{F`q$(cW(R0seJj9DWrc#ZfeoV~xqs2SL|3kh449=*B$rNjRWM zkx`-`y)(`?blt-lJN(vt7bljnn7ntrXFkWL7Oc=2gSgw{uyHER)A0SYtw1Z6TaGe( z8l6exPIB*M$oYL(pB!^f#wJ$U-Vwb=5Ath}ImaELOk$XmEfS0q&WT=?rT-H>{9PppyL=J z2Q5|_Ytbg0_HfMxao^&T^6;#QnQzcK_{f^{J-WKF1RuS@c3X4k=oS^lmvoO4-6x|t z+AW6Bn;6XFs16=U$~mpbl9P=Mz182GYDP|vFOI6L9Q*!gWCTRY(K-8;kA-Bt@4)E+yOK6Kb%acZxeaCo7kx{Zf%oA+&jTYXRi zdKo8V-^q4#z&XrT^;_Tj*VEwe=Ue|Z9&C}h-@l$sWDmNlzdDE};HwVJ*rHc*e`vCL z=*L6D{aewwKQfa((T!c|i*euu|8nr;@nIP(6$O(8q8~@R!DcK2;rbM0|cw zB;#HY(vvqY9=e;{xSu}0XI_t<=;kM~%Vm;8-*WCrwQx|KptR6p!Qn|`BO-6*#z|jo zygNc>B4%vYj7i5CJX1%wwpDNMbYm9{zSP3GlndxZumi{P<7h*_(GOVnlgVe>iv6r| zBHGU;lh2+n@qAG|4zl6&m-p?IT`hR&L2|~J23v$MNxDym>$MZ3dZX8SZ5bvvFXb@$ zs9kGT{-{9}JjH=^wtcIh!D8BcfjW{^038Z}9V zDNdxJ``(>v`wj-f6fa&FX)$C9IDtesRmWm%j2HtE7_~Wx+af3eSRN7C*}FUNzgXut z?0e0Qw{r{gh=h=(eUr4%XVDY^{GprFdpNLB79m z!bOO==A(PN`@2tygp0tYl(|3qaS>A2&gnzIzIg47v!g9N5!y3(B#?H`UM&Bw`(5)- za`HTARrhp)dXKXBn1?xqsMTLa|M?WThY#y}J3uU;3rZ_kI8Or9kIsX?68d^}g{NzmvziJ+f_g z3hA>Y!-|>7W%y>xbofv1oc)yGD;XuW9>TTj0S8rH|Kqk)#H!WzX!D~#^cU;n+0C#0 z>MxH1Xe*nLWMhrqB?GnZ{cia(M)9s54xS#Ms;Pk73GVF`NP`PM2WIm_<)0LKOsN$bzq^dR zF{GTKOMUWt;Mo=WSA6bhYBvY2V&SMd)kD#N2PlnX);+*&FYmi%%=+9tncw{Y4_J0Q z>idVelt;5@^j17_H^=Qbx9hgZ5b&-@EHfo#()dQp{YN36NP_r=!FlI)Z6@D1uTS)@ zZJX~E%`m)D8x!TTmTlMg1vi-~DP)wqxz~Rpg^U@84$SLEl3}8aE(_sK4l%?ya3wAl zL3#OOu;HljWb9$d$jLkXNlx8K&T?X1%~2h4Qhr;p7u6R#erd*jC@#=E}GzVyP`wWHnv|FL}zL&-h~Sow22;wk!k)ednOL z5HHi2f@qAs$2qe^RWe%ks)qxB^~qVo@KP`QJt#7hLTui&khSDV?bXIOb1HZ1y>bNw zvu!{Oky){yo`!;88wXLiuqc6k7_V?K4pe%5hL}^TL}b7PXS4npYx42nbc+)?VxGn$ z_)6cG|01Z!MC-#0JrP+>DLdOL@2elbw)ylYKe@S7{~r{oISXFl>rt?sK9@W#jQ>6w z$a%*(a;8WbVI=yD9kO3?GcFvr5zmov6h)HZn$`;*8D`Yb{2Mbm3(<=y&al=8*RTebT#*Gg8gF%GKELjkjDQ^J;h^ zE5jR|e~#1km=Iahg_7ZeeCL1(hxK{SL^C`ayH);;ExfStU*ns+)g^*W%|+xf)~K%)d~+J!6w3={PzBAGiuEAY|>fuZBA62 zXXd${3@XrN`0&?Q@AMe7vz!{W15dhHUEr?Tp|c@1PNyvlGfq8AyjOQ+=dVV#7tzzB z?d97R*6Bh8+~6Z~Q!`lgMKiqu7b4cR9paWeDxtd5&2~B&!XJG=|25O!z;FC& z(j{Wgsfj0|gGHaK5IO9X-DS){;G%<0b^Q~Vy}Nry{e$W0lH%wCZNm+CJb;NM2XLWr z(dFgHVArikt;RiFnX^pfSpykD>#X{kpGCRs=jb^e`4L{CMSZ1%uZW^xiiVCvJ@&Vs zJl_kC`{7SMu~;MLQFL1SPzW(#vQ5sPO_?W>&3nz+d&$CQ@$s|fv%Tgn&Zpg$K3lgQ z>+=kD@*^zg^oYz-$f9<+0TMsSq=d8&EFHeuT>#Uegv zPP>;JJX!twIXLMZ2h|<-HQve9`g*SN9OvW)UJCGXg|>|=_&;jbN7m7^rO6bg1_x($ zxkvb6e4ci+w=N8|2&JdPOr9b6-L+#{+)of-Bm#FBG2)d#>iIQH*2C?1d6&Szt&9~> zE-nIeLfN9)osu(xrFOMJ5gTD20a&^HW?k^Y5Mz8&V6E0s8VHVMR0O+8!34`a>A}~% zR-d2aZCifl9HEHLK2r<{fZ^7!Tj@8Xz;67$R%QBa04X3Zj>R%I|L~jBhZk-ygK*t2 zg}4kMy7}shkp8e2fhrG!iPmb#$;M)*%rr2QuoGTQCSbCPB1F*ED=RPV34yi(dzHeQ zkdx@~-B#u7$^Uq{a0~{cXN-gxW+E~&Tqu|KGqQw5+cGw*;t43Hvm73_ayAD4Y6Jbe zTO!A+MW!$ecJw<57Q^Yqcwj$A&JX{{U)uclf9r26ZN%A0T0 zuj9>s`XBxE&E1?0_VZrJ3;$k9vxpVEF24u-P(XKv$ta2VwenU195F8;n6R^;--F>c&?W86P|8v>Pw38 zzx?grp4I7Ba)?=pXE2^M8MG~dLEvOHD?fyl=>z||@Rghw;P>4)&i%@P?_!34m2g`c z>S{D3blQn6%TX7UV@OV3nkFPp9G3|n?K85KVaze?yL?k#x7h>TlE(`QwGV{5jz0xUR93by+mph zA;zbyf4#H4v30h0r-^}*MS<}-Wh#arrUAFMms=l@W7Umbtd6d08e3ntw-_+vAOf?$ zDYD9u@zbV`bnWuXkU6_3BHulpF0Y!Of8V|~ci|e&^~B}3KVzx_aQbfmcz?p1vb%p~ zZmKK;WEE-dQOC9+u)=|RyKBj})#mOVx=?0%)i~@*PwUmCx)o#C_`!T-{e87eRBoPC z#&`@bMnjy^vt1y*CQtNkuW)C%@p)@M-#9ObrqMz!SxsNQ(x z_MP{J<~c<0)Hq91U?n3bKc7A=Ur_utj%>pj^1yoX`0CWfc$BR7M(=WfT)lcF_!tX0 zMyvDS)XO6$;Kz0(Nq*t$QjSy?98c(Y{ooY#n@q*`=KaGQ$QN@GaW36ytI@q29*pL5 z9U051|EN=oB%b7$Wk|#Ad&xRw82S1&#!YbJX>$X<$GajqTnu$&e7TY>FWI*igG7k5eXB9y^ipoBRsms&`jkicAFrz<ScxHRR}qr(;-F13|w|L&>HSKhiFUBx?%m-+B!TgBx;5@Av0!Bev3=B_(Ap2!Dd zSTnOF%!Q&(_srQh(-CabCMU@PbI;Q;tm{kgpEwa8C$!zOjr~;oV7$yzjL(sM zi2$lvRILM37Z{%=3*_&ByOHf}1#v0yr2c?;fY!HYaw1uR9ZuHbN`2~mkJnrJxaR9| z%8)(PMxKmwrs|$HmSp^L*u+0Q8(F%o!x+&uz$#x3*+SNgO!TQSWtXBj^|OKIc6>&+ zBA4Wp8QE6}k-Eo{AxH1vBWvsN)TMs16Fs3-i`r}(&TI=rzAqaij3Jl)V_T2B?vqJj z?&<6N^)X{Uw%Y*Zjmr0(egIH&vUzfxJAF>{MX-?{gY(LRT@>`fHJqwtwO=Lc|31c{ z5lt9-xItILLkd^-dQ0&w-0p2}njg`Jxyn8ECBB};r|xl(8RuDin3s$r{k;VVFL#-< z@Ugt66J;IE1=PSlAlIi^*r*(*ythQ9$uBcWf5YjM$^aA{@A|KcVJ4iozXe#C-d}E- zZ9m?B6b)B+)X6oUlaHJ?Q$D>3Z_*9mR7GKI`olRpGAQWiRpxlV7_9?Z|&zK zpCik&n@4|R4}_D*YsNSVAHFig~F3y#+Bg83%j&mkGz7T+`B zLkvB>viK=Knr&PDIQc~wg{WDymI~oaGR2m4^4K;pGUjypKe-{Y+d%*l;(yexhUKGq zA?n6xnD&DZV&v#}gePKEX2#+rlk?pPB$E<$re*C}sT#XPIffOSV zGJPh9CdUCtMp%Zo)zJ^M0*CsCf#)(|bPsciLa>+dNh?z`dB=brK5Ih7|EoN~uzh!X z%Lk$G)t9zD?6nhYyU*QweveQNw)x(J9VqjA?lUmgL~r7v@Kd5kNvb?y%wTYF$a&9L znlQ#mL`c5?KSA%>IrMz@jz1FR`8a#D*BYb;-Qo{wr_2wYgWQzp!N| zvA9eoms2v2&#vUm6eg>l`hKnmAI?rgD|y)IlT>BWn2Didi4|@K6-}mP? zKlcxRcJsy0eP_x@idA@*+dvXhXxqOSY|$2l)Lwl)RY}!=dGp;8m`Y0NN7tFBdDa

X$tBk$f5*(hXufO+|FSl*$OPlZi?(gn- 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 z%0A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

YJk9xP!^NLR9T(w$jFsl$3eqy|G(m-Y8_txJ?PH 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�ndsmmWt}n0iG{yzcp!s=5yT;c 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

Ec73ysp5~R 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

a`NN>_b~$v#IveEX=01qVcA&F($ydk+d4 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 z%0AEtI`>-&wSRfqGE24C!Vtiy1!9RKC73i&Yd5s%_?G)~T&#?f+;PyRO!~6Eao4*v zflx;`XmiXi?$71;A?DIf<;1q}5;{B#IE=^o{RDgt9J0lpSYCVSt7Ui);@j1C7!rDL zyY#mXDt(2!EFC0|q(6GP(RcvQ>T@QgH=YmW{lB}8>4K5ub8~FI-}(8@-~HaabAL$Ds2m24jPuXRwnlAyPiB zcse728Uz`mI*arMWoV&}LvbPRYB%UZAP2!7&7Yc*7*Z1;M6^7BC(6j>Yz9EdDuA+T z`l?8Ef;#00yz&x>VzOsDn zfBE6d$7cM$tK*>d{==Vn^YXR-*RL∾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

y;= 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 literal 0 HcmV?d00001 diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 2b06da1d..975eb4a7 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -33,6 +33,8 @@ class BotText: 3. You have the option to buy more tokens. Type /buy for more information. 4. The source code for this bot will always remain open and can be found here: https://github.com/tgbot-collection/ytdlbot + +5. Need help with deployment or exclusive features? I offer paid service - contact me at @BennyThink """ about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 38b46e31..0013f00b 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -21,6 +21,7 @@ import ffmpeg import ffpb import filetype +import requests import yt_dlp as ytdl from tqdm import tqdm @@ -153,7 +154,7 @@ def can_convert_mp4(video_path, uid): return True -def ytdl_download(url, tempdir, bm, **kwargs) -> dict: +def ytdl_download(url, tempdir: "str", bm, **kwargs) -> dict: payment = Payment() chat_id = bm.chat.id hijack = kwargs.get("hijack") @@ -180,7 +181,8 @@ def ytdl_download(url, tempdir, bm, **kwargs) -> dict: None, ] adjust_formats(chat_id, url, formats, hijack) - add_instagram_cookies(url, ydl_opts) + if download_instagram(url, tempdir): + return {"status": True, "error": "", "filepath": list(pathlib.Path(tempdir).glob("*"))} address = ["::", "0.0.0.0"] if IPv6 else [None] for format_ in formats: @@ -252,9 +254,20 @@ def convert_audio_format(resp: "dict", bm): resp["filepath"][index] = new_path -def add_instagram_cookies(url: "str", opt: "dict"): +def download_instagram(url: "str", tempdir: "str"): if url.startswith("https://www.instagram.com"): - opt["cookiefile"] = pathlib.Path(__file__).parent.joinpath("instagram.com_cookies.txt").as_posix() + api = f"https://ssmstore.store/rami/index.php?url={url}" + res = requests.get(api).json() + if isinstance(res, dict): + downloadable = {i["url"]: i["ext"] for i in res["url"]} + else: + downloadable = {i["url"]: i["ext"] for item in res for i in item["url"]} + + for link, ext in downloadable.items(): + save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") + with open(save_path, "wb") as f: + f.write(requests.get(link, stream=True).content) + return True def split_large_video(response: "dict"): @@ -270,3 +283,8 @@ def split_large_video(response: "dict"): if split and original_video: response["filepath"] = [i.as_posix() for i in pathlib.Path(original_video).parent.glob("*")] + + +if __name__ == "__main__": + a = download_instagram("https://www.instagram.com/p/CrEAz-AI99Y/", "tmp") + print(a) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index f3f65565..0951de27 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -22,6 +22,7 @@ from hashlib import md5 from urllib.parse import quote_plus +import filetype import psutil import pyrogram.errors import requests @@ -42,7 +43,6 @@ ENABLE_VIP, OWNER, RATE_LIMIT, - TG_MAX_SIZE, WORKERS, ) from constant import BotText @@ -127,7 +127,7 @@ def forward_video(client, bot_msg, url): try: res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid) - obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation + obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo caption, _ = gen_cap(bot_msg, url, obj) res_msg.edit_text(caption, reply_markup=gen_video_markup()) @@ -265,27 +265,19 @@ def ytdl_normal_download(bot_msg, client, url): logging.info("Download complete.") if result["status"]: client.send_chat_action(chat_id, "upload_document") - video_paths = result["filepath"] + video_paths: "list" = result["filepath"] bot_msg.edit_text("Download complete. Sending now...") - for video_path in video_paths: - # normally there's only one video in that path... - st_size = os.stat(video_path).st_size - if st_size > TG_MAX_SIZE: - bot_msg.edit_text(f"Your video({sizeof_fmt(st_size)}) is too large for Telegram.") - # client.send_chat_action(chat_id, 'upload_document') - # client.send_message(chat_id, upload_transfer_sh(bot_msg, video_paths)) - continue - try: - upload_processor(client, bot_msg, url, video_path) - except pyrogram.errors.Flood as e: - logging.critical("FloodWait from Telegram: %s", e) - client.send_message( - chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", - ) - flood_owner_message(client, e) - time.sleep(e.x) - upload_processor(client, bot_msg, url, video_path) + try: + upload_processor(client, bot_msg, url, video_paths) + except pyrogram.errors.Flood as e: + logging.critical("FloodWait from Telegram: %s", e) + client.send_message( + chat_id, + f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", + ) + flood_owner_message(client, e) + time.sleep(e.x) + upload_processor(client, bot_msg, url, video_paths) bot_msg.edit_text("Download success!โœ…") else: @@ -296,12 +288,43 @@ def ytdl_normal_download(bot_msg, client, url): temp_dir.cleanup() -def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.Path]"): +def generate_input_media(file_paths: "list", cap: "str") -> list: + input_media = [] + for path in file_paths: + mime = filetype.guess_mime(path) + if "video" in mime: + input_media.append(pyrogram.types.InputMediaVideo(media=path)) + elif "image" in mime: + input_media.append(pyrogram.types.InputMediaPhoto(media=path)) + elif "audio" in mime: + input_media.append(pyrogram.types.InputMediaAudio(media=path)) + else: + input_media.append(pyrogram.types.InputMediaDocument(media=path)) + + input_media[0].caption = cap + return input_media + + +def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, list]"): # raise pyrogram.errors.exceptions.FloodWait(13) + # if is str, it's a file id; else it's a list of paths payment = Payment() chat_id = bot_msg.chat.id markup = gen_video_markup() - cap, meta = gen_cap(bot_msg, url, vp_or_fid) + if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1: + # just generate the first for simplicity, send as media group(2-20) + cap, meta = gen_cap(bot_msg, url, vp_or_fid[0]) + res_msg = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) + # TODO no cache for now + return res_msg[0] + elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1: + # normal download, just contains one file in video_paths + vp_or_fid = vp_or_fid[0] + cap, meta = gen_cap(bot_msg, url, vp_or_fid) + else: + # just a file id as string + cap, meta = gen_cap(bot_msg, url, vp_or_fid) + settings = payment.get_user_settings(str(chat_id)) if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): chat_id = ARCHIVE_ID @@ -364,9 +387,19 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, pathlib.P reply_markup=markup, **meta, ) + except FileNotFoundError: + # this is likely a photo + logging.info("Retry to send as photo") + res_msg = client.send_photo( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + ) unique = get_unique_clink(url, bot_msg.chat.id) - obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation + obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo redis.add_send_cache(unique, getattr(obj, "file_id", None)) redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): From 3afb82d319bbb309b806c43a074e20fe8a85ae3d Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 17 Apr 2023 19:03:45 +0200 Subject: [PATCH 085/297] fix subscribe bug, add many type annotations --- ytdlbot/channel.py | 22 +++++++-------- ytdlbot/client_init.py | 2 +- ytdlbot/config.py | 10 +++---- ytdlbot/constant.py | 4 +-- ytdlbot/database.py | 14 +++++----- ytdlbot/downloader.py | 14 +++++----- ytdlbot/limit.py | 24 ++++++++--------- ytdlbot/tasks.py | 51 ++++++++++++++++++----------------- ytdlbot/utils.py | 8 +++--- ytdlbot/ytdl_bot.py | 61 +++++++++++++++++++++++------------------- 10 files changed, 109 insertions(+), 101 deletions(-) diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 56e79a51..3d36c700 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -13,7 +13,7 @@ class Channel(Payment): - def subscribe_channel(self, user_id: "int", share_link: "str"): + def subscribe_channel(self, user_id: int, share_link: str) -> str: if not re.findall(r"youtube\.com|youtu\.be", share_link): raise ValueError("Is this a valid YouTube Channel link?") if ENABLE_VIP: @@ -41,7 +41,7 @@ def subscribe_channel(self, user_id: "int", share_link: "str"): logging.info("User %s subscribed channel %s", user_id, data["title"]) return "Subscribed to {}".format(data["title"]) - def unsubscribe_channel(self, user_id: "int", channel_id: "str"): + def unsubscribe_channel(self, user_id: int, channel_id: str) -> int: affected_rows = self.cur.execute( "DELETE FROM subscribe WHERE user_id=%s AND channel_id=%s", (user_id, channel_id) ) @@ -50,7 +50,7 @@ def unsubscribe_channel(self, user_id: "int", channel_id: "str"): return affected_rows @staticmethod - def extract_canonical_link(url): + def extract_canonical_link(url: str) -> str: # canonic link works for many websites. It will strip out unnecessary stuff props = ["canonical", "alternate", "shortlinkUrl"] headers = { @@ -77,7 +77,7 @@ def extract_canonical_link(url): return url - def get_channel_info(self, url: "str"): + def get_channel_info(self, url: str) -> dict: api_key = os.getenv("GOOGLE_API_KEY") canonical_link = self.extract_canonical_link(url) try: @@ -104,7 +104,7 @@ def get_channel_info(self, url: "str"): } @staticmethod - def get_latest_video(playlist_id: "str"): + def get_latest_video(playlist_id: str) -> str: api_key = os.getenv("GOOGLE_API_KEY") video_api = ( f"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=1&" @@ -115,7 +115,7 @@ def get_latest_video(playlist_id: "str"): logging.info(f"Latest video %s from %s", video_id, data["items"][0]["snippet"]["channelTitle"]) return f"https://www.youtube.com/watch?v={video_id}" - def has_newer_update(self, channel_id: "str"): + def has_newer_update(self, channel_id: str) -> str: self.cur.execute("SELECT playlist,latest_video FROM channel WHERE channel_id=%s", (channel_id,)) data = self.cur.fetchone() playlist_id = data[0] @@ -127,7 +127,7 @@ def has_newer_update(self, channel_id: "str"): self.con.commit() return newest_video - def get_user_subscription(self, user_id: "int"): + def get_user_subscription(self, user_id: int) -> str: self.cur.execute( """ select title, link, channel.channel_id from channel, subscribe @@ -141,7 +141,7 @@ def get_user_subscription(self, user_id: "int"): text += "[{}]({}) `{}\n`".format(*item) return text - def group_subscriber(self): + def group_subscriber(self) -> dict: # {"channel_id": [user_id, user_id, ...]} self.cur.execute("select * from subscribe where is_valid=1") data = self.cur.fetchall() @@ -151,11 +151,11 @@ def group_subscriber(self): logging.info("Checking periodic subscriber...") return group - def deactivate_user_subscription(self, user_id: "int"): + def deactivate_user_subscription(self, user_id: int): self.cur.execute("UPDATE subscribe set is_valid=0 WHERE user_id=%s", (user_id,)) self.con.commit() - def sub_count(self): + def sub_count(self) -> str: sql = """ select user_id, channel.title, channel.link from subscribe, channel where subscribe.channel_id = channel.channel_id @@ -167,7 +167,7 @@ def sub_count(self): text += "{} ==> [{}]({})\n".format(*item) return text - def del_cache(self, user_link: "str"): + def del_cache(self, user_link: str) -> int: unique = self.extract_canonical_link(user_link) caches = self.r.hgetall("cache") count = 0 diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index a3129326..2cbfc9ea 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -12,7 +12,7 @@ from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 -def create_app(session, workers=PYRO_WORKERS): +def create_app(session: str, workers: int = PYRO_WORKERS) -> Client: _app = Client( session, APP_ID, diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 10b57003..2a91b132 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -10,9 +10,9 @@ import os # general settings -WORKERS: "int" = int(os.getenv("WORKERS", 100)) -PYRO_WORKERS: "int" = int(os.getenv("PYRO_WORKERS", min(32, os.cpu_count() + 4))) -APP_ID: "int" = int(os.getenv("APP_ID", 198214)) +WORKERS: int = int(os.getenv("WORKERS", 100)) +PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, os.cpu_count() + 4))) +APP_ID: int = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") @@ -32,10 +32,10 @@ OWNER = os.getenv("OWNER", "BennyThink") # limitation settings -AUTHORIZED_USER: "str" = os.getenv("AUTHORIZED_USER", "") +AUTHORIZED_USER: str = os.getenv("AUTHORIZED_USER", "") # membership requires: the format could be username(without @ sign)/chat_id of channel or group. # You need to add the bot to this group/channel as admin -REQUIRED_MEMBERSHIP: "str" = os.getenv("REQUIRED_MEMBERSHIP", "") +REQUIRED_MEMBERSHIP: str = os.getenv("REQUIRED_MEMBERSHIP", "") # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 975eb4a7..3aa7e6bb 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -87,7 +87,7 @@ class BotText: custom_text = os.getenv("CUSTOM_TEXT", "") @staticmethod - def get_receive_link_text(): + def get_receive_link_text() -> str: reserved = get_func_queue("reserved") if ENABLE_CELERY and reserved: text = f"Too many tasks. Your tasks was added to the reserved queue {reserved}." @@ -97,7 +97,7 @@ def get_receive_link_text(): return text @staticmethod - def ping_worker(): + def ping_worker() -> str: from tasks import app as celery_app workers = InfluxDB().extract_dashboard_data() diff --git a/ytdlbot/database.py b/ytdlbot/database.py index e9067311..3e2fb8bd 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -102,7 +102,7 @@ def __init__(self): def __del__(self): self.r.close() - def update_metrics(self, metrics): + def update_metrics(self, metrics: str): logging.info(f"Setting metrics: {metrics}") all_ = f"all_{metrics}" today = f"today_{metrics}" @@ -110,7 +110,7 @@ def update_metrics(self, metrics): self.r.hincrby("metrics", today) @staticmethod - def generate_table(header, all_data: "list"): + def generate_table(header, all_data: list): table = BeautifulTable() for data in all_data: table.rows.append(data) @@ -186,10 +186,10 @@ def generate_file(self): file.name = f"{date}.txt" return file - def add_send_cache(self, unique, file_id): + def add_send_cache(self, unique: str, file_id: str): self.r.hset("cache", unique, file_id) - def get_send_cache(self, unique) -> "str": + def get_send_cache(self, unique) -> str: return self.r.hget("cache", unique) def del_send_cache(self, unique): @@ -266,7 +266,7 @@ def init_db(self): def __del__(self): self.con.close() - def get_user_settings(self, user_id: "str") -> "tuple": + def get_user_settings(self, user_id: int) -> tuple: cur = self.con.cursor() cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) data = cur.fetchone() @@ -274,7 +274,7 @@ def get_user_settings(self, user_id: "str") -> "tuple": return 100, "high", "video", "Celery" return data - def set_user_settings(self, user_id: int, field: "str", value: "str"): + def set_user_settings(self, user_id: int, field: str, value: str): cur = self.con.cursor() cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) data = cur.fetchone() @@ -301,7 +301,7 @@ def __del__(self): self.client.close() @staticmethod - def get_worker_data(): + def get_worker_data() -> dict: username = os.getenv("FLOWER_USERNAME", "benny") password = os.getenv("FLOWER_PASSWORD", "123456abc") token = base64.b64encode(f"{username}:{password}".encode()).decode() diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 0013f00b..e88a7f41 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -33,7 +33,7 @@ apply_log_formatter() -def edit_text(bot_msg, text): +def edit_text(bot_msg, text: str): key = f"{bot_msg.chat.id}-{bot_msg.message_id}" # if the key exists, we shouldn't send edit message if not r.exists(key): @@ -142,7 +142,7 @@ def update(self, n=1): edit_text(self.bot_msg, t) -def run_ffmpeg(cmd_list, bm): +def run_ffmpeg(cmd_list: list, bm): cmd_list = cmd_list.copy()[1:] ProgressBar.b = bm ffpb.main(cmd_list, tqdm=ProgressBar) @@ -154,7 +154,7 @@ def can_convert_mp4(video_path, uid): return True -def ytdl_download(url, tempdir: "str", bm, **kwargs) -> dict: +def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> dict: payment = Payment() chat_id = bm.chat.id hijack = kwargs.get("hijack") @@ -211,7 +211,7 @@ def ytdl_download(url, tempdir: "str", bm, **kwargs) -> dict: return response # convert format if necessary - settings = payment.get_user_settings(str(chat_id)) + settings = payment.get_user_settings(chat_id) if settings[2] == "video" or isinstance(settings[2], MagicMock): # only convert if send type is video convert_to_mp4(response, bm) @@ -222,7 +222,7 @@ def ytdl_download(url, tempdir: "str", bm, **kwargs) -> dict: return response -def convert_audio_format(resp: "dict", bm): +def convert_audio_format(resp: dict, bm): # 1. file is audio, default format # 2. file is video, default format # 3. non default format @@ -254,7 +254,7 @@ def convert_audio_format(resp: "dict", bm): resp["filepath"][index] = new_path -def download_instagram(url: "str", tempdir: "str"): +def download_instagram(url: str, tempdir: str): if url.startswith("https://www.instagram.com"): api = f"https://ssmstore.store/rami/index.php?url={url}" res = requests.get(api).json() @@ -270,7 +270,7 @@ def download_instagram(url: "str", tempdir: "str"): return True -def split_large_video(response: "dict"): +def split_large_video(response: dict): original_video = None split = False for original_video in response.get("filepath", []): diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 91b342a3..9f651f5c 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -41,14 +41,14 @@ def _get_data(self, url): if next_page: self._get_data(next_page) - def _get_bmac_status(self, email: "str") -> "dict": + def _get_bmac_status(self, email: str) -> dict: self._get_data(self._url) for user in self._data: if user["payer_email"] == email or user["support_email"] == email: return user return {} - def get_user_payment(self, email: "str") -> ("int", "float", "str"): + def get_user_payment(self, email: str) -> (int, "float", str): order = self._get_bmac_status(email) price = float(order.get("support_coffee_price", 0)) cups = float(order.get("support_coffees", 1)) @@ -78,7 +78,7 @@ def _generate_signature(self): return data - def _get_afdian_status(self, trade_no: "str") -> "dict": + def _get_afdian_status(self, trade_no: str) -> dict: req_data = self._generate_signature() data = requests.post(self._url, json=req_data).json() # latest 50 @@ -88,7 +88,7 @@ def _get_afdian_status(self, trade_no: "str") -> "dict": return {} - def get_user_payment(self, trade_no: "str") -> ("int", "float", "str"): + def get_user_payment(self, trade_no: str) -> (int, float, str): order = self._get_afdian_status(trade_no) amount = float(order.get("show_amount", 0)) # convert to USD @@ -96,17 +96,17 @@ def get_user_payment(self, trade_no: "str") -> ("int", "float", "str"): class Payment(Redis, MySQL): - def check_old_user(self, user_id: "int") -> "tuple": + def check_old_user(self, user_id: int) -> tuple: self.cur.execute("SELECT * FROM payment WHERE user_id=%s AND old_user=1", (user_id,)) data = self.cur.fetchone() return data - def get_pay_token(self, user_id: "int") -> int: + def get_pay_token(self, user_id: int) -> int: self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) data = self.cur.fetchall() or [(0,)] return sum([i[0] for i in data if i[0]]) - def get_free_token(self, user_id: "int") -> int: + def get_free_token(self, user_id: int) -> int: if self.r.exists(user_id): return int(self.r.get(user_id)) else: @@ -114,11 +114,11 @@ def get_free_token(self, user_id: "int") -> int: self.r.set(user_id, FREE_DOWNLOAD, ex=EXPIRE) return FREE_DOWNLOAD - def get_token(self, user_id): + def get_token(self, user_id: int): ttl = self.r.ttl(user_id) return self.get_free_token(user_id), self.get_pay_token(user_id), current_time(time.time() + ttl) - def use_free_token(self, user_id: "int"): + def use_free_token(self, user_id: int): if self.r.exists(user_id): self.r.decr(user_id, 1) else: @@ -134,18 +134,18 @@ def use_pay_token(self, user_id: int): self.cur.execute("UPDATE payment SET token=token-1 WHERE payment_id=%s", (payment_id,)) self.con.commit() - def use_token(self, user_id): + def use_token(self, user_id: int): free = self.get_free_token(user_id) if free > 0: self.use_free_token(user_id) else: self.use_pay_token(user_id) - def add_pay_user(self, pay_data: "list"): + def add_pay_user(self, pay_data: list): self.cur.execute("INSERT INTO payment VALUES (%s,%s,%s,%s,%s)", pay_data) self.con.commit() - def verify_payment(self, user_id: "int", unique: "str") -> "str": + def verify_payment(self, user_id: int, unique: str) -> str: pay = BuyMeACoffee() if "@" in unique else Afdian() self.cur.execute("SELECT * FROM payment WHERE payment_id=%s ", (unique,)) data = self.cur.fetchone() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 0951de27..846fb0c4 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -29,7 +29,9 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel +from pyrogram import Client from pyrogram import idle +from pyrogram import types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor @@ -83,10 +85,10 @@ def get_messages(chat_id, message_id): @app.task(rate_limit=f"{RATE_LIMIT}/m") -def ytdl_download_task(chat_id, message_id, url): +def ytdl_download_task(chat_id, message_id, url: str): logging.info("YouTube celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - ytdl_normal_download(bot_msg, celery_client, url) + ytdl_normal_download(celery_client, bot_msg, url) logging.info("YouTube celery tasks ended.") @@ -94,13 +96,13 @@ def ytdl_download_task(chat_id, message_id, url): def audio_task(chat_id, message_id): logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) bot_msg = get_messages(chat_id, message_id) - normal_audio(bot_msg, celery_client) + normal_audio(celery_client, bot_msg) logging.info("Audio celery tasks ended.") -def get_unique_clink(original_url, user_id): +def get_unique_clink(original_url: str, user_id: int): payment = Payment() - settings = payment.get_user_settings(str(user_id)) + settings = payment.get_user_settings(user_id) clink = channel.extract_canonical_link(original_url) try: # different user may have different resolution settings @@ -114,11 +116,11 @@ def get_unique_clink(original_url, user_id): def direct_download_task(chat_id, message_id, url): logging.info("Direct download celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - direct_normal_download(bot_msg, celery_client, url) + direct_normal_download(celery_client, bot_msg, url) logging.info("Direct download celery tasks ended.") -def forward_video(client, bot_msg, url): +def forward_video(client, bot_msg, url: str): chat_id = bot_msg.chat.id unique = get_unique_clink(url, chat_id) cached_fid = redis.get_send_cache(unique) @@ -141,26 +143,26 @@ def forward_video(client, bot_msg, url): redis.update_metrics("cache_miss") -def ytdl_download_entrance(client, bot_msg, url): +def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str): payment = Payment() chat_id = bot_msg.chat.id if forward_video(client, bot_msg, url): return - mode = payment.get_user_settings(str(chat_id))[-1] + mode = payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) else: - ytdl_normal_download(bot_msg, client, url) + ytdl_normal_download(client, bot_msg, url) -def direct_download_entrance(bot_msg, client, url): +def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): if ENABLE_CELERY: # TODO disable it for now - direct_normal_download(bot_msg, client, url) + direct_normal_download(client, bot_msg, url) # direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url) else: - direct_normal_download(bot_msg, client, url) + direct_normal_download(client, bot_msg, url) def audio_entrance(bot_msg, client): @@ -171,12 +173,11 @@ def audio_entrance(bot_msg, client): normal_audio(bot_msg, client) -def direct_normal_download(bot_msg, client, url): +def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): chat_id = bot_msg.chat.id headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36" } - vip = Payment() length = 0 req = None @@ -217,11 +218,13 @@ def direct_normal_download(bot_msg, client, url): bot_msg.edit_text("Download success!โœ…") -def normal_audio(bot_msg, client): +def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]): chat_id = bot_msg.chat.id # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) - status_msg = bot_msg.reply_text("Converting to audio...please wait patiently", quote=True) - orig_url: "str" = re.findall(r"https?://.*", bot_msg.caption)[0] + status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text( + "Converting to audio...please wait patiently", quote=True + ) + orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: client.send_chat_action(chat_id, "record_audio") # just try to download the audio using yt-dlp @@ -241,7 +244,7 @@ def get_dl_source(): return "" -def upload_transfer_sh(bm, paths: list) -> "str": +def upload_transfer_sh(bm, paths: list) -> str: d = {p.name: (md5(p.name.encode("utf8")).hexdigest() + p.suffix, p.open("rb")) for p in paths} monitor = MultipartEncoderMonitor(MultipartEncoder(fields=d), lambda x: upload_hook(x.bytes_read, x.len, bm)) headers = {"Content-Type": monitor.content_type} @@ -257,7 +260,7 @@ def flood_owner_message(client, ex): client.send_message(OWNER, f"CRITICAL INFO: {ex}") -def ytdl_normal_download(bot_msg, client, url): +def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): chat_id = bot_msg.chat.id temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-") @@ -265,7 +268,7 @@ def ytdl_normal_download(bot_msg, client, url): logging.info("Download complete.") if result["status"]: client.send_chat_action(chat_id, "upload_document") - video_paths: "list" = result["filepath"] + video_paths: list = result["filepath"] bot_msg.edit_text("Download complete. Sending now...") try: upload_processor(client, bot_msg, url, video_paths) @@ -288,7 +291,7 @@ def ytdl_normal_download(bot_msg, client, url): temp_dir.cleanup() -def generate_input_media(file_paths: "list", cap: "str") -> list: +def generate_input_media(file_paths: list, cap: str) -> list: input_media = [] for path in file_paths: mime = filetype.guess_mime(path) @@ -305,7 +308,7 @@ def generate_input_media(file_paths: "list", cap: "str") -> list: return input_media -def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, list]"): +def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): # raise pyrogram.errors.exceptions.FloodWait(13) # if is str, it's a file id; else it's a list of paths payment = Payment() @@ -325,7 +328,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: "typing.Any[str, list]"): # just a file id as string cap, meta = gen_cap(bot_msg, url, vp_or_fid) - settings = payment.get_user_settings(str(chat_id)) + settings = payment.get_user_settings(chat_id) if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): chat_id = ARCHIVE_ID if settings[2] == "document": diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index b79f97e5..1c481725 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -35,7 +35,7 @@ def apply_log_formatter(): ) -def customize_logger(logger: "list"): +def customize_logger(logger: list): apply_log_formatter() for log in logger: logging.getLogger(log).setLevel(level=logging.INFO) @@ -49,12 +49,12 @@ def sizeof_fmt(num: int, suffix="B"): return "%.1f%s%s" % (num, "Yi", suffix) -def is_youtube(url: "str"): +def is_youtube(url: str): if url.startswith("https://www.youtube.com/") or url.startswith("https://youtu.be/"): return True -def adjust_formats(user_id: "str", url: "str", formats: "list", hijack=None): +def adjust_formats(user_id: int, url: str, formats: list, hijack=None): from database import MySQL # high: best quality 1080P, 2K, 4K, 8K @@ -149,7 +149,7 @@ def tail_log(f, lines=1, _buffer=4098): class Detector: - def __init__(self, logs: "str"): + def __init__(self, logs: str): self.logs = logs @staticmethod diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 76209a4c..5c1ac12a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -63,7 +63,7 @@ def private_use(func): - def wrapper(client: "Client", message: "types.Message"): + def wrapper(client: Client, message: types.Message): chat_id = getattr(message.from_user, "id", None) # message type check @@ -83,7 +83,10 @@ def wrapper(client: "Client", message: "types.Message"): if REQUIRED_MEMBERSHIP: try: - if app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id).status not in [ + member: typing.Union[types.ChatMember, typing.Coroutine] = app.get_chat_member( + REQUIRED_MEMBERSHIP, chat_id + ) + if member.status not in [ "creator", "administrator", "member", @@ -103,7 +106,7 @@ def wrapper(client: "Client", message: "types.Message"): @app.on_message(filters.command(["start"])) -def start_handler(client: "Client", message: "types.Message"): +def start_handler(client: Client, message: types.Message): payment = Payment() from_id = message.from_user.id logging.info("Welcome to youtube-dl bot!") @@ -121,21 +124,21 @@ def start_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["help"])) -def help_handler(client: "Client", message: "types.Message"): +def help_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.help, disable_web_page_preview=True) @app.on_message(filters.command(["about"])) -def about_handler(client: "Client", message: "types.Message"): +def about_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.about) @app.on_message(filters.command(["sub"])) -def subscribe_handler(client: "Client", message: "types.Message"): +def subscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") if message.text == "/sub": @@ -150,7 +153,7 @@ def subscribe_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["unsub"])) -def unsubscribe_handler(client: "Client", message: "types.Message"): +def unsubscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") text = message.text.split(" ") @@ -167,7 +170,7 @@ def unsubscribe_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["patch"])) -def patch_handler(client: "Client", message: "types.Message"): +def patch_handler(client: Client, message: types.Message): username = message.from_user.username chat_id = message.chat.id if username == OWNER: @@ -178,7 +181,7 @@ def patch_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["uncache"])) -def uncache_handler(client: "Client", message: "types.Message"): +def uncache_handler(client: Client, message: types.Message): username = message.from_user.username link = message.text.split()[1] if username == OWNER: @@ -187,14 +190,14 @@ def uncache_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["purge"])) -def purge_handler(client: "Client", message: "types.Message"): +def purge_handler(client: Client, message: types.Message): username = message.from_user.username if username == OWNER: message.reply_text(purge_tasks(), quote=True) @app.on_message(filters.command(["ping"])) -def ping_handler(client: "Client", message: "types.Message"): +def ping_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): @@ -209,7 +212,7 @@ def ping_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["sub_count"])) -def sub_count_handler(client: "Client", message: "types.Message"): +def sub_count_handler(client: Client, message: types.Message): username = message.from_user.username chat_id = message.chat.id if username == OWNER: @@ -220,7 +223,7 @@ def sub_count_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["direct"])) -def direct_handler(client: "Client", message: "types.Message"): +def direct_handler(client: Client, message: types.Message): chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") url = re.sub(r"/direct\s*", "", message.text) @@ -232,14 +235,14 @@ def direct_handler(client: "Client", message: "types.Message"): bot_msg = message.reply_text("Request received.", quote=True) redis.update_metrics("direct_request") - direct_download_entrance(bot_msg, client, url) + direct_download_entrance(client, bot_msg, url) @app.on_message(filters.command(["settings"])) -def settings_handler(client: "Client", message: "types.Message"): +def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, "typing") - data = MySQL().get_user_settings(str(chat_id)) + data = MySQL().get_user_settings(chat_id) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" @@ -268,7 +271,7 @@ def settings_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["buy"])) -def buy_handler(client: "Client", message: "types.Message"): +def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id text = message.text.strip() @@ -297,7 +300,7 @@ def buy_handler(client: "Client", message: "types.Message"): @app.on_message(filters.command(["redeem"])) -def redeem_handler(client: "Client", message: "types.Message"): +def redeem_handler(client: Client, message: types.Message): payment = Payment() chat_id = message.chat.id text = message.text.strip() @@ -306,7 +309,7 @@ def redeem_handler(client: "Client", message: "types.Message"): message.reply_text(msg, quote=True) -def generate_invoice(amount: "int", title: "str", description: "str", payload: "str"): +def generate_invoice(amount: int, title: str, description: str, payload: str): invoice = raw_types.input_media_invoice.InputMediaInvoice( invoice=raw_types.invoice.Invoice( currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)] @@ -323,7 +326,7 @@ def generate_invoice(amount: "int", title: "str", description: "str", payload: " @app.on_message(filters.incoming & filters.text) @private_use -def download_handler(client: "Client", message: "types.Message"): +def download_handler(client: Client, message: types.Message): payment = Payment() chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") @@ -366,7 +369,7 @@ def download_handler(client: "Client", message: "types.Message"): text = BotText.get_receive_link_text() try: # raise pyrogram.errors.exceptions.FloodWait(10) - bot_msg: typing.Union["types.Message", "typing.Any"] = message.reply_text(text, quote=True) + bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) except pyrogram.errors.Flood as e: f = BytesIO() f.write(str(e).encode()) @@ -385,7 +388,7 @@ def download_handler(client: "Client", message: "types.Message"): @app.on_callback_query(filters.regex(r"document|video|audio")) -def send_method_callback(client: "Client", callback_query: types.CallbackQuery): +def send_method_callback(client: Client, callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) @@ -394,7 +397,7 @@ def send_method_callback(client: "Client", callback_query: types.CallbackQuery): @app.on_callback_query(filters.regex(r"high|medium|low")) -def download_resolution_callback(client: "Client", callback_query: types.CallbackQuery): +def download_resolution_callback(client: Client, callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id data = callback_query.data logging.info("Setting %s file type to %s", chat_id, data) @@ -403,7 +406,7 @@ def download_resolution_callback(client: "Client", callback_query: types.Callbac @app.on_callback_query(filters.regex(r"convert")) -def audio_callback(client: "Client", callback_query: types.CallbackQuery): +def audio_callback(client: Client, callback_query: types.CallbackQuery): if not ENABLE_FFMPEG: callback_query.answer("Audio conversion is disabled now.") callback_query.message.reply_text("Audio conversion is disabled now.") @@ -416,7 +419,7 @@ def audio_callback(client: "Client", callback_query: types.CallbackQuery): @app.on_callback_query(filters.regex(r"Local|Celery")) -def owner_local_callback(client: "Client", callback_query: types.CallbackQuery): +def owner_local_callback(client: Client, callback_query: types.CallbackQuery): chat_id = callback_query.message.chat.id MySQL().set_user_settings(chat_id, "mode", callback_query.data) callback_query.answer(f"Download mode was changed to {callback_query.data}") @@ -430,8 +433,10 @@ def periodic_sub_check(): logging.info(f"periodic update:{video_url} - {uids}") for uid in uids: try: - bot_msg = app.send_message(uid, f"{video_url} is downloading...", disable_web_page_preview=True) - ytdl_download_entrance(bot_msg, app, video_url) + bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( + uid, f"{video_url} is downloading...", disable_web_page_preview=True + ) + ytdl_download_entrance(app, bot_msg, video_url) except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) @@ -442,7 +447,7 @@ def periodic_sub_check(): @app.on_raw_update() -def raw_update(client: "Client", update, users, chats): +def raw_update(client: Client, update, users, chats): payment = Payment() action = getattr(getattr(update, "message", None), "action", None) if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": From 783d1af3bade98fffa4625e08ab78ca2621a578f Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 17 Apr 2023 20:07:56 +0200 Subject: [PATCH 086/297] telegram send webp as sticker, need to convert it to jpg --- ytdlbot/downloader.py | 14 ++++++++++---- ytdlbot/tasks.py | 4 ++-- ytdlbot/ytdl_bot.py | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index e88a7f41..d33b9edd 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -122,7 +122,7 @@ def convert_to_mp4(resp: dict, bot_msg): edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") new_file_path = path.with_suffix(".mp4") logging.info("Detected %s, converting to mp4...", mime) - run_ffmpeg(["ffmpeg", "-y", "-i", path, new_file_path], bot_msg) + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_file_path], bot_msg) index = resp["filepath"].index(path) resp["filepath"][index] = new_file_path @@ -142,7 +142,7 @@ def update(self, n=1): edit_text(self.bot_msg, t) -def run_ffmpeg(cmd_list: list, bm): +def run_ffmpeg_progressbar(cmd_list: list, bm): cmd_list = cmd_list.copy()[1:] ProgressBar.b = bm ffpb.main(cmd_list, tqdm=ProgressBar) @@ -241,14 +241,14 @@ def convert_audio_format(resp: dict, bm): break ext = audio_stream["codec_name"] new_path = path.with_suffix(f".{ext}") - run_ffmpeg(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) path.unlink() index = resp["filepath"].index(path) resp["filepath"][index] = new_path else: logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) new_path = path.with_suffix(f".{AUDIO_FORMAT}") - run_ffmpeg(["ffmpeg", "-y", "-i", path, new_path], bm) + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_path], bm) path.unlink() index = resp["filepath"].index(path) resp["filepath"][index] = new_path @@ -267,6 +267,12 @@ def download_instagram(url: str, tempdir: str): save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") with open(save_path, "wb") as f: f.write(requests.get(link, stream=True).content) + # telegram send webp as sticker, so we'll convert it to png + for path in pathlib.Path(tempdir).glob("*.webp"): + logging.info("Converting %s to png", path) + new_path = path.with_suffix(".jpg") + ffmpeg.input(path).output(new_path.as_posix()).run() + path.unlink() return True diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 846fb0c4..fe0f9dc5 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -165,12 +165,12 @@ def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message direct_normal_download(client, bot_msg, url) -def audio_entrance(bot_msg, client): +def audio_entrance(client, bot_msg): if ENABLE_CELERY: async_task(audio_task, bot_msg.chat.id, bot_msg.message_id) # audio_task.delay(bot_msg.chat.id, bot_msg.message_id) else: - normal_audio(bot_msg, client) + normal_audio(client, bot_msg) def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 5c1ac12a..d6ad58ed 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -415,7 +415,7 @@ def audio_callback(client: Client, callback_query: types.CallbackQuery): callback_query.answer(f"Converting to audio...please wait patiently") redis.update_metrics("audio_request") vmsg = callback_query.message - audio_entrance(vmsg, client) + audio_entrance(client, vmsg) @app.on_callback_query(filters.regex(r"Local|Celery")) From baaf3bfea8a4beb7c01d09c841772bb15cc6e38b Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 18 Apr 2023 19:09:44 +0200 Subject: [PATCH 087/297] better error handling --- README.md | 12 ++-- ytdlbot/config.py | 2 +- ytdlbot/downloader.py | 132 +++++++++++++++++++----------------------- ytdlbot/tasks.py | 88 +++++++++++++--------------- 4 files changed, 107 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 35e4192b..841a24a4 100644 --- a/README.md +++ b/README.md @@ -263,16 +263,16 @@ https://www.youtube.com/watch?v=BaW_jenozKc https://www.youtube.com/playlist?list=PL1Hdq7xjQCJxQnGc05gS4wzHWccvEJy0w -## Test m3u8 - -https://dmesg.app/m3u8/prog_index.m3u8 - -## test twitter +## Test twitter https://twitter.com/nitori_sayaka/status/1526199729864200192 https://twitter.com/BennyThinks/status/1475836588542341124 -## test instagram +## Test instagram + +https://www.instagram.com/p/ClBSqo3PkJw/ +https://www.instagram.com/p/CaiAHoWDnrM/ +https://www.instagram.com/p/CZtUDyyv1u1/ # Donation diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 2a91b132..766d1ff0 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -18,7 +18,7 @@ REDIS = os.getenv("REDIS", "redis") -TG_MAX_SIZE = 2 * 1024 * 1024 * 1024 * 0.99 +TG_MAX_SIZE = 2000 * 1024 * 1024 # TG_MAX_SIZE = 10 * 1024 * 1024 EXPIRE = 24 * 3600 diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index d33b9edd..c66ee057 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -14,6 +14,7 @@ import re import subprocess import time +import traceback from io import StringIO from unittest.mock import MagicMock @@ -105,28 +106,23 @@ def upload_hook(current, total, bot_msg): edit_text(bot_msg, text) -def convert_to_mp4(resp: dict, bot_msg): +def convert_to_mp4(video_paths: list, bot_msg): default_type = ["video/x-flv", "video/webm"] - if resp["status"]: - # all_converted = [] - for path in resp["filepath"]: - # if we can't guess file type, we assume it's video/mp4 - mime = getattr(filetype.guess(path), "mime", "video/mp4") - if mime in default_type: - if not can_convert_mp4(path, bot_msg.chat.id): - logging.warning("Conversion abort for %s", bot_msg.chat.id) - bot_msg._client.send_message( - bot_msg.chat.id, "Can't convert your video to streaming format. ffmpeg has been disabled." - ) - break - edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") - new_file_path = path.with_suffix(".mp4") - logging.info("Detected %s, converting to mp4...", mime) - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_file_path], bot_msg) - index = resp["filepath"].index(path) - resp["filepath"][index] = new_file_path - - return resp + # all_converted = [] + for path in video_paths: + # if we can't guess file type, we assume it's video/mp4 + mime = getattr(filetype.guess(path), "mime", "video/mp4") + if mime in default_type: + if not can_convert_mp4(path, bot_msg.chat.id): + logging.warning("Conversion abort for %s", bot_msg.chat.id) + bot_msg._client.send_message(bot_msg.chat.id, "Can't convert your video. ffmpeg has been disabled.") + break + edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") + new_file_path = path.with_suffix(".mp4") + logging.info("Detected %s, converting to mp4...", mime) + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_file_path], bot_msg) + index = video_paths.index(path) + video_paths[index] = new_file_path class ProgressBar(tqdm): @@ -154,11 +150,10 @@ def can_convert_mp4(video_path, uid): return True -def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> dict: +def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: payment = Payment() chat_id = bm.chat.id hijack = kwargs.get("hijack") - response = {"status": True, "error": "", "filepath": []} output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { "progress_hooks": [lambda d: download_hook(d, bm)], @@ -182,9 +177,11 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> dict: ] adjust_formats(chat_id, url, formats, hijack) if download_instagram(url, tempdir): - return {"status": True, "error": "", "filepath": list(pathlib.Path(tempdir).glob("*"))} + return list(pathlib.Path(tempdir).glob("*")) address = ["::", "0.0.0.0"] if IPv6 else [None] + error = None + video_paths = None for format_ in formats: ydl_opts["format"] = format_ for addr in address: @@ -194,64 +191,57 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> dict: logging.info("Downloading for %s with format %s", url, format_) with ytdl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) - response["status"] = True - response["error"] = "" - response["filepath"] = list(pathlib.Path(tempdir).glob("*")) + video_paths = list(pathlib.Path(tempdir).glob("*")) break - except Exception as e: - logging.error("Download failed for %s ", url) - response["status"] = False - response["error"] = str(e) - - if response["status"]: + except Exception: + error = traceback.format_exc() + logging.error("Download failed for %s - %s, try another way", format_, url) + if error is None: break - logging.info("%s - %s", url, response) - if response["status"] is False: - return response + if not video_paths: + raise Exception(error) # convert format if necessary settings = payment.get_user_settings(chat_id) if settings[2] == "video" or isinstance(settings[2], MagicMock): # only convert if send type is video - convert_to_mp4(response, bm) + convert_to_mp4(video_paths, bm) if settings[2] == "audio" or hijack == "bestaudio[ext=m4a]": - convert_audio_format(response, bm) - # disable it for now - # split_large_video(response) - return response + convert_audio_format(video_paths, bm) + # split_large_video(video_paths) + return video_paths -def convert_audio_format(resp: dict, bm): +def convert_audio_format(video_paths: list, bm): # 1. file is audio, default format # 2. file is video, default format # 3. non default format - if resp["status"]: - path: "pathlib.Path" - for path in resp["filepath"]: - streams = ffmpeg.probe(path)["streams"] - if AUDIO_FORMAT is None and len(streams) == 1 and streams[0]["codec_type"] == "audio": - logging.info("%s is audio, default format, no need to convert", path) - elif AUDIO_FORMAT is None and len(streams) >= 2: - logging.info("%s is video, default format, need to extract audio", path) - audio_stream = {"codec_name": "m4a"} - for stream in streams: - if stream["codec_type"] == "audio": - audio_stream = stream - break - ext = audio_stream["codec_name"] - new_path = path.with_suffix(f".{ext}") - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) - path.unlink() - index = resp["filepath"].index(path) - resp["filepath"][index] = new_path - else: - logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) - new_path = path.with_suffix(f".{AUDIO_FORMAT}") - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_path], bm) - path.unlink() - index = resp["filepath"].index(path) - resp["filepath"][index] = new_path + + for path in video_paths: + streams = ffmpeg.probe(path)["streams"] + if AUDIO_FORMAT is None and len(streams) == 1 and streams[0]["codec_type"] == "audio": + logging.info("%s is audio, default format, no need to convert", path) + elif AUDIO_FORMAT is None and len(streams) >= 2: + logging.info("%s is video, default format, need to extract audio", path) + audio_stream = {"codec_name": "m4a"} + for stream in streams: + if stream["codec_type"] == "audio": + audio_stream = stream + break + ext = audio_stream["codec_name"] + new_path = path.with_suffix(f".{ext}") + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) + path.unlink() + index = video_paths.index(path) + video_paths[index] = new_path + else: + logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) + new_path = path.with_suffix(f".{AUDIO_FORMAT}") + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_path], bm) + path.unlink() + index = video_paths.index(path) + video_paths[index] = new_path def download_instagram(url: str, tempdir: str): @@ -276,10 +266,10 @@ def download_instagram(url: str, tempdir: str): return True -def split_large_video(response: dict): +def split_large_video(video_paths: list): original_video = None split = False - for original_video in response.get("filepath", []): + for original_video in video_paths: size = os.stat(original_video).st_size if size > TG_MAX_SIZE: split = True @@ -288,7 +278,7 @@ def split_large_video(response: dict): os.remove(original_video) if split and original_video: - response["filepath"] = [i.as_posix() for i in pathlib.Path(original_video).parent.glob("*")] + return [i for i in pathlib.Path(original_video).parent.glob("*")] if __name__ == "__main__": diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index fe0f9dc5..b841fc14 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -29,9 +29,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel -from pyrogram import Client -from pyrogram import idle -from pyrogram import types +from pyrogram import Client, idle, types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor @@ -125,40 +123,38 @@ def forward_video(client, bot_msg, url: str): unique = get_unique_clink(url, chat_id) cached_fid = redis.get_send_cache(unique) if not cached_fid: + redis.update_metrics("cache_miss") return False - try: - res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid) - obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo - - caption, _ = gen_cap(bot_msg, url, obj) - res_msg.edit_text(caption, reply_markup=gen_video_markup()) - bot_msg.edit_text(f"Download success!โœ…โœ…โœ…") - redis.update_metrics("cache_hit") - return True - except Exception as e: - traceback.print_exc() - logging.error("Failed to forward message %s", e) - redis.del_send_cache(unique) - redis.update_metrics("cache_miss") + res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid) + obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo + + caption, _ = gen_cap(bot_msg, url, obj) + res_msg.edit_text(caption, reply_markup=gen_video_markup()) + bot_msg.edit_text(f"Download success!โœ…โœ…โœ…") + redis.update_metrics("cache_hit") + return True def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str): payment = Payment() chat_id = bot_msg.chat.id - if forward_video(client, bot_msg, url): - return - mode = payment.get_user_settings(chat_id)[-1] - if ENABLE_CELERY and mode in [None, "Celery"]: - async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) - # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) - else: - ytdl_normal_download(client, bot_msg, url) + try: + if forward_video(client, bot_msg, url): + return + mode = payment.get_user_settings(chat_id)[-1] + if ENABLE_CELERY and mode in [None, "Celery"]: + async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) + # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) + else: + ytdl_normal_download(client, bot_msg, url) + except Exception as e: + logging.error("Failed to download %s, error: ", url, e) + bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True) def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): if ENABLE_CELERY: - # TODO disable it for now direct_normal_download(client, bot_msg, url) # direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url) else: @@ -228,10 +224,10 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: client.send_chat_action(chat_id, "record_audio") # just try to download the audio using yt-dlp - resp = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") + filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") client.send_chat_action(chat_id, "upload_audio") - for f in resp["filepath"]: + for f in filepath: client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") Redis().update_metrics("audio_success") @@ -264,29 +260,23 @@ def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, ty chat_id = bot_msg.chat.id temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-") - result = ytdl_download(url, temp_dir.name, bot_msg) + video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") - if result["status"]: - client.send_chat_action(chat_id, "upload_document") - video_paths: list = result["filepath"] - bot_msg.edit_text("Download complete. Sending now...") - try: - upload_processor(client, bot_msg, url, video_paths) - except pyrogram.errors.Flood as e: - logging.critical("FloodWait from Telegram: %s", e) - client.send_message( - chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", - ) - flood_owner_message(client, e) - time.sleep(e.x) - upload_processor(client, bot_msg, url, video_paths) + client.send_chat_action(chat_id, "upload_document") + bot_msg.edit_text("Download complete. Sending now...") + try: + upload_processor(client, bot_msg, url, video_paths) + except pyrogram.errors.Flood as e: + logging.critical("FloodWait from Telegram: %s", e) + client.send_message( + chat_id, + f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", + ) + flood_owner_message(client, e) + time.sleep(e.x) + upload_processor(client, bot_msg, url, video_paths) - bot_msg.edit_text("Download success!โœ…") - else: - client.send_chat_action(chat_id, "typing") - tb = result["error"][0:4000] - bot_msg.edit_text(f"Download failed!โŒ\n\n```{tb}```", disable_web_page_preview=True) + bot_msg.edit_text("Download success!โœ…") temp_dir.cleanup() From cddc37ff7e2e3257fb6d8024aa2a0cc3ae476468 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 20 Apr 2023 18:57:29 +0200 Subject: [PATCH 088/297] enhance db connection --- requirements.txt | 2 +- ytdlbot/database.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1a6e6a85..a622088b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ PyMySQL==1.0.3 celery==5.2.7 filetype==1.2.0 flower==1.2.0 -psutil==5.9.4 +psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 fakeredis==2.10.3 diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 3e2fb8bd..f5268984 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -41,6 +41,9 @@ def commit(self): def close(self): pass + def ping(self, reconnect): + pass + class Cursor: def __init__(self): @@ -50,7 +53,8 @@ def __init__(self): def execute(self, *args, **kwargs): sql = self.sub(args[0]) new_args = (sql,) + args[1:] - return self.cur.execute(*new_args, **kwargs) + with contextlib.suppress(sqlite3.OperationalError): + return self.cur.execute(*new_args, **kwargs) def fetchall(self): return self.cur.fetchall() @@ -245,13 +249,14 @@ class MySQL: """ def __init__(self): - if MYSQL_HOST: + try: self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) - else: + except pymysql.err.OperationalError: self.con = FakeMySQL() + self.con.ping(reconnect=True) self.cur = self.con.cursor() self.init_db() super().__init__() From ffcfcea46e2c5d5c34040bcd0661fca1c5b81029 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 26 Apr 2023 22:22:30 +0200 Subject: [PATCH 089/297] add sentry error tracking --- requirements.txt | 3 ++- ytdlbot/config.py | 2 ++ ytdlbot/tasks.py | 5 +++++ ytdlbot/ytdl_bot.py | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a622088b..54a82dca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.2.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.10.3 +fakeredis==2.11.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 @@ -22,3 +22,4 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 +sentry-sdk==1.21.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 766d1ff0..b76c1026 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -62,3 +62,5 @@ TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits RATE_LIMIT = os.getenv("RATE_LIMIT", 10) + +DSN = os.getenv("DSN") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index b841fc14..3eb539e8 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -26,18 +26,21 @@ import psutil import pyrogram.errors import requests +import sentry_sdk from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel from pyrogram import Client, idle, types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor +from sentry_sdk.integrations.celery import CeleryIntegration from channel import Channel from client_init import create_app from config import ( ARCHIVE_ID, BROKER, + DSN, ENABLE_CELERY, ENABLE_QUEUE, ENABLE_VIP, @@ -71,6 +74,8 @@ session = "ytdl-celery" celery_client = create_app(session) +if DSN: + sentry_sdk.init(DSN, integrations=[CeleryIntegration()]) def get_messages(chat_id, message_id): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index d6ad58ed..bb8e526d 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -17,6 +17,7 @@ from io import BytesIO import pyrogram.errors +import sentry_sdk from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant @@ -29,6 +30,7 @@ from client_init import create_app from config import ( AUTHORIZED_USER, + DSN, ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, @@ -60,6 +62,8 @@ logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() channel = Channel() +if DSN: + sentry_sdk.init(dsn=DSN) def private_use(func): From f2b47fb392a45597c4a85b2336100cf89a27835b Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 28 Apr 2023 18:06:11 +0200 Subject: [PATCH 090/297] change default pyrogram worker count --- requirements.txt | 2 +- ytdlbot/config.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 54a82dca..a8e402dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ fakeredis==2.11.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 -requests==2.28.2 +requests==2.29.0 tqdm==4.65.0 requests-toolbelt==0.10.1 ffpb==0.4.1 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index b76c1026..a3d78e9b 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -11,7 +11,7 @@ # general settings WORKERS: int = int(os.getenv("WORKERS", 100)) -PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, os.cpu_count() + 4))) +PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(64, (os.cpu_count() + 4) * 10))) APP_ID: int = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") @@ -61,6 +61,6 @@ FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 5) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits -RATE_LIMIT = os.getenv("RATE_LIMIT", 10) +RATE_LIMIT = os.getenv("RATE_LIMIT", 20) DSN = os.getenv("DSN") From 3e759615a43d0b839d8e2451e3908b589d1ed137 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 1 May 2023 20:43:35 +0200 Subject: [PATCH 091/297] correct way to use fakeredis fix #229 --- ytdlbot/channel.py | 2 +- ytdlbot/database.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 3d36c700..6dbf7e0f 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -59,7 +59,7 @@ def extract_canonical_link(url: str) -> str: cookie = {"CONSENT": "YES+cb.20210328-17-p0.en+FX+999"} # send head request first r = requests.head(url, headers=headers, allow_redirects=True, cookies=cookie) - if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type"): + if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type", ""): # get content-type, if it's not text/html, there's no need to issue a GET request logging.warning("%s Content-type is not text/html, no need to GET for extract_canonical_link", url) return url diff --git a/ytdlbot/database.py b/ytdlbot/database.py index f5268984..f064c546 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -71,9 +71,10 @@ def sub(sql): class Redis: def __init__(self): - if REDIS: + try: self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True) - else: + self.r.ping() + except redis.RedisError: self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True) db_banner = "=" * 20 + "DB data" + "=" * 20 From 2e41d6e0bc1f271edeead71df29a74031893352d Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 6 May 2023 20:38:16 +0200 Subject: [PATCH 092/297] add search function --- requirements.txt | 8 ++++---- ytdlbot/ytdl_bot.py | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index a8e402dc..0ff684b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,15 +11,15 @@ flower==1.2.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.11.1 +fakeredis==2.11.2 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 -requests==2.29.0 +requests==2.30.0 tqdm==4.65.0 -requests-toolbelt==0.10.1 +requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.21.0 +sentry-sdk==1.21.1 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index bb8e526d..bdade93a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -17,6 +17,7 @@ from io import BytesIO import pyrogram.errors +import requests import sentry_sdk from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types @@ -328,6 +329,17 @@ def generate_invoice(amount: int, title: str, description: str, payload: str): return invoice +def search(kw: str): + api = f"https://dmesg.app/ytdlbot.php?search={kw}" + # title, url, time, image + text, index = "", 1 + for item in requests.get(api).json()["results"][:10]: + item["index"] = index + text += "{index}. {title}\n{url}\n{time}\n\n".format(**item) + index += 1 + return text + + @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: Client, message: types.Message): @@ -342,7 +354,8 @@ def download_handler(client: Client, message: types.Message): # check url if not re.findall(r"^https?://", url.lower()): redis.update_metrics("bad_request") - message.reply_text("I think you should send me a link.", quote=True) + text = search(url) + message.reply_text(text, quote=True) return # disable by default From c7ac2bb2d8d4717aec61d719b05d294321e5cd31 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 18 May 2023 13:25:25 +0200 Subject: [PATCH 093/297] sub direct download --- requirements.txt | 4 ++-- ytdlbot/tasks.py | 4 ++-- ytdlbot/ytdl_bot.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0ff684b5..8e97e682 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==1.2.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.11.2 +fakeredis==2.12.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 @@ -22,4 +22,4 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.21.1 +sentry-sdk==1.23.1 diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 3eb539e8..f2129296 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -141,13 +141,13 @@ def forward_video(client, bot_msg, url: str): return True -def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str): +def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): payment = Payment() chat_id = bot_msg.chat.id try: if forward_video(client, bot_msg, url): return - mode = payment.get_user_settings(chat_id)[-1] + mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index bdade93a..1243e5c2 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -453,7 +453,7 @@ def periodic_sub_check(): bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( uid, f"{video_url} is downloading...", disable_web_page_preview=True ) - ytdl_download_entrance(app, bot_msg, video_url) + ytdl_download_entrance(app, bot_msg, video_url, mode="direct") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) From c51032373f25bdbbe0dd782b443f3e2381bb9728 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 18 May 2023 20:14:20 +0200 Subject: [PATCH 094/297] add link checker, local mode exclusive --- ytdlbot/constant.py | 1 + ytdlbot/limit.py | 7 ++++++- ytdlbot/ytdl_bot.py | 38 +++++++++++++++++++++++++------------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 3aa7e6bb..c7eeb681 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -51,6 +51,7 @@ class BotText: 5. I will record your unique ID after a successful payment, which is usually your payment ID or email address. +6. Paid user can change default download mode to Local mode in settings, which is faster. If your used up all your tokens, you will be reset to default mode. **Download token price:** 1. Everyone: {FREE_DOWNLOAD} tokens per 24 hours, free of charge. diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 9f651f5c..dcb4e3a7 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -104,7 +104,12 @@ def check_old_user(self, user_id: int) -> tuple: def get_pay_token(self, user_id: int) -> int: self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) data = self.cur.fetchall() or [(0,)] - return sum([i[0] for i in data if i[0]]) + number = sum([i[0] for i in data if i[0]]) + if number == 0: + logging.info("User %s has no token, set download mode to Celery", user_id) + # change download mode to Celery + self.set_user_settings(user_id, "mode", "Celery") + return number def get_free_token(self, user_id: int) -> int: if self.r.exists(user_id): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 1243e5c2..409d6117 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -19,6 +19,7 @@ import pyrogram.errors import requests import sentry_sdk +import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant @@ -246,12 +247,13 @@ def direct_handler(client: Client, message: types.Message): @app.on_message(filters.command(["settings"])) def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id + payment = Payment() client.send_chat_action(chat_id, "typing") data = MySQL().get_user_settings(chat_id) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" - if message.chat.username == OWNER: + if message.chat.username == OWNER or payment.get_pay_token(chat_id): extra = [InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] else: extra = [] @@ -340,6 +342,22 @@ def search(kw: str): return text +def link_checker(url: str) -> str: + ytdl = yt_dlp.YoutubeDL() + + if ( + not PLAYLIST_SUPPORT + and re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) + or "list" in url + ): + return "Playlist or channel links are disabled." + + if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): + return "m3u8 links are disabled." + if ytdl.extract_info(url, download=False).get("live_status"): + return "Live stream links are disabled. Please download it after the stream ends." + + @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: Client, message: types.Message): @@ -358,16 +376,10 @@ def download_handler(client: Client, message: types.Message): message.reply_text(text, quote=True) return - # disable by default - if not PLAYLIST_SUPPORT: - if re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url: - message.reply_text( - "The ability to download a channel or list has been disabled." - "Kindly provide me with the specific link for each video instead.", - quote=True, - ) - redis.update_metrics("reject_channel") - return + if text := link_checker(url): + message.reply_text(text, quote=True) + redis.update_metrics("reject_link_checker") + return # old user is not limited by token if ENABLE_VIP and not payment.check_old_user(chat_id): @@ -451,9 +463,9 @@ def periodic_sub_check(): for uid in uids: try: bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( - uid, f"{video_url} is downloading...", disable_web_page_preview=True + uid, f"{video_url} is out. Watch it on YouTube" ) - ytdl_download_entrance(app, bot_msg, video_url, mode="direct") + # ytdl_download_entrance(app, bot_msg, video_url, mode="direct") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) From b8f945d9c2a31f5e4a02099984a1263a7c5e704e Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 18 May 2023 20:23:13 +0200 Subject: [PATCH 095/297] update actions --- .github/workflows/builder.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index e8b78fcf..92fd95c5 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -9,18 +9,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: true - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Cache Docker layers - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} @@ -28,13 +28,13 @@ jobs: ${{ runner.os }}-buildx- - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -42,18 +42,18 @@ jobs: - name: Lower case for Docker Hub id: dh_string - uses: ASzc/change-string-case-action@v1 + uses: ASzc/change-string-case-action@v5 with: string: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} - name: Lower case for ghcr id: ghcr_string - uses: ASzc/change-string-case-action@v1 + uses: ASzc/change-string-case-action@v5 with: string: ${{ github.event.repository.full_name }} - name: Build and push - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v4 with: context: . platforms: linux/arm,linux/amd64,linux/arm64 @@ -68,4 +68,4 @@ jobs: - name: Move cache run: | rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache \ No newline at end of file + mv /tmp/.buildx-cache-new /tmp/.buildx-cache From 22900f4afac59f0b9ecabe1547beefab64c04894 Mon Sep 17 00:00:00 2001 From: Nova Kwok Date: Fri, 19 May 2023 10:52:51 +0800 Subject: [PATCH 096/297] Revert https://github.com/tgbot-collection/ytdlbot/commit/c51032373f25bdbbe0dd782b443f3e2381bb9728 (#242) --- ytdlbot/constant.py | 2 -- ytdlbot/limit.py | 7 +------ ytdlbot/ytdl_bot.py | 37 +++++++++++++------------------------ 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index c7eeb681..d248d5ba 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -51,8 +51,6 @@ class BotText: 5. I will record your unique ID after a successful payment, which is usually your payment ID or email address. -6. Paid user can change default download mode to Local mode in settings, which is faster. If your used up all your tokens, you will be reset to default mode. - **Download token price:** 1. Everyone: {FREE_DOWNLOAD} tokens per 24 hours, free of charge. 2. 1 USD == {TOKEN_PRICE} tokens, valid indefinitely. diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index dcb4e3a7..9f651f5c 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -104,12 +104,7 @@ def check_old_user(self, user_id: int) -> tuple: def get_pay_token(self, user_id: int) -> int: self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) data = self.cur.fetchall() or [(0,)] - number = sum([i[0] for i in data if i[0]]) - if number == 0: - logging.info("User %s has no token, set download mode to Celery", user_id) - # change download mode to Celery - self.set_user_settings(user_id, "mode", "Celery") - return number + return sum([i[0] for i in data if i[0]]) def get_free_token(self, user_id: int) -> int: if self.r.exists(user_id): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 409d6117..ee76c7fb 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -19,7 +19,6 @@ import pyrogram.errors import requests import sentry_sdk -import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant @@ -253,7 +252,7 @@ def settings_handler(client: Client, message: types.Message): set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" - if message.chat.username == OWNER or payment.get_pay_token(chat_id): + if message.chat.username == OWNER: extra = [InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] else: extra = [] @@ -342,22 +341,6 @@ def search(kw: str): return text -def link_checker(url: str) -> str: - ytdl = yt_dlp.YoutubeDL() - - if ( - not PLAYLIST_SUPPORT - and re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) - or "list" in url - ): - return "Playlist or channel links are disabled." - - if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): - return "m3u8 links are disabled." - if ytdl.extract_info(url, download=False).get("live_status"): - return "Live stream links are disabled. Please download it after the stream ends." - - @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: Client, message: types.Message): @@ -376,10 +359,16 @@ def download_handler(client: Client, message: types.Message): message.reply_text(text, quote=True) return - if text := link_checker(url): - message.reply_text(text, quote=True) - redis.update_metrics("reject_link_checker") - return + # disable by default + if not PLAYLIST_SUPPORT: + if re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url: + message.reply_text( + "The ability to download a channel or list has been disabled." + "Kindly provide me with the specific link for each video instead.", + quote=True, + ) + redis.update_metrics("reject_channel") + return # old user is not limited by token if ENABLE_VIP and not payment.check_old_user(chat_id): @@ -463,9 +452,9 @@ def periodic_sub_check(): for uid in uids: try: bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( - uid, f"{video_url} is out. Watch it on YouTube" + uid, f"{video_url} is downloading...", disable_web_page_preview=True ) - # ytdl_download_entrance(app, bot_msg, video_url, mode="direct") + ytdl_download_entrance(app, bot_msg, video_url, mode="direct") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) From 6c97810aedccf289b11d306c9b7f093d8db0f9c3 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 17:19:17 +0200 Subject: [PATCH 097/297] Revert "Revert https://github.com/tgbot-collection/ytdlbot/commit/c51032373f25bdbbe0dd782b443f3e2381bb9728 (#242)" This reverts commit 22900f4afac59f0b9ecabe1547beefab64c04894. --- ytdlbot/constant.py | 2 ++ ytdlbot/limit.py | 7 ++++++- ytdlbot/ytdl_bot.py | 37 ++++++++++++++++++++++++------------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index d248d5ba..c7eeb681 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -51,6 +51,8 @@ class BotText: 5. I will record your unique ID after a successful payment, which is usually your payment ID or email address. +6. Paid user can change default download mode to Local mode in settings, which is faster. If your used up all your tokens, you will be reset to default mode. + **Download token price:** 1. Everyone: {FREE_DOWNLOAD} tokens per 24 hours, free of charge. 2. 1 USD == {TOKEN_PRICE} tokens, valid indefinitely. diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 9f651f5c..dcb4e3a7 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -104,7 +104,12 @@ def check_old_user(self, user_id: int) -> tuple: def get_pay_token(self, user_id: int) -> int: self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) data = self.cur.fetchall() or [(0,)] - return sum([i[0] for i in data if i[0]]) + number = sum([i[0] for i in data if i[0]]) + if number == 0: + logging.info("User %s has no token, set download mode to Celery", user_id) + # change download mode to Celery + self.set_user_settings(user_id, "mode", "Celery") + return number def get_free_token(self, user_id: int) -> int: if self.r.exists(user_id): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ee76c7fb..409d6117 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -19,6 +19,7 @@ import pyrogram.errors import requests import sentry_sdk +import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant @@ -252,7 +253,7 @@ def settings_handler(client: Client, message: types.Message): set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" - if message.chat.username == OWNER: + if message.chat.username == OWNER or payment.get_pay_token(chat_id): extra = [InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] else: extra = [] @@ -341,6 +342,22 @@ def search(kw: str): return text +def link_checker(url: str) -> str: + ytdl = yt_dlp.YoutubeDL() + + if ( + not PLAYLIST_SUPPORT + and re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) + or "list" in url + ): + return "Playlist or channel links are disabled." + + if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): + return "m3u8 links are disabled." + if ytdl.extract_info(url, download=False).get("live_status"): + return "Live stream links are disabled. Please download it after the stream ends." + + @app.on_message(filters.incoming & filters.text) @private_use def download_handler(client: Client, message: types.Message): @@ -359,16 +376,10 @@ def download_handler(client: Client, message: types.Message): message.reply_text(text, quote=True) return - # disable by default - if not PLAYLIST_SUPPORT: - if re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url: - message.reply_text( - "The ability to download a channel or list has been disabled." - "Kindly provide me with the specific link for each video instead.", - quote=True, - ) - redis.update_metrics("reject_channel") - return + if text := link_checker(url): + message.reply_text(text, quote=True) + redis.update_metrics("reject_link_checker") + return # old user is not limited by token if ENABLE_VIP and not payment.check_old_user(chat_id): @@ -452,9 +463,9 @@ def periodic_sub_check(): for uid in uids: try: bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( - uid, f"{video_url} is downloading...", disable_web_page_preview=True + uid, f"{video_url} is out. Watch it on YouTube" ) - ytdl_download_entrance(app, bot_msg, video_url, mode="direct") + # ytdl_download_entrance(app, bot_msg, video_url, mode="direct") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) From 2b9a6c790b396deb68636e4a83615c3e454b072f Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 17:25:40 +0200 Subject: [PATCH 098/297] fix stream checker --- docker-compose.yml | 6 ++++-- ytdlbot/ytdl_bot.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 226aa2d2..6c50943d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,12 +35,14 @@ services: volumes: - ./data/instagram.com_cookies.txt:/ytdlbot/ytdlbot/instagram.com_cookies.txt - ./data/vnstat/:/var/lib/vnstat/ + labels: + - "com.centurylinklabs.watchtower.enable=true" flower: image: bennythink/ytdlbot env_file: - env/ytdl.env - restart: on-failure + restart: unless-stopped command: [ "/usr/local/bin/celery", "-A", "flower_tasks", "flower", "--basic_auth=benny:123456", @@ -48,4 +50,4 @@ services: volumes: - ./data/flower:/ytdlbot/ytdlbot/flower ports: - - "5555:5555" + - "127.0.0.1:15555:5555" diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 409d6117..1da513e1 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -354,7 +354,7 @@ def link_checker(url: str) -> str: if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): return "m3u8 links are disabled." - if ytdl.extract_info(url, download=False).get("live_status"): + if ytdl.extract_info(url, download=False).get("live_status") == "is_live": return "Live stream links are disabled. Please download it after the stream ends." From 941cc069cb033f18d694bed52469d980abe5ced9 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 20:24:16 +0200 Subject: [PATCH 099/297] fix instagram api --- README.md | 2 ++ ytdlbot/downloader.py | 4 ++-- ytdlbot/ytdl_bot.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 841a24a4..2af028aa 100644 --- a/README.md +++ b/README.md @@ -271,7 +271,9 @@ https://twitter.com/BennyThinks/status/1475836588542341124 ## Test instagram https://www.instagram.com/p/ClBSqo3PkJw/ + https://www.instagram.com/p/CaiAHoWDnrM/ + https://www.instagram.com/p/CZtUDyyv1u1/ # Donation diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index c66ee057..f235274f 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,7 +26,7 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6 +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, TOKEN from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -246,7 +246,7 @@ def convert_audio_format(video_paths: list, bm): def download_instagram(url: str, tempdir: str): if url.startswith("https://www.instagram.com"): - api = f"https://ssmstore.store/rami/index.php?url={url}" + api = f"https://ytdlbot.dmesg.app?token={TOKEN}&url={url}" res = requests.get(api).json() if isinstance(res, dict): downloadable = {i["url"]: i["ext"] for i in res["url"]} diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 1da513e1..08b17952 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -332,7 +332,7 @@ def generate_invoice(amount: int, title: str, description: str, payload: str): def search(kw: str): - api = f"https://dmesg.app/ytdlbot.php?search={kw}" + api = f"https://dmesg.app/ytdlbot/search.php?search={kw}" # title, url, time, image text, index = "", 1 for item in requests.get(api).json()["results"][:10]: From dd0627c045d00066bba1e04a0c4e512f3ff58582 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 20:43:05 +0200 Subject: [PATCH 100/297] supress error --- ytdlbot/ytdl_bot.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 08b17952..f9f8211b 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -7,6 +7,7 @@ __author__ = "Benny " +import contextlib import logging import os import random @@ -354,8 +355,10 @@ def link_checker(url: str) -> str: if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): return "m3u8 links are disabled." - if ytdl.extract_info(url, download=False).get("live_status") == "is_live": - return "Live stream links are disabled. Please download it after the stream ends." + + with contextlib.suppress(yt_dlp.utils.DownloadError): + if ytdl.extract_info(url, download=False).get("live_status") == "is_live": + return "Live stream links are disabled. Please download it after the stream ends." @app.on_message(filters.incoming & filters.text) From 83954869cf811eae66cfc5df007308838beaa70b Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 21:16:16 +0200 Subject: [PATCH 101/297] fix instagram --- ytdlbot/downloader.py | 1 + ytdlbot/tasks.py | 48 +++++++++++++++++++++++-------------------- ytdlbot/ytdl_bot.py | 2 ++ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index f235274f..bc0e8d9a 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -246,6 +246,7 @@ def convert_audio_format(video_paths: list, bm): def download_instagram(url: str, tempdir: str): if url.startswith("https://www.instagram.com"): + logging.info("Requesting instagram download link for %s", url) api = f"https://ytdlbot.dmesg.app?token={TOKEN}&url={url}" res = requests.get(api).json() if isinstance(res, dict): diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index f2129296..89516cf2 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -154,7 +154,7 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod else: ytdl_normal_download(client, bot_msg, url) except Exception as e: - logging.error("Failed to download %s, error: ", url, e) + logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True) @@ -326,6 +326,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): settings = payment.get_user_settings(chat_id) if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): chat_id = ARCHIVE_ID + if settings[2] == "document": logging.info("Sending as document") try: @@ -362,6 +363,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): progress_args=(bot_msg,), ) else: + # settings==video logging.info("Sending as video") try: res_msg = client.send_video( @@ -374,27 +376,29 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): reply_markup=markup, **meta, ) - except ValueError: - logging.info("Retry to send as animation") - res_msg = client.send_animation( - chat_id, - vp_or_fid, - caption=cap, - progress=upload_hook, - progress_args=(bot_msg,), - reply_markup=markup, - **meta, - ) - except FileNotFoundError: - # this is likely a photo - logging.info("Retry to send as photo") - res_msg = client.send_photo( - chat_id, - vp_or_fid, - caption=cap, - progress=upload_hook, - progress_args=(bot_msg,), - ) + except Exception: + # try to send as annimation, photo + try: + logging.warning("Retry to send as animation") + res_msg = client.send_animation( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + reply_markup=markup, + **meta, + ) + except Exception: + # this is likely a photo + logging.warning("Retry to send as photo") + res_msg = client.send_photo( + chat_id, + vp_or_fid, + caption=cap, + progress=upload_hook, + progress_args=(bot_msg,), + ) unique = get_unique_clink(url, bot_msg.chat.id) obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index f9f8211b..33f5f006 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -344,6 +344,8 @@ def search(kw: str): def link_checker(url: str) -> str: + if url.startswith("https://www.instagram.com"): + return "" ytdl = yt_dlp.YoutubeDL() if ( From 8a50104e2003e90ec087967b23a97ece245e9b19 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 20 May 2023 21:36:05 +0200 Subject: [PATCH 102/297] fix celery override --- ytdlbot/limit.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index dcb4e3a7..a7ccc8c7 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -102,11 +102,12 @@ def check_old_user(self, user_id: int) -> tuple: return data def get_pay_token(self, user_id: int) -> int: - self.cur.execute("SELECT token FROM payment WHERE user_id=%s", (user_id,)) - data = self.cur.fetchall() or [(0,)] + self.cur.execute("SELECT token, old_user FROM payment WHERE user_id=%s", (user_id,)) + data = self.cur.fetchall() or [(0, False)] number = sum([i[0] for i in data if i[0]]) - if number == 0: - logging.info("User %s has no token, set download mode to Celery", user_id) + if number == 0 and data[0][1] != 1: + # not old user, no token + logging.warning("User %s has no token, set download mode to Celery", user_id) # change download mode to Celery self.set_user_settings(user_id, "mode", "Celery") return number From 4aa956bbe1c8bc84575caa8da0d1b3b5f1945a62 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 28 May 2023 10:56:27 +0200 Subject: [PATCH 103/297] add /settings notice --- requirements.txt | 6 +++--- ytdlbot/ytdl_bot.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8e97e682..aa3fd5cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,15 +11,15 @@ flower==1.2.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.12.1 +fakeredis==2.13.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 -requests==2.30.0 +requests==2.31.0 tqdm==4.65.0 requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.23.1 +sentry-sdk==1.24.0 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 33f5f006..04e00bb1 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -305,6 +305,7 @@ def buy_handler(client: Client, message: types.Message): message="Buy more download token", ) ) + client.send_message(chat_id, "In /settings, change your download mode to Local will make the download faster!") @app.on_message(filters.command(["redeem"])) From 8db05d6607933e8ec995cc5d6d793769a60221e6 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 29 May 2023 18:16:35 +0200 Subject: [PATCH 104/297] free download 10 --- README.md | 2 +- ytdlbot/channel.py | 3 +-- ytdlbot/config.py | 2 +- ytdlbot/constant.py | 3 +-- ytdlbot/ytdl_bot.py | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2af028aa..6fc16145 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/support Due to limitations on servers and bandwidth, there are some restrictions on this free service. -* Each user is limited to 5 free downloads per 24-hour period +* Each user is limited to 10 free downloads per 24-hour period * there is a maximum of three subscriptions allowed for YouTube channels. If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 6dbf7e0f..90e47233 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -19,8 +19,7 @@ def subscribe_channel(self, user_id: int, share_link: str) -> str: if ENABLE_VIP: self.cur.execute("select count(user_id) from subscribe where user_id=%s", (user_id,)) usage = int(self.cur.fetchone()[0]) - if usage >= 5: - # TODO: 5 tokens for one more subscription? + if usage >= 10: logging.warning("User %s has subscribed %s channels", user_id, usage) return "You have subscribed too many channels. Maximum 5 channels." diff --git a/ytdlbot/config.py b/ytdlbot/config.py index a3d78e9b..d2f18020 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -58,7 +58,7 @@ PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 5) +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 10) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits RATE_LIMIT = os.getenv("RATE_LIMIT", 20) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index c7eeb681..f1bf3df0 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -23,7 +23,6 @@ class BotText: start = "Welcome to YouTube Download bot. Type /help for more information." - rate_limit = "**I'm being rate limited by Telegram. Please expect a delay in processing your request.**" help = f""" 1. This bot should work at all times. If it doesn't, please wait for a few minutes and try sending the link again. @@ -93,7 +92,7 @@ def get_receive_link_text() -> str: if ENABLE_CELERY and reserved: text = f"Too many tasks. Your tasks was added to the reserved queue {reserved}." else: - text = "Your task was added to active queue.\nProcessing...\n\n" + BotText.rate_limit + text = "Your task was added to active queue.\nProcessing...\n\n" return text diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 04e00bb1..694e013f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -126,7 +126,7 @@ def start_handler(client: Client, message: types.Message): info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}" else: info = "" - text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}\n{BotText.rate_limit}" + text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" client.send_message(message.chat.id, text) From ba85246a9dec074fd74419dff1a2386a819b288d Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 11 Jun 2023 10:46:05 +0200 Subject: [PATCH 105/297] download from file --- requirements.txt | 6 +-- ytdlbot/ytdl_bot.py | 96 ++++++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/requirements.txt b/requirements.txt index aa3fd5cf..770c808b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,13 +5,13 @@ APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.3 -celery==5.2.7 +celery==5.3.0 filetype==1.2.0 flower==1.2.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.13.0 +fakeredis==2.14.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 @@ -22,4 +22,4 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.24.0 +sentry-sdk==1.25.0 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 694e013f..035850e8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -12,6 +12,7 @@ import os import random import re +import tempfile import time import traceback import typing @@ -364,62 +365,69 @@ def link_checker(url: str) -> str: return "Live stream links are disabled. Please download it after the stream ends." -@app.on_message(filters.incoming & filters.text) +@app.on_message(filters.incoming & (filters.text | filters.document)) @private_use def download_handler(client: Client, message: types.Message): payment = Payment() chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") redis.user_count(chat_id) + if message.document: + with tempfile.NamedTemporaryFile(mode="r+") as tf: + logging.info("Downloading file to %s", tf.name) + message.download(tf.name) + contents = open(tf.name, "r").read() # don't know why + urls = contents.split() + else: + urls = [re.sub(r"/ytdl\s*", "", message.text)] + logging.info("start %s", urls) + + for url in urls: + # check url + if not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + text = search(url) + message.reply_text(text, quote=True) + return - url = re.sub(r"/ytdl\s*", "", message.text) - logging.info("start %s", url) + if text := link_checker(url): + message.reply_text(text, quote=True) + redis.update_metrics("reject_link_checker") + return - # check url - if not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - text = search(url) - message.reply_text(text, quote=True) - return + # old user is not limited by token + if ENABLE_VIP and not payment.check_old_user(chat_id): + free, pay, reset = payment.get_token(chat_id) + if free + pay <= 0: + message.reply_text( + f"You don't have enough token. Please wait until {reset} or /buy more token.", quote=True + ) + redis.update_metrics("reject_token") + return + else: + payment.use_token(chat_id) - if text := link_checker(url): - message.reply_text(text, quote=True) - redis.update_metrics("reject_link_checker") - return + redis.update_metrics("video_request") - # old user is not limited by token - if ENABLE_VIP and not payment.check_old_user(chat_id): - free, pay, reset = payment.get_token(chat_id) - if free + pay <= 0: - message.reply_text( - f"You don't have enough token. Please wait until {reset} or /buy more token.", quote=True + text = BotText.get_receive_link_text() + try: + # raise pyrogram.errors.exceptions.FloodWait(10) + bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) + except pyrogram.errors.Flood as e: + f = BytesIO() + f.write(str(e).encode()) + f.write(b"Your job will be done soon. Just wait! Don't rush.") + f.name = "Please don't flood me.txt" + bot_msg = message.reply_document( + f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True ) - redis.update_metrics("reject_token") - return - else: - payment.use_token(chat_id) - - redis.update_metrics("video_request") - - text = BotText.get_receive_link_text() - try: - # raise pyrogram.errors.exceptions.FloodWait(10) - bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) - except pyrogram.errors.Flood as e: - f = BytesIO() - f.write(str(e).encode()) - f.write(b"Your job will be done soon. Just wait! Don't rush.") - f.name = "Please don't flood me.txt" - bot_msg = message.reply_document( - f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True - ) - f.close() - client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") - time.sleep(e.x) + f.close() + client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") + time.sleep(e.x) - client.send_chat_action(chat_id, "upload_video") - bot_msg.chat = message.chat - ytdl_download_entrance(client, bot_msg, url) + client.send_chat_action(chat_id, "upload_video") + bot_msg.chat = message.chat + ytdl_download_entrance(client, bot_msg, url) @app.on_callback_query(filters.regex(r"document|video|audio")) From af322dcef8194e6cc49b1eb077d2f6b2a487ce4b Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 11 Jun 2023 12:30:05 +0200 Subject: [PATCH 106/297] add config for ssyoutube --- ytdlbot/config.py | 1 + ytdlbot/downloader.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index d2f18020..e059e381 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -64,3 +64,4 @@ RATE_LIMIT = os.getenv("RATE_LIMIT", 20) DSN = os.getenv("DSN") +SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index bc0e8d9a..d34bd9d2 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,7 +26,7 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, TOKEN +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, SS_YOUTUBE from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -247,7 +247,7 @@ def convert_audio_format(video_paths: list, bm): def download_instagram(url: str, tempdir: str): if url.startswith("https://www.instagram.com"): logging.info("Requesting instagram download link for %s", url) - api = f"https://ytdlbot.dmesg.app?token={TOKEN}&url={url}" + api = SS_YOUTUBE + f"&url={url}" res = requests.get(api).json() if isinstance(res, dict): downloadable = {i["url"]: i["ext"] for i in res["url"]} From b85b559efa934babf2a456943fd93070dd03ef36 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 23 Jun 2023 11:08:34 +0200 Subject: [PATCH 107/297] bump dependencies --- README.md | 2 +- requirements.txt | 10 +++++----- ytdlbot/config.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6fc16145..b10f6903 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/support Due to limitations on servers and bandwidth, there are some restrictions on this free service. -* Each user is limited to 10 free downloads per 24-hour period +* Each user is limited to 20 free downloads per 24-hour period * there is a maximum of three subscriptions allowed for YouTube channels. If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your diff --git a/requirements.txt b/requirements.txt index 770c808b..4d6b528a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,17 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.3.4 +yt-dlp==2023.6.22 APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.0.3 -celery==5.3.0 +celery==5.3.1 filetype==1.2.0 -flower==1.2.0 +flower==2.0.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.14.1 +fakeredis==2.15.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 @@ -22,4 +22,4 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.25.0 +sentry-sdk==1.26.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index e059e381..67200b57 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -58,7 +58,7 @@ PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 10) +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 20) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits RATE_LIMIT = os.getenv("RATE_LIMIT", 20) From 92c8bf8fb6e808306e8f71926847de8e3e7c75cb Mon Sep 17 00:00:00 2001 From: Jiawen Geng Date: Sun, 9 Jul 2023 21:21:23 +0800 Subject: [PATCH 108/297] fix: exclude vp09 for download (#267) --- .gitpod.yml | 9 +++++++++ ytdlbot/downloader.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .gitpod.yml diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..76d9ef38 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,9 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) +# and commit this file to your remote git repository to share the goodness with others. + +# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart + +tasks: + - init: pip3 install -r requirements.txt + - init: sudo apt update && sudo apt install ffmpeg -y diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index d34bd9d2..965b3abe 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -170,8 +170,8 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: "--split=16", ] formats = [ - # webm and av01 are not streamable on telegram, so we'll extract mp4 and not av01 codec - "bestvideo[ext=mp4][vcodec!*=av01]+bestaudio[ext=m4a]/bestvideo+bestaudio", + # webm , vp9 and av01 are not streamable on telegram, so we'll extract mp4 and not av01 codec + "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", None, ] From f8888d642aab23b9ae343d50567cd7d8ae064946 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Jul 2023 15:21:35 +0200 Subject: [PATCH 109/297] Bump yt-dlp from 2023.6.22 to 2023.7.6 (#265) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2023.6.22 to 2023.7.6. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2023.06.22...2023.07.06) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4d6b528a..3547d015 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.6.22 +yt-dlp==2023.7.6 APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 276729049ef5b7aa386f53c81e59d10a5b53f011 Mon Sep 17 00:00:00 2001 From: Pavel Shumskii Date: Sun, 9 Jul 2023 16:22:49 +0300 Subject: [PATCH 110/297] Fix PLAYLIST_SUPPORT option not working (#262) --- ytdlbot/ytdl_bot.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 035850e8..27805f3b 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -352,8 +352,10 @@ def link_checker(url: str) -> str: if ( not PLAYLIST_SUPPORT - and re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) - or "list" in url + and ( + re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) + or "list" in url + ) ): return "Playlist or channel links are disabled." From 2d05e2c8fbea0c34266e82a3e30e51adc237c939 Mon Sep 17 00:00:00 2001 From: adityabotsex <141850364+adityabotsex@users.noreply.github.com> Date: Sat, 12 Aug 2023 20:16:29 +0530 Subject: [PATCH 111/297] Update app.json (#279) --- app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 6db4337a..f5f5fc37 100644 --- a/app.json +++ b/app.json @@ -29,7 +29,7 @@ "formation": { "worker": { "quantity": 1, - "size": "free" + "size": "eco" } }, "buildpacks": [ @@ -40,4 +40,4 @@ "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git" } ] -} \ No newline at end of file +} From f3ae3e5680152b14fa91a0779b6b77c38dc18713 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 13 Aug 2023 12:58:12 +0200 Subject: [PATCH 112/297] bump and fix #273 --- requirements.txt | 10 +++++----- ytdlbot/ytdl_bot.py | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4d6b528a..61341cab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,25 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.6.22 +yt-dlp==2023.7.6 APScheduler==3.10.1 beautifultable==1.1.0 ffmpeg-python==0.2.0 -PyMySQL==1.0.3 +PyMySQL==1.1.0 celery==5.3.1 filetype==1.2.0 flower==2.0.0 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.15.0 +fakeredis==2.17.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==4.5.4 requests==2.31.0 -tqdm==4.65.0 +tqdm==4.66.1 requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.26.0 +sentry-sdk==1.29.2 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 035850e8..c8d8081e 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -276,7 +276,12 @@ def settings_handler(client: Client, message: types.Message): ] ) - client.send_message(chat_id, BotText.settings.format(data[1], data[2]) + mode_text, reply_markup=markup) + try: + client.send_message(chat_id, BotText.settings.format(data[1], data[2]) + mode_text, reply_markup=markup) + except: + client.send_message( + chat_id, BotText.settings.format(data[1] + ".", data[2] + ".") + mode_text, reply_markup=markup + ) @app.on_message(filters.command(["buy"])) From 0b6efaae73dd9fc07a64ed33bf3067484e9ff5a1 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 15 Aug 2023 20:43:09 +0200 Subject: [PATCH 113/297] remove sentry --- requirements.txt | 1 - ytdlbot/config.py | 1 - ytdlbot/tasks.py | 5 ----- ytdlbot/ytdl_bot.py | 12 ++---------- 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/requirements.txt b/requirements.txt index 61341cab..4d0dc0f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,4 +22,3 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -sentry-sdk==1.29.2 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 67200b57..c432d446 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -63,5 +63,4 @@ RATE_LIMIT = os.getenv("RATE_LIMIT", 20) -DSN = os.getenv("DSN") SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 89516cf2..a8215c87 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -26,21 +26,18 @@ import psutil import pyrogram.errors import requests -import sentry_sdk from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel from pyrogram import Client, idle, types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor -from sentry_sdk.integrations.celery import CeleryIntegration from channel import Channel from client_init import create_app from config import ( ARCHIVE_ID, BROKER, - DSN, ENABLE_CELERY, ENABLE_QUEUE, ENABLE_VIP, @@ -74,8 +71,6 @@ session = "ytdl-celery" celery_client = create_app(session) -if DSN: - sentry_sdk.init(DSN, integrations=[CeleryIntegration()]) def get_messages(chat_id, message_id): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 246d0781..fa6305c6 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -20,7 +20,6 @@ import pyrogram.errors import requests -import sentry_sdk import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types @@ -34,7 +33,6 @@ from client_init import create_app from config import ( AUTHORIZED_USER, - DSN, ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, @@ -66,8 +64,6 @@ logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() channel = Channel() -if DSN: - sentry_sdk.init(dsn=DSN) def private_use(func): @@ -355,12 +351,8 @@ def link_checker(url: str) -> str: return "" ytdl = yt_dlp.YoutubeDL() - if ( - not PLAYLIST_SUPPORT - and ( - re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) - or "list" in url - ) + if not PLAYLIST_SUPPORT and ( + re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url ): return "Playlist or channel links are disabled." From 297e9768a27865fd7e5a28fcb9c949dc18ab59b0 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 26 Aug 2023 12:54:31 +0200 Subject: [PATCH 114/297] bump --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4d0dc0f7..cd20e45c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,20 +1,20 @@ pyrogram==1.4.16 tgcrypto==1.2.5 yt-dlp==2023.7.6 -APScheduler==3.10.1 +APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.1.0 celery==5.3.1 filetype==1.2.0 -flower==2.0.0 +flower==2.0.1 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.17.0 +fakeredis==2.18.0 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==4.5.4 +redis==5.0.0 requests==2.31.0 tqdm==4.66.1 requests-toolbelt==1.0.0 From dd2af8dcc02df0e907425ca89b6ee25e6b503908 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 26 Aug 2023 12:54:38 +0200 Subject: [PATCH 115/297] fix consent --- ytdlbot/channel.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 90e47233..3715fa4a 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -55,7 +55,7 @@ def extract_canonical_link(url: str) -> str: headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36" } - cookie = {"CONSENT": "YES+cb.20210328-17-p0.en+FX+999"} + cookie = {"CONSENT": "PENDING+197"} # send head request first r = requests.head(url, headers=headers, allow_redirects=True, cookies=cookie) if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type", ""): @@ -174,3 +174,7 @@ def del_cache(self, user_link: str) -> int: if key.startswith(unique): count += self.del_send_cache(key) return count + + +if __name__ == "__main__": + Channel.extract_canonical_link("https://www.youtube.com/watch?v=X4otYJGByic") From 56b0dd2a04ee7b80208ebe82534354313c220cab Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 26 Aug 2023 13:04:29 +0200 Subject: [PATCH 116/297] copy file to other path #282 --- README.md | 2 +- ytdlbot/config.py | 1 + ytdlbot/tasks.py | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b10f6903..6d547053 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ You can configure all the following environment variables: * FREE_DOWNLOAD: free download count per day * TOKEN_PRICE: token price per 1 USD * GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. - +* RCLONE_PATH: rclone path to upload files to cloud storage ## 3.2 Set up init data If you only need basic functionality, you can skip this step. diff --git a/ytdlbot/config.py b/ytdlbot/config.py index c432d446..21ce655a 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -64,3 +64,4 @@ RATE_LIMIT = os.getenv("RATE_LIMIT", 20) SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") +RCLONE_PATH = os.getenv("RCLONE") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index a8215c87..94a1e5cf 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -13,6 +13,7 @@ import pathlib import random import re +import shutil import subprocess import tempfile import threading @@ -42,6 +43,7 @@ ENABLE_QUEUE, ENABLE_VIP, OWNER, + RCLONE_PATH, RATE_LIMIT, WORKERS, ) @@ -278,6 +280,11 @@ def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, ty bot_msg.edit_text("Download success!โœ…") + # setup rclone environment var to back up the downloaded file + if RCLONE_PATH: + for item in os.listdir(temp_dir.name): + logging.info("Copying %s to %s", item, RCLONE_PATH) + shutil.copy(os.path.join(temp_dir.name, item), RCLONE_PATH) temp_dir.cleanup() From fcfd68fcf1d5de85e3a07e3eb1fd9ef23bbc58bf Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 27 Aug 2023 14:02:38 +0200 Subject: [PATCH 117/297] change warning to debug --- ytdlbot/ytdl_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index fa6305c6..5fdb536a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -72,7 +72,7 @@ def wrapper(client: Client, message: types.Message): # message type check if message.chat.type != "private" and not message.text.lower().startswith("/ytdl"): - logging.warning("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) + logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) return # authorized users check From 0607ae8b70be6d75b0c529012f3043090e44ae5e Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 8 Sep 2023 20:02:34 +0200 Subject: [PATCH 118/297] fix youtube short --- ytdlbot/channel.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 3715fa4a..52d2236b 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -69,10 +69,10 @@ def extract_canonical_link(url: str) -> str: element = soup.find("link", rel=prop) try: href = element["href"] - if href not in ["null", "", None]: + if href not in ["null", "", None, "https://consent.youtube.com/m"]: return href - except Exception: - logging.warning("Canonical exception %s", url) + except Exception as e: + logging.debug("Canonical exception %s %s e", url, e) return url @@ -177,4 +177,5 @@ def del_cache(self, user_link: str) -> int: if __name__ == "__main__": - Channel.extract_canonical_link("https://www.youtube.com/watch?v=X4otYJGByic") + s = Channel.extract_canonical_link("https://www.youtube.com/shorts/KkbYbknjPBM") + print(s) From d90133211c903c389881aaa148277fa96ef33532 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 10 Sep 2023 14:08:11 +0200 Subject: [PATCH 119/297] Pyrogram2 (#291) * upgrade to pyrogram 2.x #288 * reformat code * update dependencies * remove instagram cookies * add cryptography * upgrade to py3.11 * temp fix --- Dockerfile | 4 +- docker-compose.yml | 2 +- requirements.txt | 4 +- worker.yml | 4 +- ytdlbot/database.py | 7 +-- ytdlbot/downloader.py | 11 ++++- ytdlbot/flower_tasks.py | 2 +- ytdlbot/tasks.py | 94 +++++++++++++++++++++++------------------ ytdlbot/utils.py | 17 ++++++++ ytdlbot/ytdl_bot.py | 42 +++++++++--------- 10 files changed, 111 insertions(+), 76 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8dde3a93..3b8c86cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM python:3.10-alpine as builder +FROM python:3.11-alpine as builder RUN apk update && apk add --no-cache tzdata alpine-sdk libffi-dev ca-certificates ADD requirements.txt /tmp/ RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt -FROM python:3.10-alpine +FROM python:3.11-alpine WORKDIR /ytdlbot/ytdlbot ENV TZ=Europe/Stockholm diff --git a/docker-compose.yml b/docker-compose.yml index 6c50943d..d43a3f8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,7 @@ services: - ./db_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: 'root' + command: --default-authentication-plugin=mysql_native_password logging: driver: none @@ -33,7 +34,6 @@ services: - socat - redis volumes: - - ./data/instagram.com_cookies.txt:/ytdlbot/ytdlbot/instagram.com_cookies.txt - ./data/vnstat/:/var/lib/vnstat/ labels: - "com.centurylinklabs.watchtower.enable=true" diff --git a/requirements.txt b/requirements.txt index cd20e45c..86ed3642 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ -pyrogram==1.4.16 +pyrogram==2.0.106 tgcrypto==1.2.5 yt-dlp==2023.7.6 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.1.0 -celery==5.3.1 +celery==5.3.4 filetype==1.2.0 flower==2.0.1 psutil==5.9.5 diff --git a/worker.yml b/worker.yml index 30ba3a4a..990e2453 100644 --- a/worker.yml +++ b/worker.yml @@ -7,11 +7,9 @@ services: - env/ytdl.env restart: always command: [ "/usr/local/bin/supervisord", "-c" ,"/ytdlbot/conf/supervisor_worker.conf" ] - volumes: - - ./data/instagram.com_cookies.txt:/ytdlbot/ytdlbot/instagram.com_cookies.txt # network_mode: "host" # deploy: # resources: # limits: # cpus: '0.3' -# memory: 1500M \ No newline at end of file +# memory: 1500M diff --git a/ytdlbot/database.py b/ytdlbot/database.py index f064c546..db721149 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -254,7 +254,9 @@ def __init__(self): self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) + logging.debug("Used real MySQL connection.") except pymysql.err.OperationalError: + logging.warning("Using fake MySQL connection.") self.con = FakeMySQL() self.con.ping(reconnect=True) @@ -273,9 +275,8 @@ def __del__(self): self.con.close() def get_user_settings(self, user_id: int) -> tuple: - cur = self.con.cursor() - cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) - data = cur.fetchone() + self.cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) + data = self.cur.fetchone() if data is None: return 100, "high", "video", "Celery" return data diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 965b3abe..51d66a4a 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,7 +26,14 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, SS_YOUTUBE +from config import ( + AUDIO_FORMAT, + ENABLE_ARIA2, + ENABLE_FFMPEG, + SS_YOUTUBE, + TG_MAX_SIZE, + IPv6, +) from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -35,7 +42,7 @@ def edit_text(bot_msg, text: str): - key = f"{bot_msg.chat.id}-{bot_msg.message_id}" + key = f"{bot_msg.chat.id}-{bot_msg.id}" # if the key exists, we shouldn't send edit message if not r.exists(key): time.sleep(random.random()) diff --git a/ytdlbot/flower_tasks.py b/ytdlbot/flower_tasks.py index f580e95b..31a40dc6 100644 --- a/ytdlbot/flower_tasks.py +++ b/ytdlbot/flower_tasks.py @@ -11,4 +11,4 @@ from config import BROKER -app = Celery('tasks', broker=BROKER, timezone="Asia/Shanghai") +app = Celery("tasks", broker=BROKER, timezone="Asia/Shanghai") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 94a1e5cf..99f71fdc 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -7,6 +7,7 @@ __author__ = "Benny " +import asyncio import logging import math import os @@ -15,6 +16,7 @@ import re import shutil import subprocess +import sys import tempfile import threading import time @@ -30,7 +32,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel -from pyrogram import Client, idle, types +from pyrogram import Client, enums, idle, types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor @@ -43,8 +45,8 @@ ENABLE_QUEUE, ENABLE_VIP, OWNER, - RCLONE_PATH, RATE_LIMIT, + RCLONE_PATH, WORKERS, ) from constant import BotText @@ -71,24 +73,23 @@ redis = Redis() channel = Channel() -session = "ytdl-celery" -celery_client = create_app(session) +bot = create_app("ytdl-celery") def get_messages(chat_id, message_id): try: - return celery_client.get_messages(chat_id, message_id) + return bot.get_messages(chat_id, message_id) except ConnectionError as e: logging.critical("WTH!!! %s", e) - celery_client.start() - return celery_client.get_messages(chat_id, message_id) + bot.start() + return bot.get_messages(chat_id, message_id) @app.task(rate_limit=f"{RATE_LIMIT}/m") def ytdl_download_task(chat_id, message_id, url: str): logging.info("YouTube celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - ytdl_normal_download(celery_client, bot_msg, url) + ytdl_normal_download(bot, bot_msg, url) logging.info("YouTube celery tasks ended.") @@ -96,7 +97,7 @@ def ytdl_download_task(chat_id, message_id, url: str): def audio_task(chat_id, message_id): logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) bot_msg = get_messages(chat_id, message_id) - normal_audio(celery_client, bot_msg) + normal_audio(bot, bot_msg) logging.info("Audio celery tasks ended.") @@ -116,7 +117,7 @@ def get_unique_clink(original_url: str, user_id: int): def direct_download_task(chat_id, message_id, url): logging.info("Direct download celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - direct_normal_download(celery_client, bot_msg, url) + direct_normal_download(bot, bot_msg, url) logging.info("Direct download celery tasks ended.") @@ -146,8 +147,8 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod return mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: - async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) - # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) + async_task(ytdl_download_task, chat_id, bot_msg.id, url) + # ytdl_download_task.delay(chat_id, bot_msg.id, url) else: ytdl_normal_download(client, bot_msg, url) except Exception as e: @@ -158,15 +159,15 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): if ENABLE_CELERY: direct_normal_download(client, bot_msg, url) - # direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url) + # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) else: direct_normal_download(client, bot_msg, url) def audio_entrance(client, bot_msg): if ENABLE_CELERY: - async_task(audio_task, bot_msg.chat.id, bot_msg.message_id) - # audio_task.delay(bot_msg.chat.id, bot_msg.message_id) + async_task(audio_task, bot_msg.chat.id, bot_msg.id) + # audio_task.delay(bot_msg.chat.id, bot_msg.id) else: normal_audio(client, bot_msg) @@ -205,7 +206,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, logging.info("Downloaded file %s", filename) st_size = os.stat(filepath).st_size - client.send_chat_action(chat_id, "upload_document") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) client.send_document( bot_msg.chat.id, filepath, @@ -224,11 +225,11 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor ) orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: - client.send_chat_action(chat_id, "record_audio") + client.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) # just try to download the audio using yt-dlp filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") - client.send_chat_action(chat_id, "upload_audio") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) for f in filepath: client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") @@ -264,18 +265,18 @@ def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, ty video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") - client.send_chat_action(chat_id, "upload_document") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) bot_msg.edit_text("Download complete. Sending now...") try: upload_processor(client, bot_msg, url, video_paths) - except pyrogram.errors.Flood as e: + except pyrogram.errors.FloodWait as e: logging.critical("FloodWait from Telegram: %s", e) client.send_message( chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", + f"I'm being rate limited by Telegram. Your video will come after {e.value} seconds. Please wait patiently.", ) flood_owner_message(client, e) - time.sleep(e.x) + time.sleep(e.value) upload_processor(client, bot_msg, url, video_paths) bot_msg.edit_text("Download success!โœ…") @@ -407,7 +408,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): redis.add_send_cache(unique, getattr(obj, "file_id", None)) redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.message_id) + client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) return res_msg @@ -449,14 +450,8 @@ def gen_cap(bm, url, video_path): def gen_video_markup(): markup = InlineKeyboardMarkup( - [ - [ # First row - InlineKeyboardButton( # Generates a callback query when pressed - "convert to audio", callback_data="convert" - ) - ] - ] - ) + [[InlineKeyboardButton("convert to audio", callback_data="convert")]] + ) # First row # Generates a callback query when pressed return markup @@ -506,28 +501,43 @@ def async_task(task_name, *args): task_name.apply_async(args=args, queue=destination) -def run_celery(): - worker_name = os.getenv("WORKER_NAME", "") - argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name] - if ENABLE_QUEUE: - argv.extend(["-Q", worker_name]) - app.worker_main(argv) - - def purge_tasks(): count = app.control.purge() return f"purged {count} tasks." +def run_celery(): + # ๅˆ›ๅปบไธ€ไธชๆ–ฐ็š„ไบ‹ไปถๅพช็Žฏ + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + worker_name = os.getenv("WORKER_NAME", "") + argv = [ + "-A", + "tasks", + "worker", + "--loglevel=info", + "--pool=threads", + f"--concurrency={WORKERS}", + "-n", + worker_name, + ] + if ENABLE_QUEUE: + argv.extend(["-Q", worker_name]) + app.worker_main(argv) + except: + logging.warning("Celery worker failed to start.") + sys.exit(1) + + if __name__ == "__main__": - # celery_client.start() print("Bootstrapping Celery worker now.....") time.sleep(5) threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Asia/Shanghai") - scheduler.add_job(auto_restart, "interval", seconds=900) + scheduler.add_job(auto_restart, "interval", seconds=120) scheduler.start() idle() - celery_client.stop() + bot.stop() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 1c481725..4d806172 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -12,11 +12,13 @@ import logging import os import pathlib +import re import shutil import subprocess import tempfile import time import uuid +from datetime import datetime import coloredlogs import ffmpeg @@ -189,6 +191,21 @@ def next_salt_detector(self): # logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) # return True + def fail_connect_detector(self): + # TODO: don't know why sometimes it stops connected to DC + last_line = self.logs.strip().split("\n")[-1] + try: + log_time_str = re.findall(r"\[(.*),", last_line)[0] + log_time = datetime.strptime(log_time_str, "%Y-%m-%d %H:%M:%S") + except Exception: + return + + time_difference = (datetime.now() - log_time).total_seconds() + + if ("Sending as video" in last_line or "PingTask started" in last_line) and time_difference > 60: + logging.warning("Can't connect to Telegram DC") + return True + def auto_restart(): log_path = "/var/log/ytdl.log" diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 5fdb536a..d4eae5cf 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -22,7 +22,7 @@ import requests import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler -from pyrogram import Client, filters, types +from pyrogram import Client, enums, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions from pyrogram.raw import types as raw_types @@ -71,7 +71,7 @@ def wrapper(client: Client, message: types.Message): chat_id = getattr(message.from_user, "id", None) # message type check - if message.chat.type != "private" and not message.text.lower().startswith("/ytdl"): + if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"): logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) return @@ -114,7 +114,7 @@ def start_handler(client: Client, message: types.Message): payment = Payment() from_id = message.from_user.id logging.info("Welcome to youtube-dl bot!") - client.send_chat_action(from_id, "typing") + client.send_chat_action(from_id, enums.ChatAction.TYPING) is_old_user = payment.check_old_user(from_id) if is_old_user: info = "" @@ -130,21 +130,21 @@ def start_handler(client: Client, message: types.Message): @app.on_message(filters.command(["help"])) def help_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, BotText.help, disable_web_page_preview=True) @app.on_message(filters.command(["about"])) def about_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, BotText.about) @app.on_message(filters.command(["sub"])) def subscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) if message.text == "/sub": result = channel.get_user_subscription(chat_id) else: @@ -159,7 +159,7 @@ def subscribe_handler(client: Client, message: types.Message): @app.on_message(filters.command(["unsub"])) def unsubscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) text = message.text.split(" ") if len(text) == 1: client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True) @@ -179,7 +179,7 @@ def patch_handler(client: Client, message: types.Message): chat_id = message.chat.id if username == OWNER: celery_app.control.broadcast("hot_patch") - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, "Oorah!") hot_patch() @@ -203,7 +203,7 @@ def purge_handler(client: Client, message: types.Message): @app.on_message(filters.command(["ping"])) def ping_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): bot_info = "ping unavailable." else: @@ -229,7 +229,7 @@ def sub_count_handler(client: Client, message: types.Message): @app.on_message(filters.command(["direct"])) def direct_handler(client: Client, message: types.Message): chat_id = message.from_user.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) url = re.sub(r"/direct\s*", "", message.text) logging.info("direct start %s", url) if not re.findall(r"^https?://", url.lower()): @@ -246,7 +246,7 @@ def direct_handler(client: Client, message: types.Message): def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id payment = Payment() - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) data = MySQL().get_user_settings(chat_id) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") @@ -285,7 +285,7 @@ def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id text = message.text.strip() - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, BotText.buy, disable_web_page_preview=True) # generate telegram invoice here payload = f"{message.chat.id}-buy" @@ -299,7 +299,7 @@ def buy_handler(client: Client, message: types.Message): price, f"Buy {TOKEN_PRICE} download tokens", "You can pay by Telegram payment or using link above", payload ) - app.send( + app.invoke( functions.messages.SendMedia( peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), media=invoice, @@ -369,7 +369,7 @@ def link_checker(url: str) -> str: def download_handler(client: Client, message: types.Message): payment = Payment() chat_id = message.from_user.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) redis.user_count(chat_id) if message.document: with tempfile.NamedTemporaryFile(mode="r+") as tf: @@ -412,19 +412,21 @@ def download_handler(client: Client, message: types.Message): try: # raise pyrogram.errors.exceptions.FloodWait(10) bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) - except pyrogram.errors.Flood as e: + except pyrogram.errors.FloodWait as e: f = BytesIO() f.write(str(e).encode()) f.write(b"Your job will be done soon. Just wait! Don't rush.") f.name = "Please don't flood me.txt" bot_msg = message.reply_document( - f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True + f, + caption=f"Flood wait! Please wait {e.value} seconds...." f"Your job will start automatically", + quote=True, ) f.close() - client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") - time.sleep(e.x) + client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.value} seconds....") + time.sleep(e.value) - client.send_chat_action(chat_id, "upload_video") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) bot_msg.chat = message.chat ytdl_download_entrance(client, bot_msg, url) @@ -493,7 +495,7 @@ def raw_update(client: Client, update, users, chats): payment = Payment() action = getattr(getattr(update, "message", None), "action", None) if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": - client.send( + client.invoke( functions.messages.SetBotPrecheckoutResults( query_id=update.query_id, success=True, From 32fe1a83243d8f0f555d5e4d58510f59d4e5e402 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 17 Sep 2023 18:02:37 +0200 Subject: [PATCH 120/297] upgrade yt-dlp to master version --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 86ed3642..8b15615f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==2.0.106 tgcrypto==1.2.5 -yt-dlp==2023.7.6 +git+https://github.com/yt-dlp/yt-dlp@63e0c5748c0eb461a2ccca4181616eb930b4b750 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -11,7 +11,7 @@ flower==2.0.1 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.18.0 +fakeredis==2.18.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.0 From 1ea8390f5075901a0a4e21b8e681517950129d25 Mon Sep 17 00:00:00 2001 From: Bartixxx32 Date: Sun, 17 Sep 2023 22:04:29 +0200 Subject: [PATCH 121/297] m3u8 toggle (#293) --- README.md | 1 + ytdlbot/config.py | 1 + ytdlbot/ytdl_bot.py | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d547053..63f24372 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ You can configure all the following environment variables: * ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) * PROVIDER_TOKEN: stripe token on Telegram payment * PLAYLIST_SUPPORT: download playlist support +* M3U8_SUPPORT: download m3u8 files support * ENABLE_ARIA2: enable aria2c download * FREE_DOWNLOAD: free download count per day * TOKEN_PRICE: token price per 1 USD diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 21ce655a..61aebd64 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -56,6 +56,7 @@ PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) +M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 20) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index d4eae5cf..b137ac8d 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -38,6 +38,7 @@ ENABLE_VIP, OWNER, PLAYLIST_SUPPORT, + M3U8_SUPPORT, PROVIDER_TOKEN, REQUIRED_MEMBERSHIP, TOKEN_PRICE, @@ -356,7 +357,9 @@ def link_checker(url: str) -> str: ): return "Playlist or channel links are disabled." - if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): + if not M3U8_SUPPORT and ( + re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()) + ): return "m3u8 links are disabled." with contextlib.suppress(yt_dlp.utils.DownloadError): From 880bc8f9a680165619cd9901d8129330083d0fd6 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 20 Sep 2023 01:02:03 +0530 Subject: [PATCH 122/297] option for setting custom tmpfile(download) path (#294) --- README.md | 2 ++ ytdlbot/config.py | 6 ++++++ ytdlbot/tasks.py | 7 ++++--- ytdlbot/utils.py | 6 ++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 63f24372..91b74e1d 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,8 @@ You can configure all the following environment variables: * TOKEN_PRICE: token price per 1 USD * GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. * RCLONE_PATH: rclone path to upload files to cloud storage +* TMPFILE_PATH: tmpfile path(file download path) + ## 3.2 Set up init data If you only need basic functionality, you can skip this step. diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 61aebd64..dfa4f522 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -66,3 +66,9 @@ SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") RCLONE_PATH = os.getenv("RCLONE") + +# This will set the value for the tmpfile path(download path) if it is set. +# If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. +# Please ensure that the directory exists and you have necessary permissions to write to it. +# If you don't know what this is just leave it as it is. +TMPFILE_PATH = os.getenv("TMPFILE") \ No newline at end of file diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 99f71fdc..1712a496 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -48,6 +48,7 @@ RATE_LIMIT, RCLONE_PATH, WORKERS, + TMPFILE_PATH ) from constant import BotText from database import Redis @@ -193,7 +194,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, if not filename: filename = quote_plus(url) - with tempfile.TemporaryDirectory(prefix="ytdl-") as f: + with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as f: filepath = f"{f}/{filename}" # consume the req.content downloaded = 0 @@ -224,7 +225,7 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor "Converting to audio...please wait patiently", quote=True ) orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] - with tempfile.TemporaryDirectory(prefix="ytdl-") as tmp: + with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp: client.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) # just try to download the audio using yt-dlp filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") @@ -261,7 +262,7 @@ def flood_owner_message(client, ex): def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): chat_id = bot_msg.chat.id - temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-") + temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 4d806172..4f692581 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -26,6 +26,8 @@ from flower_tasks import app +from config import TMPFILE_PATH + inspect = app.control.inspect() @@ -219,14 +221,14 @@ def auto_restart(): for method in method_list: if method(): logging.critical("Bye bye world!โ˜ ๏ธ") - for item in pathlib.Path(tempfile.gettempdir()).glob("ytdl-*"): + for item in pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()).glob("ytdl-*"): shutil.rmtree(item, ignore_errors=True) psutil.Process().kill() def clean_tempfile(): - for item in pathlib.Path(tempfile.gettempdir()).glob("ytdl-*"): + for item in pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()).glob("ytdl-*"): if time.time() - item.stat().st_ctime > 3600: shutil.rmtree(item, ignore_errors=True) From 9191b4edadaac93dc7a120f5c33224b341bbaa68 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 7 Oct 2023 00:57:16 +0530 Subject: [PATCH 123/297] Update to new yt-dlp version (yt-dlp==2023.9.24) (#297) * option for setting custom tmpfile(download) path * new yt-dlp version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8b15615f..97c7c51a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==2.0.106 tgcrypto==1.2.5 -git+https://github.com/yt-dlp/yt-dlp@63e0c5748c0eb461a2ccca4181616eb930b4b750 +yt-dlp==2023.9.24 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 82ba992a8407468b5b394375e100debbb1a8639e Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 9 Oct 2023 17:45:49 +0200 Subject: [PATCH 124/297] rollback to pyrogram1 --- requirements.txt | 8 ++-- ytdlbot/database.py | 2 - ytdlbot/downloader.py | 11 +---- ytdlbot/tasks.py | 96 +++++++++++++++++++------------------------ ytdlbot/utils.py | 20 +-------- ytdlbot/ytdl_bot.py | 46 ++++++++++----------- 6 files changed, 71 insertions(+), 112 deletions(-) diff --git a/requirements.txt b/requirements.txt index 97c7c51a..2cbe803d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -pyrogram==2.0.106 +pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.9.24 +yt-dlp==2023.10.7 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -11,10 +11,10 @@ flower==2.0.1 psutil==5.9.5 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.18.1 +fakeredis==2.19.0 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==5.0.0 +redis==5.0.1 requests==2.31.0 tqdm==4.66.1 requests-toolbelt==1.0.0 diff --git a/ytdlbot/database.py b/ytdlbot/database.py index db721149..f7873978 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -254,9 +254,7 @@ def __init__(self): self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) - logging.debug("Used real MySQL connection.") except pymysql.err.OperationalError: - logging.warning("Using fake MySQL connection.") self.con = FakeMySQL() self.con.ping(reconnect=True) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 51d66a4a..965b3abe 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,14 +26,7 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import ( - AUDIO_FORMAT, - ENABLE_ARIA2, - ENABLE_FFMPEG, - SS_YOUTUBE, - TG_MAX_SIZE, - IPv6, -) +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, SS_YOUTUBE from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -42,7 +35,7 @@ def edit_text(bot_msg, text: str): - key = f"{bot_msg.chat.id}-{bot_msg.id}" + key = f"{bot_msg.chat.id}-{bot_msg.message_id}" # if the key exists, we shouldn't send edit message if not r.exists(key): time.sleep(random.random()) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 1712a496..16cedfb2 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -7,7 +7,6 @@ __author__ = "Benny " -import asyncio import logging import math import os @@ -16,7 +15,6 @@ import re import shutil import subprocess -import sys import tempfile import threading import time @@ -32,7 +30,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel -from pyrogram import Client, enums, idle, types +from pyrogram import Client, idle, types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor @@ -45,10 +43,10 @@ ENABLE_QUEUE, ENABLE_VIP, OWNER, - RATE_LIMIT, RCLONE_PATH, + RATE_LIMIT, WORKERS, - TMPFILE_PATH + TMPFILE_PATH, ) from constant import BotText from database import Redis @@ -74,23 +72,24 @@ redis = Redis() channel = Channel() -bot = create_app("ytdl-celery") +session = "ytdl-celery" +celery_client = create_app(session) def get_messages(chat_id, message_id): try: - return bot.get_messages(chat_id, message_id) + return celery_client.get_messages(chat_id, message_id) except ConnectionError as e: logging.critical("WTH!!! %s", e) - bot.start() - return bot.get_messages(chat_id, message_id) + celery_client.start() + return celery_client.get_messages(chat_id, message_id) @app.task(rate_limit=f"{RATE_LIMIT}/m") def ytdl_download_task(chat_id, message_id, url: str): logging.info("YouTube celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - ytdl_normal_download(bot, bot_msg, url) + ytdl_normal_download(celery_client, bot_msg, url) logging.info("YouTube celery tasks ended.") @@ -98,7 +97,7 @@ def ytdl_download_task(chat_id, message_id, url: str): def audio_task(chat_id, message_id): logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) bot_msg = get_messages(chat_id, message_id) - normal_audio(bot, bot_msg) + normal_audio(celery_client, bot_msg) logging.info("Audio celery tasks ended.") @@ -118,7 +117,7 @@ def get_unique_clink(original_url: str, user_id: int): def direct_download_task(chat_id, message_id, url): logging.info("Direct download celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - direct_normal_download(bot, bot_msg, url) + direct_normal_download(celery_client, bot_msg, url) logging.info("Direct download celery tasks ended.") @@ -148,8 +147,8 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod return mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: - async_task(ytdl_download_task, chat_id, bot_msg.id, url) - # ytdl_download_task.delay(chat_id, bot_msg.id, url) + async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) + # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) else: ytdl_normal_download(client, bot_msg, url) except Exception as e: @@ -160,15 +159,15 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): if ENABLE_CELERY: direct_normal_download(client, bot_msg, url) - # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) + # direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url) else: direct_normal_download(client, bot_msg, url) def audio_entrance(client, bot_msg): if ENABLE_CELERY: - async_task(audio_task, bot_msg.chat.id, bot_msg.id) - # audio_task.delay(bot_msg.chat.id, bot_msg.id) + async_task(audio_task, bot_msg.chat.id, bot_msg.message_id) + # audio_task.delay(bot_msg.chat.id, bot_msg.message_id) else: normal_audio(client, bot_msg) @@ -207,7 +206,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, logging.info("Downloaded file %s", filename) st_size = os.stat(filepath).st_size - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + client.send_chat_action(chat_id, "upload_document") client.send_document( bot_msg.chat.id, filepath, @@ -226,11 +225,11 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor ) orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp: - client.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) + client.send_chat_action(chat_id, "record_audio") # just try to download the audio using yt-dlp filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) + client.send_chat_action(chat_id, "upload_audio") for f in filepath: client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") @@ -266,18 +265,18 @@ def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, ty video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + client.send_chat_action(chat_id, "upload_document") bot_msg.edit_text("Download complete. Sending now...") try: upload_processor(client, bot_msg, url, video_paths) - except pyrogram.errors.FloodWait as e: + except pyrogram.errors.Flood as e: logging.critical("FloodWait from Telegram: %s", e) client.send_message( chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e.value} seconds. Please wait patiently.", + f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", ) flood_owner_message(client, e) - time.sleep(e.value) + time.sleep(e.x) upload_processor(client, bot_msg, url, video_paths) bot_msg.edit_text("Download success!โœ…") @@ -409,7 +408,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): redis.add_send_cache(unique, getattr(obj, "file_id", None)) redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) + client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.message_id) return res_msg @@ -451,8 +450,14 @@ def gen_cap(bm, url, video_path): def gen_video_markup(): markup = InlineKeyboardMarkup( - [[InlineKeyboardButton("convert to audio", callback_data="convert")]] - ) # First row # Generates a callback query when pressed + [ + [ # First row + InlineKeyboardButton( # Generates a callback query when pressed + "convert to audio", callback_data="convert" + ) + ] + ] + ) return markup @@ -502,43 +507,28 @@ def async_task(task_name, *args): task_name.apply_async(args=args, queue=destination) +def run_celery(): + worker_name = os.getenv("WORKER_NAME", "") + argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name] + if ENABLE_QUEUE: + argv.extend(["-Q", worker_name]) + app.worker_main(argv) + + def purge_tasks(): count = app.control.purge() return f"purged {count} tasks." -def run_celery(): - # ๅˆ›ๅปบไธ€ไธชๆ–ฐ็š„ไบ‹ไปถๅพช็Žฏ - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - worker_name = os.getenv("WORKER_NAME", "") - argv = [ - "-A", - "tasks", - "worker", - "--loglevel=info", - "--pool=threads", - f"--concurrency={WORKERS}", - "-n", - worker_name, - ] - if ENABLE_QUEUE: - argv.extend(["-Q", worker_name]) - app.worker_main(argv) - except: - logging.warning("Celery worker failed to start.") - sys.exit(1) - - if __name__ == "__main__": + # celery_client.start() print("Bootstrapping Celery worker now.....") time.sleep(5) threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Asia/Shanghai") - scheduler.add_job(auto_restart, "interval", seconds=120) + scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() - bot.stop() + celery_client.stop() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 4f692581..66ddc289 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -12,21 +12,18 @@ import logging import os import pathlib -import re import shutil import subprocess import tempfile import time import uuid -from datetime import datetime import coloredlogs import ffmpeg import psutil -from flower_tasks import app - from config import TMPFILE_PATH +from flower_tasks import app inspect = app.control.inspect() @@ -193,21 +190,6 @@ def next_salt_detector(self): # logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) # return True - def fail_connect_detector(self): - # TODO: don't know why sometimes it stops connected to DC - last_line = self.logs.strip().split("\n")[-1] - try: - log_time_str = re.findall(r"\[(.*),", last_line)[0] - log_time = datetime.strptime(log_time_str, "%Y-%m-%d %H:%M:%S") - except Exception: - return - - time_difference = (datetime.now() - log_time).total_seconds() - - if ("Sending as video" in last_line or "PingTask started" in last_line) and time_difference > 60: - logging.warning("Can't connect to Telegram DC") - return True - def auto_restart(): log_path = "/var/log/ytdl.log" diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index b137ac8d..6496c1c8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -22,7 +22,7 @@ import requests import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler -from pyrogram import Client, enums, filters, types +from pyrogram import Client, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions from pyrogram.raw import types as raw_types @@ -72,7 +72,7 @@ def wrapper(client: Client, message: types.Message): chat_id = getattr(message.from_user, "id", None) # message type check - if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"): + if message.chat.type != "private" and not message.text.lower().startswith("/ytdl"): logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) return @@ -115,7 +115,7 @@ def start_handler(client: Client, message: types.Message): payment = Payment() from_id = message.from_user.id logging.info("Welcome to youtube-dl bot!") - client.send_chat_action(from_id, enums.ChatAction.TYPING) + client.send_chat_action(from_id, "typing") is_old_user = payment.check_old_user(from_id) if is_old_user: info = "" @@ -131,21 +131,21 @@ def start_handler(client: Client, message: types.Message): @app.on_message(filters.command(["help"])) def help_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.help, disable_web_page_preview=True) @app.on_message(filters.command(["about"])) def about_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.about) @app.on_message(filters.command(["sub"])) def subscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") if message.text == "/sub": result = channel.get_user_subscription(chat_id) else: @@ -160,7 +160,7 @@ def subscribe_handler(client: Client, message: types.Message): @app.on_message(filters.command(["unsub"])) def unsubscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") text = message.text.split(" ") if len(text) == 1: client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True) @@ -180,7 +180,7 @@ def patch_handler(client: Client, message: types.Message): chat_id = message.chat.id if username == OWNER: celery_app.control.broadcast("hot_patch") - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") client.send_message(chat_id, "Oorah!") hot_patch() @@ -204,7 +204,7 @@ def purge_handler(client: Client, message: types.Message): @app.on_message(filters.command(["ping"])) def ping_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): bot_info = "ping unavailable." else: @@ -230,7 +230,7 @@ def sub_count_handler(client: Client, message: types.Message): @app.on_message(filters.command(["direct"])) def direct_handler(client: Client, message: types.Message): chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") url = re.sub(r"/direct\s*", "", message.text) logging.info("direct start %s", url) if not re.findall(r"^https?://", url.lower()): @@ -247,7 +247,7 @@ def direct_handler(client: Client, message: types.Message): def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id payment = Payment() - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") data = MySQL().get_user_settings(chat_id) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") @@ -286,7 +286,7 @@ def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id text = message.text.strip() - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.buy, disable_web_page_preview=True) # generate telegram invoice here payload = f"{message.chat.id}-buy" @@ -300,7 +300,7 @@ def buy_handler(client: Client, message: types.Message): price, f"Buy {TOKEN_PRICE} download tokens", "You can pay by Telegram payment or using link above", payload ) - app.invoke( + app.send( functions.messages.SendMedia( peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), media=invoice, @@ -357,9 +357,7 @@ def link_checker(url: str) -> str: ): return "Playlist or channel links are disabled." - if not M3U8_SUPPORT and ( - re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()) - ): + if not M3U8_SUPPORT and (re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower())): return "m3u8 links are disabled." with contextlib.suppress(yt_dlp.utils.DownloadError): @@ -372,7 +370,7 @@ def link_checker(url: str) -> str: def download_handler(client: Client, message: types.Message): payment = Payment() chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_chat_action(chat_id, "typing") redis.user_count(chat_id) if message.document: with tempfile.NamedTemporaryFile(mode="r+") as tf: @@ -415,21 +413,19 @@ def download_handler(client: Client, message: types.Message): try: # raise pyrogram.errors.exceptions.FloodWait(10) bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) - except pyrogram.errors.FloodWait as e: + except pyrogram.errors.Flood as e: f = BytesIO() f.write(str(e).encode()) f.write(b"Your job will be done soon. Just wait! Don't rush.") f.name = "Please don't flood me.txt" bot_msg = message.reply_document( - f, - caption=f"Flood wait! Please wait {e.value} seconds...." f"Your job will start automatically", - quote=True, + f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True ) f.close() - client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.value} seconds....") - time.sleep(e.value) + client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") + time.sleep(e.x) - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) + client.send_chat_action(chat_id, "upload_video") bot_msg.chat = message.chat ytdl_download_entrance(client, bot_msg, url) @@ -498,7 +494,7 @@ def raw_update(client: Client, update, users, chats): payment = Payment() action = getattr(getattr(update, "message", None), "action", None) if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": - client.invoke( + client.send( functions.messages.SetBotPrecheckoutResults( query_id=update.query_id, success=True, From 6bce8b92dc209e34b2399d6740f9a9f429cfef19 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 15 Oct 2023 18:13:59 +0530 Subject: [PATCH 125/297] Always use the `source` format for Google Drive URLs (#300) Always using the `source` format for Google Drive URLs will ensure getting original file, without any modifications. --- ytdlbot/downloader.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 965b3abe..33b2ff83 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -169,12 +169,17 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: "--max-concurrent-downloads=16", "--split=16", ] - formats = [ - # webm , vp9 and av01 are not streamable on telegram, so we'll extract mp4 and not av01 codec - "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", - "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", - None, - ] + if url.startswith("https://drive.google.com"): + # Always use the `source` format for Google Drive URLs. + formats = ["source"] + else: + # Use the default formats for other URLs. + formats = [ + # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 + "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", + "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", + None, + ] adjust_formats(chat_id, url, formats, hijack) if download_instagram(url, tempdir): return list(pathlib.Path(tempdir).glob("*")) From 4962cb4f2c2c717c20ea1df0ad694084483bb590 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 20 Oct 2023 18:11:51 +0200 Subject: [PATCH 126/297] bump --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2cbe803d..4fbf0c9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.10.7 +yt-dlp==2023.10.13 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -8,7 +8,7 @@ PyMySQL==1.1.0 celery==5.3.4 filetype==1.2.0 flower==2.0.1 -psutil==5.9.5 +psutil==5.9.6 influxdb==5.3.1 beautifulsoup4==4.12.2 fakeredis==2.19.0 From c40ef74f8f9879733824d4aea7b5ecc125a78691 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 21 Oct 2023 10:10:27 +0200 Subject: [PATCH 127/297] add msg_id is too low --- conf/supervisor_main.conf | 6 +++--- ytdlbot/utils.py | 18 +++++++++++++----- ytdlbot/ytdl_bot.py | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/conf/supervisor_main.conf b/conf/supervisor_main.conf index cd2130cd..8c4bb9f8 100644 --- a/conf/supervisor_main.conf +++ b/conf/supervisor_main.conf @@ -16,8 +16,8 @@ command=python ytdl_bot.py autorestart=true priority=900 stopasgroup=true -startsecs = 5 -startretries = 5 +startsecs = 10 +startretries = 3 redirect_stderr=true stdout_logfile_maxbytes = 50MB @@ -31,4 +31,4 @@ priority=999 redirect_stderr=true stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 \ No newline at end of file +stdout_logfile_maxbytes=0 diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 66ddc289..9840847b 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -173,14 +173,22 @@ def updates_too_long_detector(self): ] for indicator in indicators: if indicator in self.logs: - logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) + logging.critical("kick out crash: %s", self.func_name()) return True logging.debug("No crash detected.") def next_salt_detector(self): text = "Next salt in" if self.logs.count(text) >= 4: - logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) + logging.critical("Next salt crash: %s", self.func_name()) + return True + + def msg_id_detector(self): + text = "The msg_id is too low" + if text in self.logs: + logging.critical("msg id crash: %s ", self.func_name()) + for item in pathlib.Path("/ytdlbot/ytdlbot").glob("*.session"): + item.unlink(missing_ok=True) return True # def idle_detector(self): @@ -196,16 +204,16 @@ def auto_restart(): if not os.path.exists(log_path): return with open(log_path) as f: - logs = "".join(tail_log(f, lines=10)) + logs = "".join(tail_log(f, lines=50)) det = Detector(logs) method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] for method in method_list: if method(): - logging.critical("Bye bye world!โ˜ ๏ธ") + logging.critical("%s bye bye world!โ˜ ๏ธ", method) for item in pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()).glob("ytdl-*"): shutil.rmtree(item, ignore_errors=True) - + time.sleep(5) psutil.Process().kill() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 6496c1c8..8261770a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -114,7 +114,7 @@ def wrapper(client: Client, message: types.Message): def start_handler(client: Client, message: types.Message): payment = Payment() from_id = message.from_user.id - logging.info("Welcome to youtube-dl bot!") + logging.info("%s welcome to youtube-dl bot!", message.from_user.id) client.send_chat_action(from_id, "typing") is_old_user = payment.check_old_user(from_id) if is_old_user: From 1cf4261be4e862b9a6c18e9997e324c95813a6d1 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:54:42 +0530 Subject: [PATCH 128/297] Temporary fix for 'msg_id is too low' error and small fix for getting path of session file in `utils.py` (#305) * Always use the `source` format for Google Drive URLs Always using the `source` format for Google Drive URLs will ensure getting original file, without any modifications. * Temporary fix for 'msg_id is too low' error * small fix for getting path of session file --- ytdlbot/utils.py | 2 +- ytdlbot/ytdl_bot.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 9840847b..3492e18b 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -187,7 +187,7 @@ def msg_id_detector(self): text = "The msg_id is too low" if text in self.logs: logging.critical("msg id crash: %s ", self.func_name()) - for item in pathlib.Path("/ytdlbot/ytdlbot").glob("*.session"): + for item in pathlib.Path(__file__).parent.glob("*.session"): item.unlink(missing_ok=True) return True diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 8261770a..2fb096ae 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -10,6 +10,7 @@ import contextlib import logging import os +from pathlib import Path import random import re import tempfile @@ -507,8 +508,15 @@ def raw_update(client: Client, update, users, chats): payment.add_pay_user([uid, amount, action.charge.provider_charge_id, 0, amount * TOKEN_PRICE]) client.send_message(uid, f"Thank you {uid}. Payment received: {amount} {action.currency}") +def temp_fix_The_msg_id_is_too_low(): + current_dir = Path(__file__).parent + s_file_path = current_dir / "ytdl-main.session" + if os.path.exists(s_file_path): + print(f"Deleting session file :", s_file_path) + os.remove(s_file_path) if __name__ == "__main__": + temp_fix_The_msg_id_is_too_low() MySQL() scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) From bbcc690c953ee44a169e95d2e31d5c68e76b7ec9 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 25 Oct 2023 18:26:10 +0200 Subject: [PATCH 129/297] don't call it in main --- ytdlbot/ytdl_bot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 2fb096ae..cc7713da 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -10,7 +10,6 @@ import contextlib import logging import os -from pathlib import Path import random import re import tempfile @@ -18,6 +17,7 @@ import traceback import typing from io import BytesIO +from pathlib import Path import pyrogram.errors import requests @@ -508,6 +508,7 @@ def raw_update(client: Client, update, users, chats): payment.add_pay_user([uid, amount, action.charge.provider_charge_id, 0, amount * TOKEN_PRICE]) client.send_message(uid, f"Thank you {uid}. Payment received: {amount} {action.currency}") + def temp_fix_The_msg_id_is_too_low(): current_dir = Path(__file__).parent s_file_path = current_dir / "ytdl-main.session" @@ -515,8 +516,8 @@ def temp_fix_The_msg_id_is_too_low(): print(f"Deleting session file :", s_file_path) os.remove(s_file_path) + if __name__ == "__main__": - temp_fix_The_msg_id_is_too_low() MySQL() scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) From 1e9b058676d42bdf43ab3857371c614e6e58354f Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 29 Oct 2023 11:09:21 +0100 Subject: [PATCH 130/297] add id to low script --- conf/supervisor_main.conf | 4 ++-- requirements.txt | 2 +- scripts/low_id.sh | 12 ++++++++++++ ytdlbot/utils.py | 5 +++-- 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 scripts/low_id.sh diff --git a/conf/supervisor_main.conf b/conf/supervisor_main.conf index 8c4bb9f8..dbae1442 100644 --- a/conf/supervisor_main.conf +++ b/conf/supervisor_main.conf @@ -16,8 +16,8 @@ command=python ytdl_bot.py autorestart=true priority=900 stopasgroup=true -startsecs = 10 -startretries = 3 +startsecs = 30 +startretries = 2 redirect_stderr=true stdout_logfile_maxbytes = 50MB diff --git a/requirements.txt b/requirements.txt index 4fbf0c9e..688bc891 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==2.0.1 psutil==5.9.6 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.19.0 +fakeredis==2.20.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.1 diff --git a/scripts/low_id.sh b/scripts/low_id.sh new file mode 100644 index 00000000..16fc2cef --- /dev/null +++ b/scripts/low_id.sh @@ -0,0 +1,12 @@ +#!/bin/bash +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/opt/bin + +# Check the logs for the given string +if docker-compose logs --tail=100 ytdl | grep -q "The msg_id is too low"; then + # If the string is found, stop the ytdl service + echo "ytdl service stopped due to 'The msg_id is too low' found in logs." + docker-compose stop ytdl && docker-compose rm ytdl && docker-compose up -d + +else + echo "String not found in logs." +fi diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 3492e18b..ceaa1c6d 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -187,8 +187,9 @@ def msg_id_detector(self): text = "The msg_id is too low" if text in self.logs: logging.critical("msg id crash: %s ", self.func_name()) - for item in pathlib.Path(__file__).parent.glob("*.session"): + for item in pathlib.Path(__file__).parent.glob("ytdl-*"): item.unlink(missing_ok=True) + time.sleep(3) return True # def idle_detector(self): @@ -204,7 +205,7 @@ def auto_restart(): if not os.path.exists(log_path): return with open(log_path) as f: - logs = "".join(tail_log(f, lines=50)) + logs = "".join(tail_log(f, lines=100)) det = Detector(logs) method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] From 6106b94705a52c91971a4b8d90d2f5b14692b545 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 15 Nov 2023 17:00:13 +0100 Subject: [PATCH 131/297] update yt-dlp --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 688bc891..f5bc54f9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.10.13 +yt-dlp==2023.11.14 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.1.0 -celery==5.3.4 +celery==5.3.5 filetype==1.2.0 flower==2.0.1 psutil==5.9.6 From aae0f9270cc82317d83b02975cdc0020bca53b4d Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 15 Nov 2023 17:27:26 +0100 Subject: [PATCH 132/297] drop armv7 --- .github/workflows/builder.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index 92fd95c5..958bd716 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -56,7 +56,7 @@ jobs: uses: docker/build-push-action@v4 with: context: . - platforms: linux/arm,linux/amd64,linux/arm64 + platforms: linux/amd64,linux/arm64 push: true tags: | ${{ steps.dh_string.outputs.lowercase }} From 1d499523aa8e4def3a7bf10e10b7bda4fd25e9fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:09:13 +0100 Subject: [PATCH 133/297] Bump yt-dlp from 2023.11.14 to 2023.11.16 (#310) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2023.11.14 to 2023.11.16. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2023.11.14...2023.11.16) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f5bc54f9..9a314792 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==1.4.16 tgcrypto==1.2.5 -yt-dlp==2023.11.14 +yt-dlp==2023.11.16 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 73bdf2a54d3c8a1242d9d0121418a316e51b194c Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 17 Nov 2023 21:56:00 +0100 Subject: [PATCH 134/297] double default limits --- ytdlbot/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index dfa4f522..5d09df0e 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -59,10 +59,10 @@ M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 20) +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 40) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits -RATE_LIMIT = os.getenv("RATE_LIMIT", 20) +RATE_LIMIT = os.getenv("RATE_LIMIT", 60) SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") RCLONE_PATH = os.getenv("RCLONE") @@ -71,4 +71,4 @@ # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. # Please ensure that the directory exists and you have necessary permissions to write to it. # If you don't know what this is just leave it as it is. -TMPFILE_PATH = os.getenv("TMPFILE") \ No newline at end of file +TMPFILE_PATH = os.getenv("TMPFILE") From 2b6fb38c5c9df4160e38c0654994b1c6b2b334ea Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:06:53 +0530 Subject: [PATCH 135/297] Update README.md (Improve) (#315) Improve "Run natively on your machine" guide --- README.md | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 91b74e1d..21d6e508 100644 --- a/README.md +++ b/README.md @@ -77,14 +77,39 @@ Need help with deployment or exclusive features? I offer paid service - contact To deploy this bot, follow these steps: -1. Clone the code from the repository. -2. Install FFmpeg. -3. Install Python 3.6 or a later version. -4. Install Aria2 and add it to the PATH. -5. Install the required packages by running `pip3 install -r requirements.txt`. -6. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. -7. Run `python3 ytdl_bot.py`. - +1. Install bot dependencies + * Install Python 3.6 or a later version, FFmpeg. + * Aria2 and add it to the PATH. + +2. Clone the code from the repository and cd into it. + * ```Bash + git clone "https://github.com/tgbot-collection/ytdlbot.git + ``` + * ```Bash + cd ytdlbot/ + ``` +3. Creating a virtual environment and installing required modules in Python. + * ```Python + python -m venv venv + ``` + * ```Bash + source venv/bin/activate #Linux + #or + .\venv\Scripts\activate #Windows + ``` + * ```Python + pip install --upgrade pip + ``` + * ```Python + pip install -r requirements.txt + ``` +4. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. + * Change values in ytdlbot/config.py or + * Use export APP_ID=111 APP_HASH=111 TOKEN=123 +5. Finally run the bot with + * ```Python + python ytdl_bot.py + ``` ## Docker This bot has a simple one-line code and some functions, such as VIP and ping, are disabled. From 37f1d4c2ba6ec76670583516419f0c2659dfdfcd Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 7 Dec 2023 16:28:32 +0100 Subject: [PATCH 136/297] Update config.py --- ytdlbot/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 5d09df0e..1d55aa67 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -59,10 +59,10 @@ M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 40) +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 20) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits -RATE_LIMIT = os.getenv("RATE_LIMIT", 60) +RATE_LIMIT = os.getenv("RATE_LIMIT", 120) SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") RCLONE_PATH = os.getenv("RCLONE") From a09cce485687eacabfcd085e941cf485bc11bcf1 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 8 Dec 2023 18:56:57 +0100 Subject: [PATCH 137/297] Tron (#318) * add tron support * update README * update README * add notification --- FAQ.md | 25 ------------ README.md | 81 +++++++++++++++++++------------------- assets/SEK.png | Bin 58480 -> 0 bytes assets/tron.png | Bin 0 -> 14975 bytes requirements.txt | 6 ++- scripts/transfer.py | 28 ++++++++++++++ ytdlbot/config.py | 26 ++++++++----- ytdlbot/constant.py | 47 ++++++++++------------ ytdlbot/downloader.py | 9 ++++- ytdlbot/limit.py | 88 ++++++++++++++++++++++++++++++++++++++++++ ytdlbot/tasks.py | 4 +- ytdlbot/ytdl_bot.py | 30 ++++++++++---- 12 files changed, 231 insertions(+), 113 deletions(-) delete mode 100644 FAQ.md delete mode 100644 assets/SEK.png create mode 100644 assets/tron.png create mode 100644 scripts/transfer.py diff --git a/FAQ.md b/FAQ.md deleted file mode 100644 index 4a0f7b54..00000000 --- a/FAQ.md +++ /dev/null @@ -1,25 +0,0 @@ -# Project status - -This project is currently inactive and is no longer being maintained. - -No new features will be added, and no bugs will be fixed. - -While the existing code and bot will continue to work, there are no guarantees regarding their reliability or -functionality. - -# Can you support downloading from XXX? - -Please reach out to [yt-dlp](https://github.com/yt-dlp/yt-dlp) - -# My video is not downloading, slow, or not downloading at all - -Just wait. It is a free service, and it is not guaranteed to work. - - -# Why is the bot not responding? - -If `/start` command is not responding, please contact me [@BennyThink](https://t.me/BennyThink) - -# Can't redeem VIP - -If you are not able to redeem VIP, please contact me [@BennyThink](https://t.me/BennyThink) diff --git a/README.md b/README.md index 21d6e508..62d9ce1a 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,10 @@ [![docker image](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml/badge.svg)](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml) -YouTube Download Bot๐Ÿš€ +YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ -This Telegram bot allows you to download videos from YouTube and other supported platforms, including Instagram! +This Telegram bot allows you to download videos from YouTube and other supported websites, including Instagram! ------ -**READ [FAQ](FAQ.md) FIRST IF YOU ENCOUNTER ANY ISSUES.** - ------

Deploy to heroku Deploy to Heroku @@ -26,14 +22,14 @@ longer be available.** [https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) Send link directly to the bot. Any -Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/supportedsites.html) will work to. +Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) will work t0o. # Limitations of my bot Due to limitations on servers and bandwidth, there are some restrictions on this free service. * Each user is limited to 20 free downloads per 24-hour period -* there is a maximum of three subscriptions allowed for YouTube channels. +* Maximum of three subscriptions allowed for YouTube channels. If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your own bot. See below instructions. @@ -44,14 +40,14 @@ own bot. See below instructions. 2. ads free 3. support progress bar 4. audio conversion -5. playlist support -6. payment support -7. support different video resolutions -8. support sending as file or streaming as video -9. supports celery worker distribution - faster than before. +5. playlist download +6. payment support: afdian, buy me a coffee, Telegram Payment and Tron(TRX) +7. different video resolutions +8. sending as file or streaming as video +9. celery worker distribution - faster than before. 10. subscriptions to YouTube Channels 11. cache mechanism - download once for the same video. -12. support instagram posts +12. instagram posts # Screenshots @@ -71,19 +67,17 @@ own bot. See below instructions. This bot can be deployed on any platform that supports Python. -Need help with deployment or exclusive features? I offer paid service - contact me at @BennyThink - ## Run natively on your machine To deploy this bot, follow these steps: 1. Install bot dependencies * Install Python 3.6 or a later version, FFmpeg. - * Aria2 and add it to the PATH. + * (optional)Aria2 and add it to the PATH. 2. Clone the code from the repository and cd into it. * ```Bash - git clone "https://github.com/tgbot-collection/ytdlbot.git + git clone https://github.com/tgbot-collection/ytdlbot ``` * ```Bash cd ytdlbot/ @@ -93,9 +87,9 @@ To deploy this bot, follow these steps: python -m venv venv ``` * ```Bash - source venv/bin/activate #Linux + source venv/bin/activate # Linux #or - .\venv\Scripts\activate #Windows + .\venv\Scripts\activate # Windows ``` * ```Python pip install --upgrade pip @@ -105,14 +99,15 @@ To deploy this bot, follow these steps: ``` 4. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. * Change values in ytdlbot/config.py or - * Use export APP_ID=111 APP_HASH=111 TOKEN=123 -5. Finally run the bot with + * Use export APP_ID=111 APP_HASH=111 TOKEN=123 +5. Finally, run the bot with * ```Python python ytdl_bot.py ``` + ## Docker -This bot has a simple one-line code and some functions, such as VIP and ping, are disabled. +One line command to run the bot ```shell docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot @@ -121,7 +116,7 @@ docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot # Complete deployment guide for docker-compose * contains every functionality -* compatible with amd64, arm64 and armv7l +* compatible with amd64 and arm64 ## 1. get docker-compose.yml @@ -174,6 +169,8 @@ You can configure all the following environment variables: * GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. * RCLONE_PATH: rclone path to upload files to cloud storage * TMPFILE_PATH: tmpfile path(file download path) +* TRONGRID_KEY: TronGrid key, better use your own key to avoid rate limit +* TRON_MNEMONIC: Tron mnemonic, the default one is on nile testnet. ## 3.2 Set up init data @@ -181,7 +178,7 @@ If you only need basic functionality, you can skip this step. ### 3.2.1 Create MySQL db -Required for VIP, settings, YouTube subscription. +Required for VIP(Download token), settings, YouTube subscription. ```shell docker-compose up -d @@ -204,15 +201,11 @@ Type "help", "copyright", "credits" or "license" for more information. >>> import dbm;dbm.open("flower","n");exit() ``` -### 3.2.3 Setup instagram cookies - -You don't need to do this anymore! This bot support instagram posts out of the box, including photos, videos and reels. - ## 3.3 Tidy docker-compose.yml In `flower` service section, you may want to change your basic authentication username password and publish port. -You can also limit CPU and RAM usage by adding a `deploy' key: +You can also limit CPU and RAM usage by adding a `deploy` key, use `--compatibility` when deploying. ```docker deploy: @@ -222,8 +215,6 @@ You can also limit CPU and RAM usage by adding a `deploy' key: memory: 1500M ``` -Be sure to use `--compatibility` when deploying. - ## 4. run ### 4.1. standalone mode @@ -256,7 +247,7 @@ On the other machine: docker-compose -f worker.yml up -d ``` -**โš ๏ธ Please bear in mind that you should not publish Redis directly on the internet. +**โš ๏ธ You should not publish Redis directly on the internet. Instead, you can use WireGuard to wrap it up for added security.** ## kubernetes @@ -306,20 +297,32 @@ https://www.instagram.com/p/CZtUDyyv1u1/ # Donation +Found this bot useful? You can donate to support the development of this bot. + +## Donation Platforms + * [Buy me a coffee](https://www.buymeacoffee.com/bennythink) * [Afdian](https://afdian.net/@BennyThink) * [GitHub Sponsor](https://github.com/sponsors/BennyThink) ## Stripe -You can choose to donate via Stripe by clicking the button below. +You can choose to donate via Stripe. + +| USD(Card, Apple Pay and Google Pay) | CNY(Card, Apple Pay, Google Pay and Alipay) | +|--------------------------------------------------|--------------------------------------------------| +| [USD](https://buy.stripe.com/cN203sdZB98RevC3cd) | [CNY](https://buy.stripe.com/dR67vU4p13Ox73a6oq) | +| ![](assets/USD.png) | ![](assets/CNY.png) | + +## Cryptocurrency -Select the currency and payment method that suits you. +TRX or USDT(TRC20) -| USD(Card, Apple Pay and Google Pay) | SEK(Card, Apple Pay and Google Pay) | CNY(Card, Apple Pay, Google Pay and Alipay) | -|--------------------------------------------------|--------------------------------------------------|--------------------------------------------------| -| [USD](https://buy.stripe.com/cN203sdZB98RevC3cd) | [SEK](https://buy.stripe.com/bIYbMa9JletbevCaEE) | [CNY](https://buy.stripe.com/dR67vU4p13Ox73a6oq) | -| ![](assets/USD.png) | ![](assets/SEK.png) | ![](assets/CNY.png) | +![](assets/tron.png) + +``` +TF9peZjC2FYjU4xNMPg3uP4caYLJxtXeJS +``` # License diff --git a/assets/SEK.png b/assets/SEK.png deleted file mode 100644 index 9e99f652b987c442462a85eabbccb029e2f8bef3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58480 zcmeFac|6q7_djmNmaJ*9FYQWMlE^N~o+V3m5z2&-B*rq6N(pUh$}UT`K{SkIm=cOm zGM2GT3EB4ivFyexLW}@%wxq-|zR+UwZJm_nvd^Ip>~x&hy-E8J*N(-^jm_ zj*gC9M_bc`j*cEqN5>$tfeHM^WSq4H{D63yXdR==X%HNwqZ6gm(Ns6RVmE?!fA7+6 z7GGpYonaF>wt;QitDB-~ZA@2<2q_FZ$k(`c6((&oNlCLl6fL~H^GTAj^M%p3PB24$E5vxqjZ$lYMr=z%lugdl-J%c{Z8a$OT_nRUQIz5)TsH}h z#ZK0?krFsCc<*zgLaj9v3Ybp=);rE#Ine^<3lzwy=v!#(UMdM4pC`85c+TIly_C$O z8XQ2v!dg{|=fU8r?Cu!g6oWu>2+l@j+&tUo0T-LN!$hSzSI(C4knX9OGP z?t*q!v05awl4j9`UURW{;=IOhG^#qSt98;uCAlv)E$)S%Ho=yJHdr(jP=Maq*I} zauXD(4|5S+6PJ%|<|dDoPJEpX^_uH%A)R9R&=R$7mh71}dQttJ zj+$z(&^_JIQ?W2~<{VG*bt}W)X;Rw_?QN4#9t>%~f3dFcxpG+7_cXF}b?KoYUkyb~ zfK;P(t03|Z5jQbZQ&=nCj|G!Il?E#~3T>we_fEV>&8Jc;nClKrOO4;xAcSqO5xFtR z$Z1nP@TnLUEku^+M$Fccq{=Vma3!FB=OE7w+B=c2cPDo7(NS3JR=_WHsFkg94(rV@ zm;DDJ7%twLPko#!7srOva(pi$Wy%bWk9q$lL&77LcPw5Hps}lMUmlJ2lYY0Uc7{DG zy;01}INw!fJ&DF`7k;Y4cWV&X161(3#%kprW=34iYC5u|n;h(Zu~Nc&@V8~uwxEVJ zV2ww=+~I9gBnT~U?+RX8v|IkM64SS?GdRc|b0va_+ zYsc8&k^!IQqGT*`Ryu&H_->tE{X^2CC>@6G%gbZl!Gk`z0^LE+p7}E2Zu8^>SPOLM z?a`Uo{^tU1cL?w80tctM><=9!*p^X;yh&0k%|V{i7Jm@EOX@a3K42(St3hZPy=u5L z9BEIMh_6_Xv=&%hH~>*O@TZs&+`K{Lrpkep>X(qQ&)2gWVZC!2i1Mn0-yI6~T{Q;O znYj@?bmQ`bPU^{JsPE*fVu+3N|UUeRnrzLmatJzrt%(>Ke@|OMFj^9ek!dv z=ZZvpz>fzE&3+o|X|Kqs_`@1SQRkqp)#4&kB(3rD>E*Z2k_o61petGm(Re=7V zf>ML}#vP?5<@%kwGTgh$(YZ`jp~zK#TA+rE7rgj$1N3)Eg}bUyQCedQXgO%-^(=dt zg@FnK&3_>BG_+ExYzb92P(DUGAC{6=pJG3;wpm!&{<(hICf8isotx z`q|d0eSdC|8VlQKk4;GcwT1%|Q5i_J!yjRjTh)flRBihQ3lG@zNN4a$tw2qSigd32!#@bPOL$Le6k1k!JXaie7=!0k!cA3B`p1$BZ|AOQVB7%a6zo)s}33iE^+Vge|G3#xS@5YoL~#X#I+yUVDaZ@Wz?`1#6w^56zWgOyk~PrbO zTe!q4yx?3Qx2Q}svX9cM$;2SY&8%!c4Ea||;my(|^e^9hqtW*{32wn+%KN)X1K^7}pZm_PQbX3R|_W0Me1x?FYEk@W^ba$^%u$UJVe>ONPb(~%Weokc3 zOJni2h~kSoE02?uXEl~C6vFia@assIhI%f>JzpM@yffOf+|(8{$aANhH2=gyCxn;0 zEpc^*Q-oBY>US<-Vnk+?PvpFVbs}TA$uA3Y*nyH;U1$qhiD>N*2zI&PId4$esTux2 zM228+!nZBNFlerIFKJ9=u`B~6s|HaAG>4n}^TxzfvkRn&d zoyBWO%H!S*g?EA<*>{M(e)RBHh@h=a=IK6MD6;YrHp&y+EvogRhK@&nHaY(2iwcUL zYezx6JDl|d?C38GK^&5t-p}a5HKYRIe=-HT(T_9*3&S}veay7{IzDQat0LE9Y`&H&_(_2gR5I`_@$$?-rTcs?_^@#W=1Ss;S1ons#9jWie-M@^ z(TOfjdzk7>ZVvjr?yb%MMgE(>_Vr8{UNrh=UH@_o8t%@@wvV@FpXzfGg>{HPjm zE_W_;rAF`Rktm<_HleopN|oxBYGfV~LzfsE)FppMgS?LwIFfEeYc>%fjV)e6r>0A*YAzALh?C<2_W`tTvhAXd+ ztV-9`GvSgaE^qn#$2~s%IC<}C?(k@nYFEF=5Wby`YLHu&MLV^qJ#Ox1#Pyn0DV#n>Ugzbn2kg?6H1u~<0_Wc#B zM=Ir1) z-@`I2i{P>`R4X`z@Mwi(gIvU|UOHz_p|ZoR4;91KortIRqCDrd?qKbgg4yB2BV(l& zvx5nKY50lT%R3p&yUy~g3%BxEpt*|;u+hCi7p#M$2>s=Rd94i>93@M&f9V>EovZD$ z$nLyiI_lNMhQh|B1FIWQ>?5vdv7eT+EAJvym9n$7!&e!HQPp+uhBT5`17Ga3 z)xAtO;+P}PI(L!B2lbp^t)_(KN)a$6pH{zuIXPFRnQ&%u?dv1z3s(u_%VTXTD|+nk zEDuUn`LYdS>VXAbbG;lyQ3hOBY80&ygFVG1x3J}|_hJ^c#41Ly10)2Vn&v;^xZb_y z=|aaBKDLy9ISy7kLRG%K@N9L&rgS*RO!C9}rL`S4GYBKHu@$ql59JoIoN=&_)s3B* zh>_3h45?7vLC_lUVH(gkMxjSHV*CRoemh^RcfeN}={qwQ@mM;!xneJ3P(wa$@~daC zda&8F)LZB=o6G}!%$c51-6jU~RVIP-yXBPCo3Iqb2GdRf?-EyJj7Bb@fC&dRFmXHz z{i1F>ny-;B=E|lfwKmF*&i(Cu(;jVJ|Eno5clypFN_oYr8&e@30@arv2D02mr@Ntr z?jP^T=?@V2woGi-U~bruKruIMnL_ZPFxZ2#M&T{up78s9_rL(*J(j!9Q=c9ZQ#A@q zo{=D3)0BL+a6-b^@a`pyJF?>jGaFodynwl?Rv`AQ-!e7A8P0`~;!ztX&*Ye?G;u*HYN;R%ZX1`^WEJmfRmQMNgE|9e-+| zJ##VB^9%PZgH9`n3Fm%=Kvy)Sf-uT1d#UqC%kF8W-{Z6QQo3%}CN8PXMl_Y4+suuR z1uSU(W#R8G{EtwiQ_RP!G`#yT-7vp}Gm`o%Vo0Q2DdahSsGC21HVJ<%ymY7G^08&E zkMWC=HWB+wQ^FuQ+R16Pm!$M}7c~sbR4Ob+dztyy)A@FyR}3m*4tEOh?QOXjJ)^;f zptoVGd0{4u3y{DZIrcaNm1tFz7L?vm>Q*}&4QZhBoyzg?G>vh|wp-?^Y^CF=ly=`r zZ=(-eD$S4`eu*=%q*mbr04)!ZKUt;`yvmIFKVRS#SZd>~2u zKI?&bpf+uJZ6SZF+{xKu<}d)tDj4;mgkXm zh@5L;GKA|6uUE!)$-Z$k@5xT37bR*fDkr_wiYbm2F`4Vz{Gz{TW?PEZ!J! zCI{dqEAOmH66zbgU!n6{ggQOE%o;&P^GuihZ$N_&#c=WeoOi|P1&BGI+1QLMUI zFm1-1#G_@EHJLi&G8zMsfVp11#7A~iGYNSaauu&+6tGW~m+UcSk?AYFImv!&6KvW1 z9@StWJnrRI*XVc$W%bD}Qubj&pR&cVC_eHrsYi>7#?o117T5X9wsMArMYB9J65N0} zYvAU2H(H$7;yU8oMQ3qdPRUqY7Ll&3HCRtlr?+WoU>H0^s6wXvGwH548qDUHtdnkP zpjQf(-(J_VrJRqPe|e8K8Y028OO{tjtgVi|3O&Vo6J7QL3lIVs>kmrx=aJd8V749K z;t5GwtZCepqQfcyBR`{8&6y4bfl4q32n6m2^BG1>K~s7y;Y$IVWpq+943t{aD8vv2__9!cv8r(vZbR1&<2pJi?52JIps= zJdiPZQuE--`IyN};4IC6Q-Os^`n4yO=scf*zGLE9Cif^kReC?{scROajK89Ve?~LG zEsb9lm~iKwz8wN&@*iVo8oJLUNtWK`@Q1UrY;bAzj3aBFndV#V=xw_6M6lMGwtF)0>Rv{YeVrwU zCsX`}3KaISh|}>Oh-~l=nh%IFgsQ_SB+=WJJ{9|kb0+@ma39IQW{-@^Pk@W8QpZPm z505$5!juNvSb*>K8M@NaImmB-*)Dlk{`Ri0`y4S+C06+Bd$kQ~4#8t=7 z{J=x?8-<|wfBL9gNF-ITUGW%R=4zrF(kX*7AH&sbce%0at3545i%0a`_2naTB$-VV zfw5`EAg*~8^4UCe=+|kuSMgziKi~#$ePXvE+&yJMgxXiowoz03nH)Jx&KgxvU?6n`VZO%?{;ZH?>RxHYa zcu2hb(kK92yCyr6+uf?{68nd=Ntxukz0Zzsm~Gf_H4_q<>V_PCi}vS{70-BbVlZ{C z0R#JWoEZN{o5fG|pGxgMUa#wxX6(PO+UD$YM2@|fE;eo0rxLtF}@}{eA`*jFiX}tHB#lO4wf1aYg zZ(-74!}eueG3p#a9q`DzOJ6DpZlrE*g>^66HT(+wZ9z>w?rta=4jP7_gP* zhl)y!4D%N6f7Z)Lb14;!RgQAaYE7juVbKLsM}A%b-(c8p@VeE3ywn7@Gjh0;GecPm zb2WPOBAj9rkFi6O1e)y{YCGuASQ+&a;9W$Ej_^9BAGEIud8HQx$r)Ekp$K9({wFD7 z!j&)HC3!dRPj8oCcIE7AAMydQ+h|{Q8T-$B@I_e2_8X)rGbOPyO+NC%n1)-UaaQ7A z2LA5Ano|E&p?@@DF6}yPGB0E*G5s8wAS8?={EQZLImBo$8_ocS0%%^NeF0rN|N` z)6%@GR<+>aBS}i1l6Ikb8PfS;ALjo^B3)IpIsyHb2Okhjlo)RA)5l*R&#-nj9PH)1 zy1__hhqm1nK6)GZCL2a}c=19~v=5gk@N7{d*G!5+)iXKMtS*!asw(furqUbd$PZg-GmSmj`7pt5Rn@YJ(RC;-aL0L zlP*KRP;lY}QbXZ&8wu+LhjOA(taeuP5Fq8G0FKyS`9VRfURak~{v=WfEps2Z?LYXt zS~6l3O@xsT!};Zxp#zh+!;qnHYk%8k_@4DMgDr{s3IPQNS|(qH;HN2J0BVMpnA@m} zBGPB(CG6r#^d-ur7*lqr32%uC-B&*(OW<4M&Qz;;PnD6Yjrrps)LMudgkpuB=VO^; zY>uiqU<77qpqJLAU+4)z=m-^5!r?`wU%7vpXUxK)&q3 z7tE!JjNYU^^{S4S{OVr+{&UDWA}29BgwD_CO43=$sHumZeUethB^c7W=846btzU{` zy_T79;kGQzXOXPe&hKL>ivi!7mL_Jj0-==z9yXDAN(ut(U7+kx8!fh5;0(M288dRM z3i3pXw@CFp5(V55CC+_k&X>QlIb6)^v{=3XWXfi&@Fu<8S)?MJc~t^GdH-dD`gWGP z%#1$mNr$sq*Q5q=i3wK2ty*zjmT!ON6*r_f?}CQ|GI)XV(5&M;eGw@@-ikT3U1Lqx z1}>gx`bVOT&_cQe{_L?#C+VNrla++gd(`5*^m2z^PzBa9maQ#N8?GaD+73PpY*rWE zXS(n%6Bd&Ad)!vcav2Iwg3Y_g%u6=!c0lh%DYKZC8kv7Abron@%I3G9%}2>ZNN)ug zxRK-FJD#)>)k6g0Xk6`XE*%1E9uK)UsEIRMBy%z%R<`9nlHm$4!T8Cio>GK^VLfO) zQFfFYPpzB($HV@H+lh0TX!ckHshWnj*V({JzJK`Y{WHkqH&zx^L;WZPuDrcJl)uB$-2*~A@Fd^dT%*?~fDA-^p#f$E4qL>7>yvr{ znxFuL@r^l!v?o~rsRc$-kr0nQ_RD_UZ4Z#&&35#t#1d#wxEqUvNce^H*UAv;cYmGc zKF$1tIqRCk-dh1Q2aH@pDZG?0gd(_q$VIv4x+;J#-`^}n*zNh=#*cO*3bca`i~eA4 z__0e4Kn4PQLnl4hL^%Ly0i6ZZ>zUYjJY(}{;xOZ;p zDfVFpd$K3Eo3>8X7c+soYh{Mp?YO8;Fftc1*23OjZ0z=B>B;F#Poj%v%h-Bj7T&AI zercm;Rmjt)E}6kC?wDwiW*FFfjMGKqfj}(ZJ!}jUE<28;$r(xWgY=!!oJPzYAHsoeF5dK=&=BaAR0s!>|8x@_&juN%ee^cfIn6{(_Df;f*t!&Zve@2c zjCFnGnjai$5CAqe7zfEYnY(A7EWzMazs56QOOxmVHy#H$R=q?Zfx5;Gue?lqB47A> za0&IoOahb3P7LV*A%Semw~`bi>cu+{ z)u2v(!dx*jZPuS{ELl;6k5AIcB04?gHk=Yvk{dcHO|=>8<|ALEx)v(XcV4)*?dyc^ z7qK_Zsn-XZ~H#OlmOdiMb7wp7}+`vf740leTKQ9RMC+0`hhhoe0TIkeNfEJ^lBrKZJ(+} zwT^UgcfDIYS?lU&QO4o?MWto4Y(=d}L)wy>|6@n#XCR%$frmEolP}8M;y+t#5m;J+ zVej6(py5~Feegql*?3C8Bj2iFqZ4CJ%4d*@<$7YOAeO|{vBUXFxzpbQW7{ltoc)m6 zek*W`<+<*s>s1S*c-->n58T^VP$q5{dg!9aFblAJiJ+6b;zyujKjV z&0ZAilb!n9dOKqL)=stg11FOnp4_1ZR{C_*6?r>BkqLJtEn`1F*#uij=c}vurcT3y zdFGUs?b8w43SsjroBIkj!g90kb=LU50%y_f1djCsT#?rA?l$iZE1NsWRL5huM{WK> zzNkp&ZMxz+KV+Y_Txb_KqAmt@8j_a*e%PAWv*TRd$zC$}HK zT~zn7!6)@knH?siy4AQl{tHfmP}bG-4Z0PC;|ieGqa$>yn~c z%c5q693rSjDp}7LO7_1E%-*Xsumolo_%(Z|J^AE$WOr`^9a(`z4z9cq*zK=0Cq>5KA!F_9XbnNJ}SW{OMD0Ulfxn~v?%H@ABn^8zN%g75dRPQAo$xci+TgnMO35Qua)Sl~qj zfLD4KQXhgrYwXJfG}&DUFcgH;x_WKIeBr@4E0F=FFfMwNbuS3QT||5lS&$roAsxx5 zOzpd*Syf(OUzRes{WBe3J&|fp{ycCEA`yQ+GmHsmI*Kz0|L2^%m4w$_Ay^Lb;Is$$ z$UDyO$#zESwM9AIFB{<{(^N1Tecw(fds4X8Ro2|zVmD1=2Y}!E>>BcG_6gR`eWRk1 zoW(-#4TXGe<@+xCpZ;VJ)wn+!pMT<~(JA~gIu6?TE3ILo5Kz4M0}Fr_oBCYDd%#}l z30+^o+p7U^$u$|%j#Rsji9``lC3s~vUIQFUv%0#F$B;8;pOV{UFP!<(S`&6J4Wb|7;l=ZcV~Pz|t8C2Ar_n zv-f~kuGW`4o-~8yn^O#o_ekCs6BQnn<^V4e?2-jy)D!2{f@jCVduDsM5+z)loiD!P zruu7gAkL7Q?}!;1ab^4yZvHMqyK(##+W$~Z>##z`u#ZGRIkdwr)R&})q-&J(pTF-V zyX~4?;N_70eFdP(#-SCyZ(E8BRZyLv%%em6af<1mQWA5N&q>`l$UKyPDnj`!j-T~d zuFCWwzfOxUK_GH>yWF^mq0{@GDLee$;ms`oSarB6WXFwOtIWGWi4uy$wg=%^&qZg3 zWF#Lj$ve8n*?m+Vh`)UanEid1S5~wlscEUcztQWNa*Z}Cm zokzG1ofkh+ga0Cm9`_PAVtd>Sm*H>a?(OrIZ=6jZ6P z2#F@IAk^)YCwY%RDRCdDyGFt?jxD^@h*$p7`uSz?@O#(n#>`2K{73D2Z)&Sfqs27C zOLF=iVi9IimSD_bU;?1MA2+y>U#FMiYg?H3sy;}<@o&2W?6Jy4f*I#MFQ4EgFG1zp z$Mpkif%IaA;vU2UWJ#0$X&Plw>nQWhv~zDfsO3hAiQ8nihaLWr($M^@(|fEq1N8dO z10b)z>nSWixhRXBM%)j|^BvjVsE~ZpC$M@Uo4+SaCZbt?dB1MymV|3dfdm{31`uH#lx4K-Q3pjScPvRL<+b zDRZE5FMg`r#9dd(7T6@Y)90&93rG@~$DU8=FJ7#p^Hop>H*`v;%^g~{;78S$aLd>i z@esp)D6IFqkB(}aBE_+*L)^0GS>x#hvtDbjw0qIF0r{jNZ@(i9M!!NXH0-sin7b}z?K^eW?Q4xu=tVqx>11O8e4g1 z4svY~TnEWW&B;eg=v3t|%cU>y<>7gyl7%G&haNp6E^LMuQeDgY<@m`hon0-_Ac6?N zN98Riap>oJM!AF?uDbeJ4ZsIX1OXVg%PlZj{&bN9$_>%3jP8K_3Tw7o5g@ld&$0^M zrcMq;g=W-oq@s@*(1`QN0+&{Yt`dgKQD>@+NA9{i`8f#luCP|Rn$NNXAF}mmlh$@G zbCa`}j@xT24EKef2NjX;IfyayqoVCAM$CKK<#c6^NB=vFFEB<{4fEhQ0tU@*E5iu=t zVIe-l+-swX@h-n&#;1&ecg5@~2+yx9Lt9avSUr*JuPt@%VLc?FL`ps)2Q8C^uc4ER zuzvE44PI>BhdjHeA@ulck68Vd^I?p%$tP3gjr=o8hzlpY@;q$UMoAR4f{zzvtm7aa zM~=Nf(sKg_68r|!j=Hg8yN1UUVfxO)9?tQH2z?$8>Fz zmJFE>3sk)<@nKmT)oCXK?oy2xe3)?U8$N(jbqiqNbXgE;m{3GYy>skycFG&8BAzv6 zYOUk@*zJUjsm8oJ1+)rtQ7!+!u7a(SaG^Q#(ivmz*oRvzhbcRZ+$Bo~{Es@35Nz;| zxQBkV;4W;&pAxrilHQM({D8X(Nq?!vZl~+XR^O5J2DJl*EPa@b2%+*% zGG%%;k8J1VJ-aW{Fk8h-0M7BQh86+X8Aq-Y=rUjJ_>SpKhG}!?*h>O8)=Zp&*zXG1}*sK zF~-&Qpmoqis>+CCizU6F;iEu$tlVkh$1DMIT7mdv3J6gQ0NP1{^FLaW9j;_aVQ=ns zF#DMT12NYT0Iw=hGV*si$4{mOU0r9p1WNgwml%n1ZcL}x`qb{p5+={K%NZd;%#`sZ z5D$e?ZbQ(CL;`rOp((Y&h@|t#WO;t_S;Cigvn_NQ9#p0lTMH5=_ErK7rsXMPVm` zMPcNs(-*#S@}|QbHlVBKyU*|qDLevagV)X9Q}{v~ooD0v z3}0;3lZLri{7fvtnAUXMiW>Ax<2AInG_ZFx4iY(Ko>o!`UMj2Bzk2QR&|Oo?w)gh^VW9re!_`ZCGaT0a@$Cm-^me{bS`*Gy3i z`AFHWsoZ$YvC(`nYTRK&AanNM<=Ft)T31>hv(n(4oKbl1!|wER<81J#qC-Skg7Xkl zVk${A>#`BuL2*Aivk?KTT;{*MG9Otiy{I{razd8#l%N7udPEk;u5jYy^>55yekHWP zALf<*#;I!LJMT<5{Vuu}rUGY)92*mzndCQu=QuR;i7pJzoUWY5DJN{dk$k{8O`+3ARoDfWRuh(FW%8fL#F7exDI> zCO4y{DDS%Oe$8(xwt4J*t5LYdaba^NrZyd+%e8>=Z-DBgFFMhQ874yibO> zuR7eS0ru@eR^LaipkE7rmVH5i>l}=J;Dv{2sgmi4Ko!Ukg=viGc$b#A1!h&Fr})4- z1D@D&p+OME92kiM6=P55OmJ^mZ&hD=FrYW^r$yT(uSMLb%6wbZ0{tV9T!`EIYORn~KH3L(6YC3htjeEsbzRt|=Kr;*=cSK_ZhSHUxU^ z$!HLr(E!MQ5BGWj}#2>(M|?LmM;r*P^AVAvZ&8dK_!ML_ZRl>=0Cag~bP7LlIHXjyx`_#m} zwsK*6H6tw*bv}z1&|D3Iec9U?$sF}n+YV1wHE%bZ#H9H;T5m&yf7UyAbBZq&oo$Wr z-E~_RyN`1?l$VG1djq*%2S2!Qw;V`|R5;9I{r)x2sZb>vv$xzWN2CF>qJNqFyR(1Q z`2P)?t!;q{5;wTrHTK7dqpVqsLS3++9rC@GEC3^U9-n~a1ePBQo-2uF062~veKg$5UNKr`$g>JBtyS<1 zhOxjXk*3oudfV7_E6d0pHKAxC@Y!d4nU{{AfD)Ht?tPLlSjU~MicQC=C(_zlYZ)4P zDT@E>&C|Da36k(<{)D=aj$V0)tK#??z~qsXrHW+9L)_{8J>hJ==Q9fjGX1WuB}~SqK9N>}={rdZLp<^+$<3k~udPg> zGz3y?se>f6fr6K+Uogh9B7TV7a?ioJJV=hiM~-^{EU9rWZp9ijN~ITsFF~>O24vWC(3Q5{tXzkx3m~n5Vi}YGV$$bdCCnC0~x{-Q)G(#NkV(WViH*BMxCI*4=liU-LF$T+6n+4 z#S5EqXs@SeXQ*#WZrIP=W`I;UoHfcGRyoEocRL(tvE0r|+vv>EVz+qk;snUwl?LDn zS9|D+3L_U7f2uyx9@K;eB-(Bx3j!te3xZ5XU)lX2@{ta!$_1o&+Y60di;QDjR?k)+ z{TIy4o_sgbB!q1f%=jBZd+aKHgM0_|)!|_+qQnSMNqsHD>-yvcxsnNY)j&}*-yXC+ z_VM6&t<2wQ1Ew-G;vKa?UeF2+@NyLg*wg%f2@>t)*$o=&%J3}4E=bmwZJ>+p-xWr8 zzA!CwM-yX?{Z?klM$iLERGiI^&+|NBpVcF(Hrks^@_+1V^R^~RWWV&y_io7BiH~tN zKnCpSmrvW0^9*T&K%4ikLDQd(aZZPe&p)2h*v+)biXFab%jQ-o;HE-LQmWI3EvjDD z6{MA&m_3Ql-a8^t*E$q{dta&4tE-Z>WOPgYn)$lMNr?>Up#?LqiK*;;xyJZYlEW;E z)l*mn5s4Y8M%?{7`&3OGxspJgRbASP9kAh@uICnB5@aS=rUU~q|9Eqq|Eu!n0hoqR zR^@jxIbWP=IUn+Bo|FC6aSxi`{~6zZS@^pPe--JQ5BR6H&}N0q0uMH1pNd$kTePHd z$c1^y;!VfDz(cYHzrcruKW>&)UQ54QX91|1UkxqApL66*S$54#;H~{ptuqWA^`K&S z?oCp^bKF!&(#oE8aPl_k`Mc|3*}?mKg}}qSBr-ig&ps}fG`>Cso~|hcZw+l>2f6zt zsPEKRCI|}f%_HqrwxBaW(F0_<;aK&4XXUhUJ=3JJw7qQ}px|^|gJTUY9~mv#g4dy8 z*#Uglkze0dmsSt1s#7@rC|A$Vz%UP=1*&*A0L~YDE+}iC4MTQ>gMI;NOq#@QX#fD* zQnP!=1k|jacV++m@UM9O4_Au9ZRAYZMw4R)UuC(-KVYHa^g-SB%E!&7({V z=EFf!5a19IzLo4#7@T-NtbFwqasK#5GhP~GXIqn^b>>=unGRItJ_tV^LFvqb=l32Q zzz%c@$Wm_v?tI2E%!1f!-;MX{0k0)=#Bez?;kd$2ZyGwJ!$KjP<uKfYqXT}gVk6-}w@QsQ3*7G31b}srbH~{=rZTz=mr1*U`1!_Nb>C z4a5#?LI}T=qXzv}MsI$3;@0KF0_HtLIRFZcoOuMv;pvJVPii>&UGU3)1rN0Nui$@a z{KO=b$RYZzXm7-PT$W=?PZ%WftZ_ymqW4767o)dWH(Pp$TO$T#a6^`teCQIS86;v+ zdW;Y^HIwgEY~ReR)<=mdt8tWiI=P=w`jd@<+SMk@kY&NWXeqS z`@?HkD!seTJ%iy=gy$RuB@8F`sMQPm)qpOiD!9IN6@X3g@8>e6zuzRF@e#nE4igTl zKMH&%^NOxU;oT7EhclZ&LUJ5iIo@9Y{h=*FLAs1PBwg(q90f~C!|Xch#d;-2Vq|tC zOA=oML7PV`Rf|=`4-;lTm9>GkBI&On6cIGcIwrKmwC6Oxz7L;_&0r>=Tnq7Tfm{9- z=oCs3(jfeLC^TJEja~aEssT}u6Yf|2T3{pw5;^fuc6Z)wFyJ!2bv0NOblNS;KU{tY zA{QT1(Wgwiz8BrEJS*{cXBnFAIS+inwC!0&7QvIqXys7x$y*OVqoMg2>)I7eO>=D- z1A4)#4|GKgmXx@L4fKM;QdxTBvw(|xzHFdHl9oWHg(XSQO|qij(Gptpo!n>JeH8#? zGrKZd0(^CxlNWT%j?#g5(diw!K0PxPox0fHcc40{H0vw2EBw&PtMa2e4=tD&4DVH| zkDq-oTyTuuMx^039*91q8?Zk4Vn2Y?&qk!aG(}5SKFo0gl)tFSmP9?+U{s(*k_J77 z(t;n&LGtOvjg8Q6`&xz%69mfd5}(m#EtHaX0nEXuDbk8lbBWJn&?O9ufeLC4H-z>eRP*xWj~x zfJW8XZ;h&$NIDT&Z0@Ju*~)(VUWwM}3M z+YDeh&EO@3>`&kws!Z@66R4ts@;7*e>09;Otq?pCK1yr9#aHGSLaSJe(9AjrzByZO)<)?mJ<~3 zB#3fhk~F*)sHi=N25*X6mx8+jY&>uHff{JH??aU&W}sySLCsFKj|Q&$LsURaU7(*A z>)SS~lPC`Xs2l2LDtUM}oIgnfIBzX0~2)vDue*@G_|MfnA zs}Y5xRE1^B-XG$x{44&y;{Wf{{)fJ8FH30;>c^wwVj|#;&(oS=a&tokB`V4OTBGc` zlEjy8G}vWNw$FG4TsG^lCK|%=GWyv$?8}(YSY3|oWC&=PdoAf5lm+mx-b)4J1u2c` z-cP^-#9c)^p({E3%(~-AotdlMbQbs!#Tixjd}po2-0MCwfwCC4hOlkJVEnMNE5Pdx zf0Au-Z7$Y7pEhF>m|abS-PT(H^v1UrmS``ifQLYokl@~Mw3@B&;(3)>=_|o2fA<+3gWzMv{oXOW-7YWG;b=65QmbnTjOIKiuNOYK>~-$!AWDxX zFKkO5qwH^vPnJ(dUxeDDrx;SpqFXb8rTzX+A^o8fab_oXs)4~T=_~?UjX~w^*yz!) z5dVF9Cum)n0Pl;Txq;sw8x(bFwydHnie*l(i$p|@?)!F3{~h&L8eFn}bz}XroPVY9 zOB#3S58dcJzxGUae$}~;-#JNKfFpO0X%{y69_kcu+F(TP&T&&y@)G2;E zb^m3xr#lc?Z~Smsq{7uVfdP7L$4ycEtxH_@_TSuaQ*{5$JxRM~x)BD)Lb?16uc{H4 zHeTIgY{X?ZC@N~;&nkP(=)SA(KycUM#2n|_cVik-TzO0|XBm^&qQl%rYTmJ!)U zFrjn zIm`b%nhs6m{aQHg3`p3SWiY&*P>d>4kUAX~g=-%gv6`%J&Fmg|-GzDU#%9fTN)i$& z4j-cgTj@%WJERZJKk7RK+ll#s1?YWXUOwMeVwojM=)<}S5|1Oj>CgK(OOHd9W~%NLfd%R^%y*e0Ba-ZC+Y$az~2TdctZ zbd;X~zqhetPO2-BKkA+F#MauJ*{-Kr!{fOZyr2=Q%745lGxYfk`XsQzSUS9ErD-oxJ~SljAv+p7Q-EHdi% z*`<(q`+^pzo`$|K17;`+DzHfx=e^Qbk zM`3sM2nUA4k#&WE6mfAW*YZCtw6-tkjOAPKT9n(u0n7ltB=xzCy^@W|xRG{z0kxna z1VAp}1&n|*tphp2KYzbzo6|H^b(_x(IE*b_j4juViEL}{Bo~jcl18TdlI>VYe|(9Y z#*Q)4=!D7tcJ~0u zkZ)12cHc;hvg1xH0E6XRmOc<_N>MSz2Zk^E;}ZMh>WnZNb++xK8NjJfb~wFCkam*f zD-2`A_B-|m8lN#HnuG#dpmU8aJ3^)Xot9QMT34TWx=gu3EWQvsRkR@aUD9#j$YIgB z+N!nHJt(;T!1K*&kww9o3ip!yK?j9<{W<~V(MM>w)Cjg1Pzh+&(fh0a0dd-vcm-;N zWJE&BvAZU~mn7I9&YU(4r7N&{4kem+ro{kqGoA#tK$Vmw48V+?GIqV}IDmows{UWq z|9k5HbLxrL8z7K-1ol41O7`B>Q&iYcOD_4dlUR$5_77;-Mh$dHyigjMmCy$Yka*Cp zmx<;0t8k%RFZ^=B^QeWxl_^|&+RC27Ywb$A^}GryV{&Q#L9CZNZrA%Svd>cbIDScz zMn*cU+SvyGIR!spYY-1Tal8N~;v|Brkh@yCj>;cgA2cjDC@)(rS~-Gjg|6aM{(hw` zc5+gxRjB{tax~)vUS_6}nJOu@pr%zZ%!b> z8I64dp;qkx_>qeGz35zvPR{JD(7C$EH7)4$E~w1>imvHg4^omKgw|Lqky^z%@&NFr zxs}Ins)2}3hP*E<=6L^H)DMita&-L%;ubFVw;Xwxi$@``?aPE)>|1L?QCB0L#XPtL zI0o6FNCo?+6th~ISt7k?>KY@A{r(8i+c>c4zfVyN7H3A_04KNPCUk$C?duOf^^g7K zjdr|L{sZO(D%R6ScpL^mwuF3}^+-v0W6G_-i-o7Gj(o_6uqiDj8W}w~LFa@Y>$|-; zSX^IzQr-B63!`G1+aI!{I}!!vA!eRH#x=wTe(STYiEJkRK?26s)(Mkw}nQnk?nN%Y8!VQ~XhYUc&3s zLqpjS6{c;P4L#ve0P7{87FP-*tN{*<|$SA28D1Fmqz1S;8k7k7)n#yT zvb3=%0Pgrhy<23+FMs^)Xnj8-nW8^r-m!Eo%Kj6eUGZb?CE`D1enS2&|80f|h56hH z8`6=Ii6!o`3{MCb$%#a*!3 zb&jEk8^^lcxj@-LmpLqQ(j`ALMuw8BKH%r{2F{*kRz_l2RztxPvaV*hy(q0z$}`-yrrs)3d>2fz?Q0S@O4>fv2~4mIoWK zw*nq3=kM7^Qq1aEX5~jy#05LYDz6hlKDHZ^$AkN|qNo2OJ&jbGaY)}Az{whi%sGG)q+DQW`l|dMfZ}` zAt^FD#^pdT_pGsOMcCo%plUz%HlQer&--jc00*+H8KW$6Zenk~z%b_A#GO*WVU(aS z%mq;btxPAz9V&q+a`oO;GN^sWBzFWoZ(2VbQe{a^7N_jk&t1;}Q6(wEWAg7%nF&f! zC9zjlq91ymv+?;pVcHIauctX%vE~2My7(HZxr`1f{tnp4LEbUa4$%3c5%Boaz?N~L zZicYl#h+UJU^VQ`B01@p?l7JW-M1C{?tglzl}2Vevn7=Ua37mS`Py`@c|)|WE<+<@ zKUDJi8B`KDu98oL$sHNCejOueb4W9MiNAl&$(%wsEedOkhyNr1A!I} zwIDBK1$+n<896#R=7}^i-m%qAuPNeMUD5?Qj|taLz{x%DEvU>`YRjjU>KdCMX4fEN6S#Yg`?ddVRF%=&)Jto2Axvv7z*^=il(QXVJO*qx=qlD0>TE!Qco;7 z3AAraegP^Pa5f<($CMgj20Dsyjf1FM+!k;+6dw<=0d=6}sy8{f@9!R4!Nu_bd3M=Q zmdLhj2&rg`KE$L5SMhU?MakyqaHOLFngBomY^GR}%51sj`BOQShIRTewAgJR#kDEb zF1{z~B+0Q|kYMwPI|pwUVo23bs~9iVlsUOablfr7GC?8!Lx z2EZQ1eGzr8?u7I~QI_}m97eN^#vvDgQ%+c_f%}<<)HsJz%nnF+7cow_4_-OnM@bcH zxr&9`-w~q}4KW{aPc6?Kcc?GP?XS6AfGrmXuxw=5le|;I;)&y=>?pEZHsNiUF4hsq zrKb5)krn?c@?S;%A9W%}S%3pVdHy*C6)ktbk3RuwlvGJ-Z3na zK}qwUKWP^Nubl_Z%9!oJXL9Fp-XPtxnDJmfsfiv-;@^3FpSe~I{`=LVhnlifs)frM zb!n>1yuu1HMC&p%Z4*b094nsvo{@rzaBgQ$A`p%d<_ky&A58d7I|3op=xL55sW zra)XNVV6SrA{#_@P%63?)B+as4LZK^{M0-!sK@(;?w8RvJvf@GuqFIK=t-)`R^&3{ zzBK(Go2A{k$`Hg;OXw0Jt+<%fxUgsw@KGJY&8qZK6E*f}i~ zBKFUGGongH(U(j99)I+*>p^_5!q(s37}YdKYP`b6t2fmpIf-ATalyZa2~g3byI7`& zyp@7CNQ{1DRkMCUCZ-!qHz#!L-k+x3AVvxI48W?UvBv>Z@@@&Vz{W*9tWwL{e@{sJ zL}azI9&EE`!;C~eUR|~AJ zZsVByz;2dTsO-RR4Fa;idhE<7QkcuX%Lcl7@G;+!I?3g*G*hQ+>W;#RIQRN*oqw#d z$!sEvRZVujYT56gr~uh8|8+yDs%=}^_|+f#)u9?C9iaUyim_Daxrc#CPXy5hl@%w} z8vodcwSen)0~;Sg|8gC?Y!TUTwZV-)#`^V@>QM1d@GzrGzifg#LVIF5PW1xZX?sjb zD}X7o-QO^R5G2lSldt$nS@rjZxzz&4EleYE(D{yGesV%BiA5Xqmjb$JCc(W~&} zZ*!!i>7l(?;f=htE6{G~)iLe?59lq%+jM3W)>4WQmy;f}$NmS6j%@N>1AN=i27hIA zcYXN+`7H%MPpdF%lL7y=1g#vxl+)Q?rl~SZnw*G+duCvf?hhdcWCidzt?P9w82KA$cZ{#??_t?m1=FX$CY~s0F!wf|en;1)AZvB# zMn8Qk)2Hhq4}a?LU0>yk|2U5onkwVlUTqIB>+}2s2*P4eJ0N5(!E->Yso0l!W5llS z#jjBVR2u2c+}uFR_2`i`-0;%za}y6g2d!8{6Q!q&TWn99Pu2Xb&@`h!-$RQSx;VE* z>u6r!HR>KQF!eAl^S4N`mcr@wwr;@Yi~l`Ow(AWekofq?ruosG|YeVg2cK}wpc<7yl7i!9dL@F$$bBEHvz z{s2wEz*f=1ruyZ{YBVfaH$bOE$EgN%)BpM#hPZ&>@ZYv_WUJClLhg;JbkNXMTG!C$ z*qs&S1IW$Fu^@)Hh>-9<&HCQN*F_=s-qX*~m+#Y5UDkeH#TUIJ@W+mtkI_P#i#DTZ zs%Gk*2_eCCq*1t0kCQd$pYR+WT!}6jXA!scF{h547Gz zss!j{^6`*@1HaWW`{&ke6JMsBRv3ZyDS~^#e3F6_r+#;)WZ93?J0@^cX@7aHA0iJz*MD=SL zioi2W@#~RIP-Icl{gQVghq7kR1MVo5a zT^+NoOSbd=!lMd<39N7a`gXn1^ovWAans(#zm!@rQPQ z_=zAYvHJ5Z)Z1XksHi#hE5ch;G%~)Ekbx2a9SgCx+y#Xe5m(}P+ED5A_pbbswTgCN zoW-f>*O~?Eb86{zTY_NR9r5e~2`V!OIcbEd}Ss*teUa-a#)Aw?K$QgDqKncE9A`N_nG~LId3TEriCK+^BqigV)s89~{-Jlk0`~L*C>rno5i0^nqQLlrsE&U9+d_a(3=@A*{}vPQDS#hPh@M=q zIO9Jar@CnM8;y=Wt*Qqck@*IEzjSmou1bK_sXs6Pu#^;F3Wa%r7WIETPV4&SznlI! zO#>4U>+*Ae68^Cm3d*qSzpU-YxKDUgj`(5$rhh!n-EjT*AD$T)QV&o_ipP1I-vQOp z<1{i~|FL`SiPscIoO;HsU#H+`TtIk#{bTq43h!Ux{cCxYz?u5@*#3)o{~vQ0H+Zk% zoOnWnS(@-0G~(NMo@cWIha%#S&P3{E2m9TCShr}V-%2Swlm{I4nJhCG+tYr(eR>)P z6>uAzTdqrUistxP76`%g7zEz_^eCFhN|_ScUAi0D$eGu|dp2l&ipeNJNpW|4UF(;# z2wVb=#4x0skLDIltf^8&s*~yYeQ$;eY_NT=I;Ve{;NW4lV3hJHQ)mRsZ~v4;$1ss( z(8EAAd~%K%oqATJrH1|dE#tl2Ssv*FERXk?L5#x}Yvvoj^X9Ji6qLnU*ObCkIBBHz z{BKt7+ZY@_U51}2}>pAs;7c|w-WSIU>SmX`|{D! z7H6~0t(`K}3vUu{JyzBucno4gRAD)QO(VsyQHZSa0%Z|8huAay2t zpEoL07R3X_cAGp&EG4c$jUz6}e;zO1?Rc*rrdW8<$1yvzJ^KR9I~MK+$=hGKe#SWh zK%7(3D^OiaQ@IP;7kZczF4+3GvxXkJU!&$F+`^am#qxFS(#PPvMWy18o`a9twg=Ar zjszz(favUNq%p%aCK(qGt=M~lr0R+zL*BJ=8Y?R*H%#%+(3&A@Z`K@c#pU0s>#-fR zjlY5K1`b2mvc*rnYJ#4U>Y^HDU=qEVD=558$=4&&7(7lI%|T41MvB#| zj{9ejx6PiBx3=edJ-AkI^`3}9?B4cddfm1Q$>hZ8a~EVxE9_S`^|N(voA_*`vIB`| zLtyvf!Ctw(61i|!YL}jdp@fOMVR*PloJxegu+;YEmaG7YMME)9)?>V*dQXzf9ZAHR zMuZ~tfedV5C+quu73rXMHid3F9bq*6?L(idhKj**}B6`kt%xz?V35dE~aqf z>ByI=K1<*7qasSSDM7mfCf!W!7v1jr#71c;u9+b|J$0&byNRv2U-n&+Mrp%P06|jw zVqKQ>-e1DsC44`)Y+!_F=^2@AzMt5u5i`BZ!hfu1t+socUeZzVx^9i35Kp+0SqzXp z4^Sae;*etJnsR$HKdt}dvbsAP^V`LOn63LI-x5>9>Q5{qTZ$0*EsMzE#5pAl34L-s z*^?UbAZv!CM_%j)tNLuF=PS4(1fcc(W|$vvwjHcA=6J0SKH_k}$3zRrAW89Qosh+i z0byG12rd9W{Yg)?8R9|r^K7VOZLK%8-X4!r^xirIWS369BS(R_r^6R=s}787gfRJ>^E9ExR|dn-D!OQ>fYwB zvA9u~gz%~sLDLQ5Vr{AL=t0q@lH_2NDTArGM}4(}8R)<*u2x8uEq-eVeVMJnT#W5A zzgg_wy^7Wp5XAf0oSO$(#Aug*I)g|cJ+wmaY2EZEZOj?MX9-5t%YxgB9)w%Iv>t%} zDaOH)2t;ia=xp^8STTeDl|k#Lvxd_dkN5Z7N53~7+a2~=6QhTsGB?%rkSy~AGkaFd zcPS6eS~ip@hPd%|LnwnNReQOb3OyWtvTU^Le4N6aCtgGEPV15$<;=PE>Wsv zAah@QZeh(XXEHIzq*Jz8)@@k~>p_e`rbmiESm7+r$YgJkV5OA@n7Iv!xD?CCmz<9d z)b*&dvFeH;(?8j}_Q4^62IA(cBv);%&GF(mZ@w4e>vLs(2i1$Q?C7{S3Ut@Uz$Nd$ zOY`ic*rDc|{5Nh`0tD!$W}cPPkL`H;EcjX0q;QCUDduPJq=+AmZ*rTeboD3t{pfm#gLDuoW*<&_H}uQ`9(Y zu|k6-W1cQmIQm7zZ&f$V^Y3m5&99Dzpqe?@L{j|)*PEUhC2V>LtlT>1b2zFGkTVYD zcNdf`&GoIP`%j*R*7wGKIBA>ujEiL@Ipbi5N#F9^rC)++finZ2h%<3cN;; z%a0qtdH7=A?EM=N!WAV=4|R@QE(_+Y8^3G zExUj7`xMh#^JaU)y9I|BgMeC?A1F+&^t_Laj-6yb-Aj*g{}ql%@A1=)*W_Hi|R&ywg?3&G29q%MYXPrl& zeVv(M@=I6wW7)%QVv-G@j&Dp{YO4k79WPaM;B7HDe#GG-cJ?&{2q_Ujn6XEsW4^R{ zg%ZBCuoMDY&`n8?x`uWa@AmmUlw>!C%0$NVAC?AysMhpW^ub_=6N zwuvUYPO&S2IRDuUzm*e2y#r4_yq69L2wB%;AWtGyq;MJBj!07mVz}wp6Geb=k4|oT zI1=oMp~xY>#0Ec{N#G-**bvo4Jl^|RBd#W-7xkP?XVE>w+>3g!jK)`1XGB`3_$(z$ z2IP(nTaE8(6WwBCXnp=7_s+4qH-kX2MYD2*M6_eZ;0oiewnxUtto->%yZIPCD*Lx2far~S{v4a-Qe#D&dY>R-zZaurVwL5k01dL0&Fl@0{ zH(!4Rbp~dn!~d}P(_5o}-r1T>*{mwp0x_IeOI83{F?W&gN7YF=XhKclU9BG$;i;1rL%%CoBfZa%)nJeJ3-7O zwd11SXSb}O0ylupr~fKdY-GnZP?^C3MjH&&!q?{!FU4mnX8iRXitTo9P|;QZxb)nw_%}p`^Q6M(YYINX1GBEj#2}RCjCHZ^B*d zS>c-Fmo%BJpq+gMd70tR@yp^h=It~x7-kb{;}O>Pi~U;3v>-(8Yts(7tfZY$9~il4 z>p@?JB&YWi$mWI4iq;uZmZ@2_4CJzX)!=?sH?tZ%eBumm2F}%!J!{ORkVy+9LaLkI z-_GXxWKYs5W5CIFpI$2D$hK=3UmMx!l^5CuB=I;Ch_`0@Yv$9_YyA`ij1B8rIQ8{Kmi5YZ4m>mYt{37 z?Fl}&7Gf5OBg!|%Ex!ec%?-HvD8)I+Un0sl6hY~xloiqWZ5sZTY8}wiSDhb3BAKF6Z{T- z7b>ajiX6*cdG4zC_0d^Mn(sj7^5zVU!nrQHrY$`BGU*P!A|&WCJWS&g9+7(1vED?Q zBq>9vAssHW;#h5Zxil>MdxWeSIgMR*fFzl1U{Ckv)`&6*Y?J)echhZ+-{;v09YR84 zE@*tVuT5RRjkwS}`U0$8UkFAPE@AINKg}+Xa4zR3Hd(N4aTQ+T`HMY5s{E-Jl=DJ5 zHg_8iQSsO)FW2Nw8^(!Ehxb8oxnGFRcFOoNZroS1W4z z)ZFiu=?OAXp$Hs${F+fMM_Os*8*rge&Da%Xk7*ZM#wgmK?6*XEX1qWsTg1$!(u1JX zk5P~A%Wl-|;EuDKGdotENmfxwg?3-~ZfmjmetQDr3xWzc&1OiLiB@}f(8_p>Pcfai zane<8_=`~3SikYgKu?{*2|JDnkJyf=jyFp4{j2bg2F+CJJ*7EJ3v)&?qqCW@uj})h z#+M)Ix$2&qFiD<4%s3C=z>*$wwYZGfMa11m{rXWNP+IxqhF75&O|Pon4@x~^TNj1;G{?1aQv-9$&;?6 z`~bD>MNj`)FO1!%p0=rxHYw>12OjbWmKu@On za)`)vM}`4A6Qb0zM){(Y)dxF9l?DtzH(;)c@I)j8bnsMuF@7;}gBUt?Gdf!U-Z8Uf zDu`lzk+ZMLRiNh6qs#Q2d3|H1bFgl^^bX8>es>3w*O>vx7Z$(#oJN-hsK$=qzCEs9pA_4srF}}Y(reEdL*YFJvGKv*4A2obP?f-0tZ?|tf%b-FIy(aXN zccSE^;-p=;Qt4%q_RHMqd^hIqxNtiRV2j0#<8N@R(z`+a8kWLHw7|@R*YvK)y7Mi$ zR_vC6z0O@)8Xmrxu@ygj$(`}MoKA8Xz!G5rjM!b~%2!LXr-Vzn#Pm}^d{9q?%Wvr? zCUkyTpU>f85-n=tJ$pr2wukkcCCp|M*+t?+QJ>O-nLCvmu|ohb2kAij-BH4uL={~* zUXuEHLXMj3#*{Vx`{ISy4vpdsMOE(OpwlcY2bxJf*n2EsuW&BQCxts7KI)F zY?c6IU}j9n-krO@7~@@7J^DH8~U`L;$Y!Xeq-C z0#r`WpJKb5(U~TKx^*+wf-m=WQg_R#SnU{(z^M|sScV6es)KlO>yce7e zZ9e#1ZqVMoxBakG?0v^JbL7p(G#QsP4-?>BtcSj57~vnP4$xS{;vq-V#m8TfU;0I& zH80LB_n$H;!iFz>bn6Co_@X;uaRhVEBCtR@>kx@7ZnAW7tW$nZ%-jBMKZq3l+r$b( zVYeS9m+Ys88dG{9 z%8MKJ;*v|vdO~a8g_+F@VcvZG^&luwlD=%^W_SI?@yFgq#dY{pikxp?>UlOt76-8S ztfh)WC;NgUy_u-bX;(HCv8oK9xUAndF5QB%S4i17B&8u0{-6RbAy(oAu>^J`8K{NW zC)z&gE9ig`WeG<-7%8PH228I$9+Umyb|bbmYnUQuaR7X7E~YUd4%mgl!PHw}&gUI-PKRZFJ2&@UJ7VlG{t~4;-h0E#`JjSj*xQ{?MJLO?gok7t$``z#h_e_G zZ{YB{r4?&=iS#nGyDph+a_TXz1j)PGyXS3&{>}*8hLcVUl z!VdY)WSKAz!yCswBNZX;c`8At#exsy3$EM_=*3+p3Oe;kv}r*z`zD7g>yspD7M!C_ ztc(f)85_Vd?_2_S;mp+BbAfHguqfl3n~GM%SmnK99_hpDO3%L}Uwf2DQgw#U>T|MU zJQvbZBnQl5tPkod56sewkAcA7uIP*s4!XS}xq9%kUH#N~{@TJ5!Ya&qn-T@#H7*+X-%UWk*pRe~o{c=Hz z4eLnqRMv;V=98OQ&Gce-lV%y2&;N(l{AU*O1po=6cosiUUKEOfM!PsIHHB# z`a)~^8-;BDfZl?8Fv)`M3~;4|zt*-}9$}Ky868ei#m%N_eZt)^$7tODDyOGddtP?f zFcT**X={M;pH}0wI*_NLeGl-tS&t`?40CLORrWb#%B`_1a5kK?Jzb2M%g3S3V_Yvt ze5UVfeY73j=p$2N%ZT!UaST8M4#2mN2JJLe8mjXLwbRU1u$AvUteT7T?~7tVtn^c# z&$TWRuWY%6SYYHg>QZWu>s>-4YmY~ z_!4R|!zS$B@ueFrH!nmRCO5Qv83_@<+$Owv40Bj93pP=Dq8uHim-}igqklmasI$68 zS`h)>Q@O}&jmBgmM;<-tWw1jPRyc5<;NaxAx4iFb#AjxUUaoS^XPx+2!CZzzd%io| zKl6H-9&;`qtUeigTS}-Yf@2bim4p?e6O<}}WX^Hh-byF3lkjEqWFF!%Nwz$U3v+rC z;QTWhrC5N9865y9!3YBpss#Er*_@{8xUYJja?wKb&N>Dmx@TO6k+6q$<$V-`bLQ?4 zXIIQD5uk&0*{5ByZzj{}@;`ZP^5nQEJlyoS419h+T~qUAZbj#fE2BNINf0t+&M3MN z02*CKH#q&u8=SQD%M7P7pUs2bIb+q+;%=Sj8b5?N2Fy_*XAve-Z0I}sp-l&03B}sn z^jEST=pv=&sLC#$uT4uP##_T_J)!`PGZB{P3~-#O)ezY5oL%YW^V`t7Ef258sC#@x zS9$Xi(56sl_pqwzq3*1qt2M~leBkCXB^>7+L@gEbeseLWl7rwa=Lb#u22;kA^3VnvA_HQK;kXP9O5JhloRw$g_jHvC zE?b%kj948J+q23Ug9T#qO51PexH5q|J@QJoX=426FBzSuQ$2!iPgnvs1*gX011c=u z>^Au8L!{x`tYH;E1u1Z^C8KeiN|t$Oh85IpSC$bgjPM+!Q`AW1g!5#4xJ_+_xw1TH zxu2MGz|k_CGd;;VL48K3S+D?2;8x{u}XmQOP-xU`gp@wHiK>_#ot zj@@We7lC3qTii6O3cUOKsfQ2s6De|-PA0<*HXYB~Vnf9{97igY3aVz~ioTu(qN4zI zz*(_tn%ryv?4F4NJ{yho6%4UyUpa4$+|NwC;4?j~uOS(~Ry0NzmlDAF_z`3?&MGcs zpb)L@aFPDxdKThvjTF4My$c!4sgG@Jubf}^x&{~3=QnueD=R}Oy8rj1^AlTaV?uI_ zw?}}qmSu_+r@2CZbZbK2;pH7uS^qUuS_B}|GY0dJqFvy>Dy;0!PxS4+qQ+as6mGjT z{|r>uIAQ&!NPkU{(3E7yfhWKvYWHhsMyxS3y-&(}JV#(0;ytishYh`Br`umEYij|N zz6z2usgK;+Rlu#4kLT9V)68_)b&Afv_Pb|}uva>0Dyt&~;U<{Az=p10f%w0e6S}LF zLyrXX+avYX%coSpy(NBb*6hYL&MNSx^Bubs@Y`MNIZX0P!pic^Ll{&1oYc9Aw_BTq zsgCC-OyDzj8XGUnYK&{-^*?&j#qiA`0Ui-H&3QpVrsfL_O<$9LsW=%hzQbiMZ=-22 zxQd>Rq_CW4xS-UAGbytsexaBYUO7-AF|&J~?kxbyrt-(1pFsWHVM3gSzITMM~1a-~>{2shtpRS##v@F2GRYC2$>~7;u+AYgcdJ8Am&J%TaF91n=G&YE=CVz3l%j#Z8ekjTpNAW#|HK zQ#R_;mY$c+2vBN|DtJhGxS9(>UQUtA7f!w9Tx2E59jZ`tnUpb}n>D6Ju3dr@j(S}{ zRmFc+SNjt9GDaOCi$nu&?qa8wv&y%~du>w8W*J$3h(d7}GmXwYmv(FQ1cRqKa3-cN zD=|YqsQJqpFpeR`6Qdv%s67{zhAT{BrfI2m}oDXUud z#2sj!w>&!N0z75Ayz{LSe9!L2Wlj%uzxsL08d}$pj!L!Q46(TsfN2aBU*2jqJv59N zKdgaUH*+5@ ziM$vY3LOMbWwXCc%^mRd{~PDKkHWJ0>h0#{-8tNTfGJv-;01aNdb*9d^cZE=(=2us zX0j&T#G3k<=d?!i_+zI-2YdIei;#DR+~P9k`afvUQx-?+?;rXv46l(xENo;CUJVp0 zJYrDxvesg;6o)0*Gwh&(s#t8n=C;*Xp>a3#$%W5MFnZLYon~Sr@H6cd?X#%_Ut=+6 znVzc~{Z^I38A65|cQs>qQ=OC7#41)q6S|n#gH3^q{dSnbCG0rK(T#cHJ74@ledrwo z#Va|^g&2)dR-k92!4g2GdbsAaIZw$~G<{iT;9C8T(6bbVIkEgArQS4t8Fpv0h$6Je zSBo!Q#Ce)yFA0M}cN41~W?#}k+ze0rqKAW%i}X8WGkHin695nW7f|}IjVfSq3sDNm z?40^6A}3NEWK$&J;B$z8xUmvK~r81LHR9>{(R?HPBKwjXA zW)FRK*>RAGIH@Q48vBsfrJGCIr6#>+)7e1?$<{P>;;UuR0P%KR84p6@U50Y=)}q}z zI#-lcUn~65a%QvEVAr-RCe#;6z2J-gk!C3jGiOybE0YR(z!h`C^#HxyHuNY8>s@sE z;?Rzm`7s&uyZASiFwh;l(Ig+B9#~&Py6VpG6Z##XsHDOaD12GwyuVL-nL%c6=uQSj zn&k+rcKoiM;UYHCdB$i##O|ozSp+Tm$^!0K@m{vMTZ+|0CW1-ts@By5%tS`#)|?Rt zqgc_cgO$9ve}ChtQ;EUN<6rmWEaiRt@>bqwbxUxOIqpRF54=*|9kkOt*vrYsKL#6Z zPLB~;FwP#Mr>Ta&H+~T_m+hLbRVLqfwO5 z@KU%J;)^P2Aj;&(b%2d88~Pp!yz^gMPxYu&!tQ{n1qTg63EA9LJ_BSB@3$_Fm_2^o zCMlDvD3sl4J->0q4V+>tx)1d>wE2#FyFbE#8V=>VF@MOOoCMM{Cg$U7qf~viIr3gq z>|%LPHN?t0n0)5#r)vjZH?Acs)tJ${g|Z}nR_B+n2kzSrpB;i@yvMq0R1@|Bo6=;} zZ&b9)r#1x(u{xmIo$_(-SH7CS5}wY9BpMtYR{#COV&v1VZ5F-@og_}R(-4wEj>YXn zt223{VM*47bNvgG8vXSPILo{-EEz9h!|HEm*+WO%*2V!9oNgk0Cj%_1KWC{4qtDEIi7k@cS-YZ5Xz8c2>_U z-t<~+Ee<}&Y5_u?+OuEH7!am&Z~EuMtxl{k9j4UTp4t@#%p44uxdmb%Oyyyqt(w{Q zpv0XCpu&H;j6Shm#A(K8jvVQ)R1rUSx%TzC?QX8cm0lc>3>i+Y4jeWTEz-ucMM&s< zG`8OtD4;4fvWe6H-&rjBR$(wbB4MhWJ@d^XjbB^fStRwomP znU2pB#(lZ&qf-x;Yl7#VbF_<+s3SV~} zZCMz1*z+S;g&R)qU7ttzIo1*VfYyw%bKDI!SK46=uH5?cZkkuf{o@}Nuzk<{cP%}1 zv%EqwLqJd?Jbc``^UZrm>Vb*>hvDfF3qN=b#qaibpD@|H{T+dHgReP9PJKPC!f{hbLSi zeyk93Rw?|CBvl=tM$}U`x@XniDqI_PU?jC3LivoREVrub zEz(y!_7$^ntvPmn11y(WxQqx_HgP6DzhWQ>6x(dBNLtT5XE)t{FHqAdUHfRY%WiQq zRtxUc(VwvrhM$Q$qB>p}g6W7cXV7D2dAB*UqAJ>|teGb~l1VPAaLosJ_rYYNYTKuo zIDWHly=Q;~9H{xr%AP#S*z__0J8PFrJXyBDCU;47vEFr&JjCfM6Ma~V6eK+*6tZBx zXy=?7w=U1{dz>{Dl|QG|d-t&Ts4z7H?XytMFUpE($-A_Rb7ZyNWoFZRm$5i7d*AVX=8ldy3eTAk};&VrpOY$oazuxX7F=hxHVtrv^GlsG;EG#u(eu~M2YtVSB0_eMJ z2bx#jNl%I~G!+GO;44Sa0F~a4Er7`3-gm^ZqUSDi-Bmg{ovd|-UYQL)vgT|dclvc={PfivKu{kR617Gd4eOK77A&vaI zGeAWtVMMxjmL2(GeM#bhNJHt=T@@tF{7_FsmRQgL*kmqxn@f+`XC=Ah+v25n!-D$; zstCjRR9U0ObwA5_dRjSK4=HVqPh535RuG5sA}N?6NMaGa=QKhwXc-msD0$!5vZn~G zzo?{qUxk~i4%wKAh1m70AuHScY+BuGD>`G%MI%6QwALv;q^-3N&!`iWFi@`4z4WGy zP9sOL(}M+2AIRp*r%|23XIxEvAVE8S3~L44q& zop|_`O13V6{w9|m-EC-S+f_Q7O927YfyUHH&(1x1cs!8H1rp)>QvJ|m`{lTi!wSmk zCPiQe&;#VDYgu;XXP1pNa39|KTy{-+b4v>o>OKR4(ktx+xD#6VfRxN53;^TDq_-kJ zThO?H1|2iLw%5iPoliIs?wf?DIp8KG67P;~PV9iX9JvDJVK$)36X*tN11?f#KEF0ji@KUDS9zJ_-<80c4B8p`lu&n> z@iH*51Te52heq{(2L2gKMVLI2|6cWWuE08j1-_DqJq$fYNh1gJI}Q54TUdTJ72X$A z36Pj}9;(8sL3dofjF7KpvcNqX)-+>5mH{0=V`nlyQ0`QxWE1kUDEk^ScSFMZ0Grr( z^?V64m0s`JdEaATh)4vNvw0y#Ud%;yyA3Gl`KuE$j@PjeqW=4tRRl zJK4g#mrgs|AJ6)E^UL=ab6^ZSFP@+C^fbP_YSVUHt&lQs3)ugNUFW->omH!MfI8Q4 zXSRtgyx7f$iRsZ=(3kEbO=Y(d9$Ioa0(Y_h+ph!g8_pMU)V4b28)wQ-@O>%C-DLr& z#&;hA5PCK;%Rz23`e__GN%fR*Ydw>WO8JIlC*r#Hv9^g^|$d-oTr zVULifr#LVE-ANJIsT?=*obRqOi_c=aeu^It&|xXfAba3?{*N#J|9(&v;McFzPZqOhEB?7H zKn6Kf{r5k2jdg-P2I?keUH>2dZKV2e14AVZQl9}XJKO*0p*?Dot76`f`Af-z^j>AE zzy+5j`9GeGqe^d*haFq4=P8SB4YH^Ihd@;tsvAN+h^pl%vjrE<)Y-HBvtf_Hm5`%h|naIw%VWS1ZM95x+9kdAOZ-nb1sv0p8w190t{xz&IWT~9$z`9nCw4P zd4vUcdb4xDdlr>!{2&7)@+AjdyXa1v&FP?UZ?ag0X zyV>VhT%_$&H2%lacv_nN_NU1&{jp9rPj*(@17-0?bN>D#Op|}dT$F+C**7(q&`s{Z zFWQg(*&g;Dj&&CHkbcVz^?K2vAv!-%F6XELOqu)|R1#pR#@b0umc3gSC z!;iWU^@*iH41iVK=3_8ZX|2={dLb*vUpUasq2<2#!W`bXz#e6djFJO2g*wga<**7_ z8>pKhjvd=q#gW&IgG;8b)+=_sldZ~17%ydQGvO_5$nbr27sU{oIH+zqo2Y{@^)BqO zH75x2dd@HZO1{yS+vUTTGo=Hi5m7(ejZSt1X9i^PA3DdeN=AdxcYT~73n9QtG!W28 zNp1zDb8BB&_q*Pcg^PFtZpP>raZS;|o3>1RghG@TfKuJ&TpQ`#4|_1dx~CGDsSs=b z%EnL*D0FiYsv>Rb(!^V?zrA2Q&0zPL#)rqL-&dTfkc)W69XKWLQKG=tHZ3_n#7wH# zgq{esMhjD~-G2^#I)>m+0)`21z2V}1nKw96Tht?lFg_F#2Oqau*E#9+5%i&#I@sk1`j$a+P!rIA3IjG*&Y6-zGd65foPn<}(1=!Q|7^EZa%7sR!dHff;r5f1_A#h$Vzf}gA*OP-G}r4m%R!*_=5PAE{bn0GJK$AJ`-D@w1mH-% z@9PfX722jH6)=VfzALtfTf3I8$hBL1BHFopJN9ISw$*- zF$Ph^eRvGGe!AJT`d~It1mUOWDY>Q)P!R54Zq3^kS87 zK~dvC`FLO4`K8I?1(SYHV=maNYN~$1!e4l{(lw38kr3Q6RPluxTZYTjcd9mvrFJ(F z`z+(eiiIg#TUtyHnx!^baiA3nT0> zn1dzR zFn`01i=*SJX4_mwEqq(Q`+Elv1~m zdQ=o2V1;xKV6N(GdrkthR_NHuG{PcX=%Hl}Qyh!xqIfbtZTihTO@(f&<$P>qyfQnS zmw@XkxwdjY2lF!qVv~R&$PC9x3=qS4BQ?4+4VjC<(MH+|F=ES}gu@LUW-WtQx7Q^1 zE^YRSkY3mI4I1I+Jmq0E)BKY}A2GK?JiC7OiD*bVniehlQ=-&x(>`TsN|}V`~^jfJ>b%ka1txh7=24Mqg9=Z`N~o&2OFB`GZV;J ztXtOib0g_YI-cnzWD)bP^LSA>#8J>0+u2lx4f+uoY{N(qbTdLEKxp!?AsSDrv0Vds zAjxr)p#=3`E+JZsh8IYqd}2g2Kq%LHItri$w3P=j8ADYz=s{NLa`4{k1=Y=oLn=lt fg+W=D0~cg4=k5F~3I-kz10T-=zV6ko)QtZDj6CA{ diff --git a/assets/tron.png b/assets/tron.png new file mode 100644 index 0000000000000000000000000000000000000000..fb3fea3ef6f00d1f726569d4ba7aec429b67b78c GIT binary patch literal 14975 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 rA#4Rwb5~%jEA36}&X-GurMsLXC32 str: reserved = get_func_queue("reserved") if ENABLE_CELERY and reserved: - text = f"Too many tasks. Your tasks was added to the reserved queue {reserved}." + text = f"Your tasks was added to the reserved queue {reserved}. Processing...\n\n" else: text = "Your task was added to active queue.\nProcessing...\n\n" diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 33b2ff83..b18c6852 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -26,7 +26,14 @@ import yt_dlp as ytdl from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6, SS_YOUTUBE +from config import ( + AUDIO_FORMAT, + ENABLE_ARIA2, + ENABLE_FFMPEG, + SS_YOUTUBE, + TG_MAX_SIZE, + IPv6, +) from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index a7ccc8c7..d1929488 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -12,6 +12,11 @@ import time import requests +from tronpy import Tron +from tronpy.exceptions import TransactionError, ValidationError +from tronpy.hdwallet import key_from_seed, seed_from_mnemonic +from tronpy.keys import PrivateKey +from tronpy.providers import HTTPProvider from config import ( AFD_TOKEN, @@ -21,6 +26,9 @@ FREE_DOWNLOAD, OWNER, TOKEN_PRICE, + TRON_MNEMONIC, + TRONGRID_KEY, + TRX_SIGNAL, ) from database import MySQL, Redis from utils import apply_log_formatter, current_time @@ -95,6 +103,80 @@ def get_user_payment(self, trade_no: str) -> (int, float, str): return amount / 7, trade_no +class TronTrx: + def __init__(self): + if TRON_MNEMONIC == "cram floor today legend service drill pitch leaf car govern harvest soda": + logging.warning("Using nile testnet") + provider = HTTPProvider(endpoint_uri="https://nile.trongrid.io") + network = "nile" + else: + provider = HTTPProvider(api_key=TRONGRID_KEY) + network = "mainnet" + self.client = Tron(provider, network=network) + + def central_transfer(self, from_, index, amount: int): + logging.info("Generated key with index %s", index) + seed = seed_from_mnemonic(TRON_MNEMONIC, passphrase="") + key = PrivateKey(key_from_seed(seed, account_path=f"m/44'/195'/1'/0/{index}")) + central = self.central_wallet() + logging.info("Transfer %s TRX from %s to %s", amount, from_, central) + try: + self.client.trx.transfer(from_, central, amount).build().sign(key).broadcast() + except (TransactionError, ValidationError): + logging.error("Failed to transfer %s TRX to %s. Lower and try again.", amount, from_) + if amount > 1_100_000: + # 1.1 trx transfer fee + self.client.trx.transfer(from_, central, amount - 1_100_000).build().sign(key).broadcast() + + def central_wallet(self): + wallet = self.client.generate_address_from_mnemonic(TRON_MNEMONIC, account_path="m/44'/195'/0'/0/0") + return wallet["base58check_address"] + + def get_payment_address(self, user_id): + # payment_id is like tron,0,TN8Mn9KKv3cSrKyrt6Xx5L18nmezbpiW31,index where 0 means unpaid + db = MySQL() + con = db.con + cur = db.cur + cur.execute("select user_id from payment where payment_id like 'tron,%'") + data = cur.fetchall() + index = len(data) + path = f"m/44'/195'/1'/0/{index}" + logging.info("Generating address for user %s with path %s", user_id, path) + addr = self.client.generate_address_from_mnemonic(TRON_MNEMONIC, account_path=path)["base58check_address"] + # add row in db, unpaid + cur.execute("insert into payment values (%s,%s,%s,%s,%s)", (user_id, 0, f"tron,0,{addr},{index}", 0, 0)) + con.commit() + return addr + + def check_payment(self): + db = MySQL() + con = db.con + cur = db.cur + + cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'") + data = cur.fetchall() + logging.info("Checking %s unpaid payment", len(data)) + for row in data: + user_id = row[0] + addr, index = row[1].split(",")[2:] + try: + balance = self.client.get_account_balance(addr) + except: + balance = 0 + if balance: + logging.info("User %s has %s TRX", user_id, balance) + # paid, calc token count + token_count = int(balance / 10 * TOKEN_PRICE) + cur.execute( + "update payment set token=%s,payment_id=%s where user_id=%s and payment_id like %s", + (token_count, f"tron,1,{addr},{index}", user_id, f"tron,%{addr}%"), + ) + con.commit() + self.central_transfer(addr, index, int(balance * 1_000_000)) + logging.debug("Dispatch signal now....") + TRX_SIGNAL.send("cron", user_id=user_id, text=f"{balance} TRX received, {token_count} tokens added.") + + class Payment(Redis, MySQL): def check_old_user(self, user_id: int) -> tuple: self.cur.execute("SELECT * FROM payment WHERE user_id=%s AND old_user=1", (user_id,)) @@ -168,3 +250,9 @@ def verify_payment(self, user_id: int, unique: str) -> str: return "Payment not found. Please check your payment ID or email address" self.add_pay_user([user_id, amount, pay_id, 0, amount * TOKEN_PRICE]) return "Thanks! Your payment has been verified. /start to get your token details" + + +if __name__ == "__main__": + a = TronTrx() + # a.central_wallet() + a.check_payment() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 16cedfb2..a70040a0 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -43,10 +43,10 @@ ENABLE_QUEUE, ENABLE_VIP, OWNER, - RCLONE_PATH, RATE_LIMIT, - WORKERS, + RCLONE_PATH, TMPFILE_PATH, + WORKERS, ) from constant import BotText from database import Redis diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index cc7713da..af54a86f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -20,6 +20,7 @@ from pathlib import Path import pyrogram.errors +import qrcode import requests import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler @@ -37,16 +38,17 @@ ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, + M3U8_SUPPORT, OWNER, PLAYLIST_SUPPORT, - M3U8_SUPPORT, PROVIDER_TOKEN, REQUIRED_MEMBERSHIP, TOKEN_PRICE, + TRX_SIGNAL, ) from constant import BotText from database import InfluxDB, MySQL, Redis -from limit import Payment +from limit import Payment, TronTrx from tasks import app as celery_app from tasks import ( audio_entrance, @@ -60,8 +62,7 @@ customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) logging.getLogger("apscheduler.executors.default").propagate = False -session = "ytdl-main" -app = create_app(session) +app = create_app(":memory:") logging.info("Authorized users are %s", AUTHORIZED_USER) redis = Redis() @@ -286,7 +287,6 @@ def settings_handler(client: Client, message: types.Message): def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id - text = message.text.strip() client.send_chat_action(chat_id, "typing") client.send_message(chat_id, BotText.buy, disable_web_page_preview=True) # generate telegram invoice here @@ -309,7 +309,12 @@ def buy_handler(client: Client, message: types.Message): message="Buy more download token", ) ) - client.send_message(chat_id, "In /settings, change your download mode to Local will make the download faster!") + + addr = TronTrx().get_payment_address(chat_id) + with BytesIO() as bio: + qr = qrcode.make(addr) + qr.save(bio) + client.send_photo(chat_id, bio, caption=f"Send TRX to `{addr}`") @app.on_message(filters.command(["redeem"])) @@ -517,13 +522,22 @@ def temp_fix_The_msg_id_is_too_low(): os.remove(s_file_path) +def trx_notify(_, **kwargs): + user_id = kwargs.get("user_id") + text = kwargs.get("text") + logging.info("Sending trx notification to %s", user_id) + app.send_message(user_id, text) + + if __name__ == "__main__": MySQL() + TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) scheduler.add_job(auto_restart, "interval", seconds=600) - scheduler.add_job(clean_tempfile, "interval", seconds=60) - scheduler.add_job(InfluxDB().collect_data, "interval", seconds=60) + scheduler.add_job(clean_tempfile, "interval", seconds=120) + scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) + scheduler.add_job(TronTrx().check_payment, "interval", seconds=60) # default quota allocation of 10,000 units per day scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() From c67a548a42474a6164289f6be0e95499c8015e29 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 9 Dec 2023 17:10:39 +0100 Subject: [PATCH 138/297] add instagram built-in support --- README.md | 2 +- ytdlbot/config.py | 3 +- ytdlbot/downloader.py | 86 ++++++++++++++++++++++++++++++------------- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 62d9ce1a..481f6a9d 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ This bot can be deployed on any platform that supports Python. To deploy this bot, follow these steps: 1. Install bot dependencies - * Install Python 3.6 or a later version, FFmpeg. + * Install Python 3.8 or a later version, FFmpeg. * (optional)Aria2 and add it to the PATH. 2. Clone the code from the repository and cd into it. diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 3a24b73d..61a249d2 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -56,9 +56,8 @@ RATE_LIMIT = os.getenv("RATE_LIMIT", 120) -SS_YOUTUBE = os.getenv("SS_YOUTUBE", "https://ytdlbot.dmesg.app?token=123456") RCLONE_PATH = os.getenv("RCLONE") - +INSTAGRAM_SESSION = os.getenv("INSTAGRAM_SESSION", "") # This will set the value for the tmpfile path(download path) if it is set. # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. # Please ensure that the directory exists and you have necessary permissions to write to it. diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index b18c6852..13781175 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -30,7 +30,7 @@ AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, - SS_YOUTUBE, + INSTAGRAM_SESSION, TG_MAX_SIZE, IPv6, ) @@ -256,29 +256,6 @@ def convert_audio_format(video_paths: list, bm): video_paths[index] = new_path -def download_instagram(url: str, tempdir: str): - if url.startswith("https://www.instagram.com"): - logging.info("Requesting instagram download link for %s", url) - api = SS_YOUTUBE + f"&url={url}" - res = requests.get(api).json() - if isinstance(res, dict): - downloadable = {i["url"]: i["ext"] for i in res["url"]} - else: - downloadable = {i["url"]: i["ext"] for item in res for i in item["url"]} - - for link, ext in downloadable.items(): - save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") - with open(save_path, "wb") as f: - f.write(requests.get(link, stream=True).content) - # telegram send webp as sticker, so we'll convert it to png - for path in pathlib.Path(tempdir).glob("*.webp"): - logging.info("Converting %s to png", path) - new_path = path.with_suffix(".jpg") - ffmpeg.input(path).output(new_path.as_posix()).run() - path.unlink() - return True - - def split_large_video(video_paths: list): original_video = None split = False @@ -294,6 +271,63 @@ def split_large_video(video_paths: list): return [i for i in pathlib.Path(original_video).parent.glob("*")] +def download_instagram(url: str, tempdir: str): + if not url.startswith("https://www.instagram.com"): + return + logging.info("Requesting instagram download link for %s", url) + cookies = {"sessionid": INSTAGRAM_SESSION} + + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "cross-site", + "Pragma": "no-cache", + "Cache-Control": "no-cache", + } + + params = {"__a": "1", "__d": "dis"} + + try: + data = requests.get(url, params=params, cookies=cookies, headers=headers).json() + except Exception: + logging.warning("Can't get instagram data for %s", url) + return + + url_results = [] + if carousels := data["items"][0].get("carousel_media"): + for carousel in carousels: + if carousel.get("video_versions"): + url_results.append(carousel["video_versions"][0]["url"]) + else: + url_results.append(carousel["image_versions2"]["candidates"][0]["url"]) + + elif video := data["items"][0].get("video_versions"): + url_results.append(video[0]["url"]) + elif image := data["items"][0].get("image_versions2"): + url_results.append(image["candidates"][0]["url"]) + else: + logging.warning("Can't find any downloadable link for %s", url) + return + + for link in url_results: + content = requests.get(link, stream=True).content + ext = filetype.guess_extension(content) + save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") + with open(save_path, "wb") as f: + f.write(content) + + return True + + if __name__ == "__main__": - a = download_instagram("https://www.instagram.com/p/CrEAz-AI99Y/", "tmp") - print(a) + pass + # download_instagram("https://www.instagram.com/p/CXpxSyOrWCA/", "image") + # download_instagram("https://www.instagram.com/p/Cah_7gnDVUW/", "video") + # download_instagram("https://www.instagram.com/p/C0ozGsjtY0W/", "reels") + # download_instagram("https://www.instagram.com/p/C0ozPQ5o536/?img_index=1", "image carousel") + # download_instagram("https://www.instagram.com/p/C0ozhsVo-m8/?img_index=1", "video image carousel") From c16718d6787991182aae928e7c45d0c2a9672e59 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 9 Dec 2023 17:21:50 +0100 Subject: [PATCH 139/297] remove search --- ytdlbot/ytdl_bot.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index af54a86f..e2061734 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -21,7 +21,6 @@ import pyrogram.errors import qrcode -import requests import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler from pyrogram import Client, filters, types @@ -342,17 +341,6 @@ def generate_invoice(amount: int, title: str, description: str, payload: str): return invoice -def search(kw: str): - api = f"https://dmesg.app/ytdlbot/search.php?search={kw}" - # title, url, time, image - text, index = "", 1 - for item in requests.get(api).json()["results"][:10]: - item["index"] = index - text += "{index}. {title}\n{url}\n{time}\n\n".format(**item) - index += 1 - return text - - def link_checker(url: str) -> str: if url.startswith("https://www.instagram.com"): return "" @@ -392,8 +380,7 @@ def download_handler(client: Client, message: types.Message): # check url if not re.findall(r"^https?://", url.lower()): redis.update_metrics("bad_request") - text = search(url) - message.reply_text(text, quote=True) + message.reply_text("please send an URL", quote=True) return if text := link_checker(url): From 25e3e724d13189430aec614f6ca2bfd5c577aff0 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 9 Dec 2023 17:57:26 +0100 Subject: [PATCH 140/297] move qrcode and pay btn in later response --- ytdlbot/constant.py | 23 +++++++++--------- ytdlbot/limit.py | 2 +- ytdlbot/ytdl_bot.py | 57 ++++++++++++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index d297b664..dcb25e28 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -23,11 +23,11 @@ class BotText: start = "Welcome to YouTube Download bot. Type /help for more information." + help = f""" 1. This bot should work at all times. If it doesn't, please wait for a few minutes and try sending the link again. -2. Source code is here: https://github.com/tgbot-collection/ytdlbot - +2. Source code: https://github.com/tgbot-collection/ytdlbot """ about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" @@ -36,34 +36,35 @@ class BotText: **Terms:** 1. You can use this bot to download video for {FREE_DOWNLOAD} times within a 24-hour period. -2. You can purchase additional download tokens, which will be valid permanently. +2. You can buy additional download tokens, valid permanently. 3. Refunds are possible, contact me if you need that @BennyThink 4. Paid user can change default download mode to Local mode in settings, which is faster. -**Download token price:** +**Price:** valid permanently 1. 1 USD == {TOKEN_PRICE} tokens 2. 7 CNY == {TOKEN_PRICE} tokens 3. 10 TRX == {TOKEN_PRICE} tokens **Payment options:** -Pay the amount you want. For example you can send 20 TRX for {TOKEN_PRICE * 2} tokens. +Pay any amount you want. For example you can send 20 TRX for {TOKEN_PRICE * 2} tokens. 1. AFDIAN(AliPay, WeChat Pay and PayPal): {AFD_LINK} 2. Buy me a coffee: {COFFEE_LINK} -3. Telegram Payment(Stripe), see following invoice. -4. TRON(TRX), see following QR code. +3. Telegram Bot Payment(Stripe), please click Bot Payment button. +4. TRON(TRX), please click TRON(TRX) button. **After payment:** - -1. Afdian: Provide your order number with the /redeem command (e.g., `/redeem 123456`). -2. Buy Me a Coffee: Provide your email with the /redeem command (e.g., `/redeem some@one.com`). **Use different email each time.** -3. Telegram Payment & Tron(TRX): Your payment will be automatically activated within 60s. Check /start to see your balance. +1. Afdian: attach order number with /redeem command (e.g., `/redeem 123456`). +2. Buy Me a Coffee: attach email with /redeem command (e.g., `/redeem 123@x.com`). **Use different email each time.** +3. Telegram Payment & Tron(TRX): automatically activated within 60s. Check /start to see your balance. Want to buy more token with Telegram payment? Let's say 100? Here you go! `/buy 123` """ + private = "This bot is for private use" + membership_require = f"You need to join this group or channel to use this bot\n\nhttps://t.me/{REQUIRED_MEMBERSHIP}" settings = """ diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index d1929488..ef6ae47c 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -155,7 +155,7 @@ def check_payment(self): cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'") data = cur.fetchall() - logging.info("Checking %s unpaid payment", len(data)) + logging.info("Checking unpaid payment...%s", data) for row in data: user_id = row[0] addr, index = row[1].split(",")[2:] diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index e2061734..ff9a4b82 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -17,7 +17,6 @@ import traceback import typing from io import BytesIO -from pathlib import Path import pyrogram.errors import qrcode @@ -287,19 +286,47 @@ def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id client.send_chat_action(chat_id, "typing") - client.send_message(chat_id, BotText.buy, disable_web_page_preview=True) - # generate telegram invoice here - payload = f"{message.chat.id}-buy" - token_count = message.text.replace("/buy", "").strip() # currency USD + token_count = message.text.replace("/buy", "").strip() if token_count.isdigit(): price = int(int(token_count) / TOKEN_PRICE * 100) else: price = 100 - invoice = generate_invoice( - price, f"Buy {TOKEN_PRICE} download tokens", "You can pay by Telegram payment or using link above", payload + + markup = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"), + InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"), + ], + ] ) + client.send_message(chat_id, BotText.buy, disable_web_page_preview=True, reply_markup=markup) + + +@app.on_callback_query(filters.regex(r"tron-trx")) +def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery): + callback_query.answer("Generating QR code...") + chat_id = callback_query.message.chat.id + client.send_chat_action(chat_id, "typing") + + addr = TronTrx().get_payment_address(chat_id) + with BytesIO() as bio: + qr = qrcode.make(addr) + qr.save(bio) + client.send_photo(chat_id, bio, caption=f"Send any amount of TRX to `{addr}`") + +@app.on_callback_query(filters.regex(r"bot-payments-.*")) +def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery): + callback_query.answer("Generating invoice...") + chat_id = callback_query.message.chat.id + client.send_chat_action(chat_id, "typing") + + data = callback_query.data + price = int(data.split("-")[-1]) + payload = f"{chat_id}-buy" + invoice = generate_invoice(price, f"Buy {TOKEN_PRICE} download tokens", "Pay by card", payload) app.send( functions.messages.SendMedia( peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), @@ -309,12 +336,6 @@ def buy_handler(client: Client, message: types.Message): ) ) - addr = TronTrx().get_payment_address(chat_id) - with BytesIO() as bio: - qr = qrcode.make(addr) - qr.save(bio) - client.send_photo(chat_id, bio, caption=f"Send TRX to `{addr}`") - @app.on_message(filters.command(["redeem"])) def redeem_handler(client: Client, message: types.Message): @@ -501,14 +522,6 @@ def raw_update(client: Client, update, users, chats): client.send_message(uid, f"Thank you {uid}. Payment received: {amount} {action.currency}") -def temp_fix_The_msg_id_is_too_low(): - current_dir = Path(__file__).parent - s_file_path = current_dir / "ytdl-main.session" - if os.path.exists(s_file_path): - print(f"Deleting session file :", s_file_path) - os.remove(s_file_path) - - def trx_notify(_, **kwargs): user_id = kwargs.get("user_id") text = kwargs.get("text") @@ -524,7 +537,7 @@ def trx_notify(_, **kwargs): scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) - scheduler.add_job(TronTrx().check_payment, "interval", seconds=60) + scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) # default quota allocation of 10,000 units per day scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() From bbc357e7b5a35b0cbbaeb1498bc379f7417603bf Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 10 Dec 2023 21:29:35 +0100 Subject: [PATCH 141/297] Correct balance and transfer amount in TRX The code has been updated to display the account balance and to ask for the transfer amount in TRX. The transfer method has also been modified to facilitate transactions in multiples of 1,000,000. --- scripts/transfer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/transfer.py b/scripts/transfer.py index e19fe99e..2ace122f 100644 --- a/scripts/transfer.py +++ b/scripts/transfer.py @@ -12,9 +12,10 @@ client = Tron(network="nile") from_ = client.generate_address_from_mnemonic(mnemonic, account_path="m/44'/195'/0'/0/0")["base58check_address"] -print("my addr: ", from_) +balance = client.get_account_balance(from_) +print("my addr: ", from_, "balance: ", balance) to = input("to: ") -amount = int(input("amount: 1_000_000\n")) +amount = int(input("amount in TRX: ")) def mnemonic_to_private_key(): @@ -23,6 +24,6 @@ def mnemonic_to_private_key(): return PrivateKey(private_key) -t = client.trx.transfer(from_, to, amount).build().inspect().sign(mnemonic_to_private_key()).broadcast() +t = client.trx.transfer(from_, to, amount * 1_000_000).build().sign(mnemonic_to_private_key()).broadcast() print(t.wait()) From 419cb2259da05e6a5fe33b25e54a933d357fd9e6 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 12 Dec 2023 17:50:37 +0100 Subject: [PATCH 142/297] auto Local and channel link --- ytdlbot/constant.py | 6 +++--- ytdlbot/limit.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index dcb25e28..954b020d 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -22,10 +22,10 @@ class BotText: - start = "Welcome to YouTube Download bot. Type /help for more information." + start = "Welcome to YouTube Download bot. Type /help for more information. Join https://t.me/+OGRC8tp9-U9mZDZl for updates." help = f""" -1. This bot should work at all times. If it doesn't, please wait for a few minutes and try sending the link again. +1. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. 2. Source code: https://github.com/tgbot-collection/ytdlbot """ @@ -40,7 +40,7 @@ class BotText: 3. Refunds are possible, contact me if you need that @BennyThink -4. Paid user can change default download mode to Local mode in settings, which is faster. +4. Download for paid user will be automatically changed to Local mode to avoid queuing. **Price:** valid permanently diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index ef6ae47c..978e6cce 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -171,6 +171,7 @@ def check_payment(self): "update payment set token=%s,payment_id=%s where user_id=%s and payment_id like %s", (token_count, f"tron,1,{addr},{index}", user_id, f"tron,%{addr}%"), ) + cur.execute("UPDATE settings SET mode='Local' WHERE user_id=%s", (user_id,)) con.commit() self.central_transfer(addr, index, int(balance * 1_000_000)) logging.debug("Dispatch signal now....") @@ -231,6 +232,7 @@ def use_token(self, user_id: int): def add_pay_user(self, pay_data: list): self.cur.execute("INSERT INTO payment VALUES (%s,%s,%s,%s,%s)", pay_data) + self.set_user_settings(pay_data[0], "mode", "Local") self.con.commit() def verify_payment(self, user_id: int, unique: str) -> str: From d09c4a59149472cc0d276c173dfcc485c504bf4c Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 12 Dec 2023 18:24:21 +0100 Subject: [PATCH 143/297] add instagram as microservice --- docker-compose.yml | 10 +++++++ requirements.txt | 1 + ytdlbot/downloader.py | 70 +++++++------------------------------------ ytdlbot/instagram.py | 68 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 60 deletions(-) create mode 100644 ytdlbot/instagram.py diff --git a/docker-compose.yml b/docker-compose.yml index d43a3f8e..37f6faa2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,3 +51,13 @@ services: - ./data/flower:/ytdlbot/ytdlbot/flower ports: - "127.0.0.1:15555:5555" + + instagram: + image: bennythink/ytdlbot + env_file: + - env/ytdl.env + restart: always + command: [ "/usr/local/bin/python", "/ytdlbot/ytdlbot/instagram.py" ] + + ports: + - "127.0.0.1:15000:5000" diff --git a/requirements.txt b/requirements.txt index 53674935..4a0e5f78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,3 +26,4 @@ tronpy==0.4.0 mnemonic==0.20 qrcode==7.4.2 blinker==1.7.0 +flask===3.0.0 diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 13781175..d18f8c66 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -30,7 +30,6 @@ AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, - INSTAGRAM_SESSION, TG_MAX_SIZE, IPv6, ) @@ -272,62 +271,13 @@ def split_large_video(video_paths: list): def download_instagram(url: str, tempdir: str): - if not url.startswith("https://www.instagram.com"): - return - logging.info("Requesting instagram download link for %s", url) - cookies = {"sessionid": INSTAGRAM_SESSION} - - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - "Connection": "keep-alive", - "Upgrade-Insecure-Requests": "1", - "Sec-Fetch-Dest": "document", - "Sec-Fetch-Mode": "navigate", - "Sec-Fetch-Site": "cross-site", - "Pragma": "no-cache", - "Cache-Control": "no-cache", - } - - params = {"__a": "1", "__d": "dis"} - - try: - data = requests.get(url, params=params, cookies=cookies, headers=headers).json() - except Exception: - logging.warning("Can't get instagram data for %s", url) - return - - url_results = [] - if carousels := data["items"][0].get("carousel_media"): - for carousel in carousels: - if carousel.get("video_versions"): - url_results.append(carousel["video_versions"][0]["url"]) - else: - url_results.append(carousel["image_versions2"]["candidates"][0]["url"]) - - elif video := data["items"][0].get("video_versions"): - url_results.append(video[0]["url"]) - elif image := data["items"][0].get("image_versions2"): - url_results.append(image["candidates"][0]["url"]) - else: - logging.warning("Can't find any downloadable link for %s", url) - return - - for link in url_results: - content = requests.get(link, stream=True).content - ext = filetype.guess_extension(content) - save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") - with open(save_path, "wb") as f: - f.write(content) - - return True - - -if __name__ == "__main__": - pass - # download_instagram("https://www.instagram.com/p/CXpxSyOrWCA/", "image") - # download_instagram("https://www.instagram.com/p/Cah_7gnDVUW/", "video") - # download_instagram("https://www.instagram.com/p/C0ozGsjtY0W/", "reels") - # download_instagram("https://www.instagram.com/p/C0ozPQ5o536/?img_index=1", "image carousel") - # download_instagram("https://www.instagram.com/p/C0ozhsVo-m8/?img_index=1", "video image carousel") + resp = requests.get(f"https://ytdlbot.dmesg.app/?url={url}").json() + if url_results := resp.get("data"): + for link in url_results: + content = requests.get(link, stream=True).content + ext = filetype.guess_extension(content) + save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") + with open(save_path, "wb") as f: + f.write(content) + + return True diff --git a/ytdlbot/instagram.py b/ytdlbot/instagram.py new file mode 100644 index 00000000..7939171f --- /dev/null +++ b/ytdlbot/instagram.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - instagram.py +# 2023-12-12 17:42 + +import logging + +import requests +from flask import Flask, request + +from config import INSTAGRAM_SESSION +from utils import apply_log_formatter + +apply_log_formatter() +app = Flask(__name__) + + +@app.route("/") +def index(): + url = request.args.get("url") + if not url.startswith("https://www.instagram.com"): + return {} + + logging.info("Requesting instagram download link for %s", url) + cookies = {"sessionid": INSTAGRAM_SESSION} + + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "cross-site", + "Pragma": "no-cache", + "Cache-Control": "no-cache", + } + + params = {"__a": "1", "__d": "dis"} + + try: + data = requests.get(url, params=params, cookies=cookies, headers=headers).json() + except Exception: + logging.warning("Can't get instagram data for %s", url) + return {} + + url_results = [] + if carousels := data["items"][0].get("carousel_media"): + for carousel in carousels: + if carousel.get("video_versions"): + url_results.append(carousel["video_versions"][0]["url"]) + else: + url_results.append(carousel["image_versions2"]["candidates"][0]["url"]) + + elif video := data["items"][0].get("video_versions"): + url_results.append(video[0]["url"]) + elif image := data["items"][0].get("image_versions2"): + url_results.append(image["candidates"][0]["url"]) + else: + logging.warning("Can't find any downloadable link for %s", url) + return {} + return {"data": url_results} + + +if __name__ == "__main__": + app.run(debug=False, host="0.0.0.0") From acc07f760ce2312a1fd2b5a2175ec86346ec963c Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 12 Dec 2023 18:24:29 +0100 Subject: [PATCH 144/297] bring back search --- ytdlbot/ytdl_bot.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ff9a4b82..02f95934 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -28,6 +28,7 @@ from pyrogram.raw import types as raw_types from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime +from youtubesearchpython import VideosSearch from channel import Channel from client_init import create_app @@ -380,6 +381,19 @@ def link_checker(url: str) -> str: return "Live stream links are disabled. Please download it after the stream ends." +def search_ytb(kw: str): + videosSearch = VideosSearch(kw, limit=10) + + text = "" + results = videosSearch.result()["result"] + for item in results: + title = item.get("title") + link = item.get("link") + index = results.index(item) + 1 + text += f"{index}. {title}\n{link}\n\n" + return text + + @app.on_message(filters.incoming & (filters.text | filters.document)) @private_use def download_handler(client: Client, message: types.Message): @@ -401,7 +415,8 @@ def download_handler(client: Client, message: types.Message): # check url if not re.findall(r"^https?://", url.lower()): redis.update_metrics("bad_request") - message.reply_text("please send an URL", quote=True) + text = search_ytb(url) + message.reply_text(text, quote=True, disable_web_page_preview=True) return if text := link_checker(url): From 320f0d5ffebb53bee2aa56509a0b1a5b7f42ca08 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 12 Dec 2023 18:36:27 +0100 Subject: [PATCH 145/297] instagram download on wg --- README.md | 8 +++++--- docker-compose.yml | 3 --- ytdlbot/downloader.py | 5 ++++- ytdlbot/instagram.py | 4 +--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 481f6a9d..06b69196 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ longer be available.** [https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) +Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. + Send link directly to the bot. Any Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) will work t0o. @@ -79,14 +81,14 @@ To deploy this bot, follow these steps: * ```Bash git clone https://github.com/tgbot-collection/ytdlbot ``` - * ```Bash + * ```Bash cd ytdlbot/ ``` 3. Creating a virtual environment and installing required modules in Python. * ```Python python -m venv venv ``` - * ```Bash + * ```Bash source venv/bin/activate # Linux #or .\venv\Scripts\activate # Windows @@ -94,7 +96,7 @@ To deploy this bot, follow these steps: * ```Python pip install --upgrade pip ``` - * ```Python + * ```Python pip install -r requirements.txt ``` 4. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. diff --git a/docker-compose.yml b/docker-compose.yml index 37f6faa2..3db5eeb5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,6 +58,3 @@ services: - env/ytdl.env restart: always command: [ "/usr/local/bin/python", "/ytdlbot/ytdlbot/instagram.py" ] - - ports: - - "127.0.0.1:15000:5000" diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index d18f8c66..d8e78b76 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -271,7 +271,10 @@ def split_large_video(video_paths: list): def download_instagram(url: str, tempdir: str): - resp = requests.get(f"https://ytdlbot.dmesg.app/?url={url}").json() + if not url.startswith("https://www.instagram.com"): + return False + + resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() if url_results := resp.get("data"): for link in url_results: content = requests.get(link, stream=True).content diff --git a/ytdlbot/instagram.py b/ytdlbot/instagram.py index 7939171f..68ad7e10 100644 --- a/ytdlbot/instagram.py +++ b/ytdlbot/instagram.py @@ -19,8 +19,6 @@ @app.route("/") def index(): url = request.args.get("url") - if not url.startswith("https://www.instagram.com"): - return {} logging.info("Requesting instagram download link for %s", url) cookies = {"sessionid": INSTAGRAM_SESSION} @@ -65,4 +63,4 @@ def index(): if __name__ == "__main__": - app.run(debug=False, host="0.0.0.0") + app.run(host="0.0.0.0", port=80) From 5df6ab3f89fc7984b75d3fb11b0409cbf9b4e827 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 12 Dec 2023 20:57:05 +0100 Subject: [PATCH 146/297] fix README.md --- .github/workflows/builder.yaml | 2 ++ README.md | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index 958bd716..e6d548a0 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -1,6 +1,8 @@ name: docker image builder on: push: + paths-ignore: + - '**.md' branches: - 'master' diff --git a/README.md b/README.md index 06b69196..81e0623e 100644 --- a/README.md +++ b/README.md @@ -81,14 +81,14 @@ To deploy this bot, follow these steps: * ```Bash git clone https://github.com/tgbot-collection/ytdlbot ``` - * ```Bash + * ```Bash cd ytdlbot/ ``` 3. Creating a virtual environment and installing required modules in Python. * ```Python python -m venv venv ``` - * ```Bash + * ```Bash source venv/bin/activate # Linux #or .\venv\Scripts\activate # Windows @@ -96,7 +96,7 @@ To deploy this bot, follow these steps: * ```Python pip install --upgrade pip ``` - * ```Python + * ```Python pip install -r requirements.txt ``` 4. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. From 23f617e5886e0c61b5b90bd403aff2119124179d Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 13 Dec 2023 18:25:54 +0100 Subject: [PATCH 147/297] add backup bot --- ytdlbot/config.py | 3 +- ytdlbot/constant.py | 5 +++- ytdlbot/instagram.py | 66 -------------------------------------------- ytdlbot/ytdl_bot.py | 12 ++++---- 4 files changed, 12 insertions(+), 74 deletions(-) delete mode 100644 ytdlbot/instagram.py diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 61a249d2..84f51690 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -55,9 +55,8 @@ ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) RATE_LIMIT = os.getenv("RATE_LIMIT", 120) - +IS_BACKUP_BOT = os.getenv("IS_BACKUP_BOT") RCLONE_PATH = os.getenv("RCLONE") -INSTAGRAM_SESSION = os.getenv("INSTAGRAM_SESSION", "") # This will set the value for the tmpfile path(download path) if it is set. # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. # Please ensure that the directory exists and you have necessary permissions to write to it. diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 954b020d..14750a1d 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -22,7 +22,10 @@ class BotText: - start = "Welcome to YouTube Download bot. Type /help for more information. Join https://t.me/+OGRC8tp9-U9mZDZl for updates." + start = """ + Welcome to YouTube Download bot. Type /help for more information. + Backup bot: @benny_2ytdlbot + Join https://t.me/+OGRC8tp9-U9mZDZl for updates.""" help = f""" 1. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. diff --git a/ytdlbot/instagram.py b/ytdlbot/instagram.py deleted file mode 100644 index 68ad7e10..00000000 --- a/ytdlbot/instagram.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 - -# ytdlbot - instagram.py -# 2023-12-12 17:42 - -import logging - -import requests -from flask import Flask, request - -from config import INSTAGRAM_SESSION -from utils import apply_log_formatter - -apply_log_formatter() -app = Flask(__name__) - - -@app.route("/") -def index(): - url = request.args.get("url") - - logging.info("Requesting instagram download link for %s", url) - cookies = {"sessionid": INSTAGRAM_SESSION} - - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - "Connection": "keep-alive", - "Upgrade-Insecure-Requests": "1", - "Sec-Fetch-Dest": "document", - "Sec-Fetch-Mode": "navigate", - "Sec-Fetch-Site": "cross-site", - "Pragma": "no-cache", - "Cache-Control": "no-cache", - } - - params = {"__a": "1", "__d": "dis"} - - try: - data = requests.get(url, params=params, cookies=cookies, headers=headers).json() - except Exception: - logging.warning("Can't get instagram data for %s", url) - return {} - - url_results = [] - if carousels := data["items"][0].get("carousel_media"): - for carousel in carousels: - if carousel.get("video_versions"): - url_results.append(carousel["video_versions"][0]["url"]) - else: - url_results.append(carousel["image_versions2"]["candidates"][0]["url"]) - - elif video := data["items"][0].get("video_versions"): - url_results.append(video[0]["url"]) - elif image := data["items"][0].get("image_versions2"): - url_results.append(image["candidates"][0]["url"]) - else: - logging.warning("Can't find any downloadable link for %s", url) - return {} - return {"data": url_results} - - -if __name__ == "__main__": - app.run(host="0.0.0.0", port=80) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 02f95934..3b78f881 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -37,6 +37,7 @@ ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, + IS_BACKUP_BOT, M3U8_SUPPORT, OWNER, PLAYLIST_SUPPORT, @@ -126,7 +127,7 @@ def start_handler(client: Client, message: types.Message): else: info = "" text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" - client.send_message(message.chat.id, text) + client.send_message(message.chat.id, text, disable_web_page_preview=True) @app.on_message(filters.command(["help"])) @@ -547,12 +548,13 @@ def trx_notify(_, **kwargs): if __name__ == "__main__": MySQL() TRX_SIGNAL.connect(trx_notify) - scheduler = BackgroundScheduler(timezone="Asia/Shanghai", job_defaults={"max_instances": 5}) - scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) + scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) - scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) - scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) + if not IS_BACKUP_BOT: + scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) + scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) + scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) # default quota allocation of 10,000 units per day scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() From acacfe58c946217e3de275ec3c3588a47a3ddf7b Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 14 Dec 2023 17:39:19 +0100 Subject: [PATCH 148/297] decouple redis and mysql --- ytdlbot/database.py | 6 ++++-- ytdlbot/tasks.py | 3 ++- ytdlbot/ytdl_bot.py | 10 +++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index f7873978..69fb28a6 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -74,7 +74,8 @@ def __init__(self): try: self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True) self.r.ping() - except redis.RedisError: + except Exception: + logging.warning("Redis connection failed, using fake redis instead.") self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True) db_banner = "=" * 20 + "DB data" + "=" * 20 @@ -254,7 +255,8 @@ def __init__(self): self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) - except pymysql.err.OperationalError: + except Exception: + logging.warning("MySQL connection failed, using fake mysql instead.") self.con = FakeMySQL() self.con.ping(reconnect=True) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index a70040a0..053347f9 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -69,7 +69,6 @@ # celery -A tasks worker --loglevel=info --pool=solo # app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') app = Celery("tasks", broker=BROKER) -redis = Redis() channel = Channel() session = "ytdl-celery" @@ -122,6 +121,7 @@ def direct_download_task(chat_id, message_id, url): def forward_video(client, bot_msg, url: str): + redis = Redis() chat_id = bot_msg.chat.id unique = get_unique_clink(url, chat_id) cached_fid = redis.get_send_cache(unique) @@ -307,6 +307,7 @@ def generate_input_media(file_paths: list, cap: str) -> list: def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): + redis = Redis() # raise pyrogram.errors.exceptions.FloodWait(13) # if is str, it's a file id; else it's a list of paths payment = Payment() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 3b78f881..83777db7 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -65,7 +65,6 @@ app = create_app(":memory:") logging.info("Authorized users are %s", AUTHORIZED_USER) -redis = Redis() channel = Channel() @@ -205,6 +204,7 @@ def purge_handler(client: Client, message: types.Message): @app.on_message(filters.command(["ping"])) def ping_handler(client: Client, message: types.Message): + redis = Redis() chat_id = message.chat.id client.send_chat_action(chat_id, "typing") if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): @@ -231,6 +231,7 @@ def sub_count_handler(client: Client, message: types.Message): @app.on_message(filters.command(["direct"])) def direct_handler(client: Client, message: types.Message): + redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") url = re.sub(r"/direct\s*", "", message.text) @@ -398,6 +399,7 @@ def search_ytb(kw: str): @app.on_message(filters.incoming & (filters.text | filters.document)) @private_use def download_handler(client: Client, message: types.Message): + redis = Redis() payment = Payment() chat_id = message.from_user.id client.send_chat_action(chat_id, "typing") @@ -480,6 +482,7 @@ def download_resolution_callback(client: Client, callback_query: types.CallbackQ @app.on_callback_query(filters.regex(r"convert")) def audio_callback(client: Client, callback_query: types.CallbackQuery): + redis = Redis() if not ENABLE_FFMPEG: callback_query.answer("Audio conversion is disabled now.") callback_query.message.reply_text("Audio conversion is disabled now.") @@ -546,6 +549,7 @@ def trx_notify(_, **kwargs): if __name__ == "__main__": + redis = Redis() MySQL() TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) @@ -555,8 +559,8 @@ def trx_notify(_, **kwargs): scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) - # default quota allocation of 10,000 units per day - scheduler.add_job(periodic_sub_check, "interval", seconds=3600) + # default quota allocation of 10,000 units per day + scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From f7243a4a939c6872f04923adc84c88ccef450832 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 16 Dec 2023 18:37:38 +0100 Subject: [PATCH 149/297] let's try pyrogram 2.x again --- .dockerignore | 3 +- .gitignore | 3 + .gitmodules | 0 .gitpod.yml | 9 --- Dockerfile | 11 +-- README.md | 13 ++- apk.txt | 1 - requirements.txt | 4 +- ytdlbot/client_init.py | 8 +- ytdlbot/config.py | 6 +- ytdlbot/database.py | 4 +- ytdlbot/downloader.py | 15 ++-- ytdlbot/limit.py | 2 +- ytdlbot/tasks.py | 180 +++++++++++++++-------------------------- ytdlbot/utils.py | 17 ++-- ytdlbot/ytdl_bot.py | 112 ++++++++++++------------- 16 files changed, 153 insertions(+), 235 deletions(-) delete mode 100644 .gitmodules delete mode 100644 .gitpod.yml delete mode 100644 apk.txt diff --git a/.dockerignore b/.dockerignore index cd3a1671..67c221fb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ env +venv db_data .ash_history -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.gitignore b/.gitignore index 7f7fc30d..6b3facfc 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,6 @@ reinforcement/* /ytdlbot/ytdl-main.session /ytdlbot/ytdl-celery.session-journal /ytdlbot/ytdl-celery.session +/ytdlbot/main.session +/ytdlbot/tasks.session +/ytdlbot/tasks.session-journal diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29b..00000000 diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 76d9ef38..00000000 --- a/.gitpod.yml +++ /dev/null @@ -1,9 +0,0 @@ -# This configuration file was automatically generated by Gitpod. -# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) -# and commit this file to your remote git repository to share the goodness with others. - -# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart - -tasks: - - init: pip3 install -r requirements.txt - - init: sudo apt update && sudo apt install ffmpeg -y diff --git a/Dockerfile b/Dockerfile index 3b8c86cf..c07cd116 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,13 @@ -FROM python:3.11-alpine as builder - -RUN apk update && apk add --no-cache tzdata alpine-sdk libffi-dev ca-certificates +FROM python:3.11 as builder ADD requirements.txt /tmp/ RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt -FROM python:3.11-alpine +FROM python:3.11-slim WORKDIR /ytdlbot/ytdlbot -ENV TZ=Europe/Stockholm +ENV TZ=Europe/London -COPY apk.txt /tmp/ -RUN apk update && xargs apk add < /tmp/apk.txt +RUN apt update && apt install -y --no-install-recommends --no-install-suggests ffmpeg vnstat git aria2 COPY --from=builder /root/.local /usr/local COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo diff --git a/README.md b/README.md index 81e0623e..c2eec60f 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ This bot can be deployed on any platform that supports Python. To deploy this bot, follow these steps: 1. Install bot dependencies - * Install Python 3.8 or a later version, FFmpeg. + * Install Python 3.10 or a later version, FFmpeg. * (optional)Aria2 and add it to the PATH. 2. Clone the code from the repository and cd into it. @@ -153,7 +153,6 @@ You can configure all the following environment variables: * AUTHORIZED_USER: only authorized users can use the bot * REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot * ENABLE_CELERY: celery mode, default: disable -* ENABLE_QUEUE: celery queue * BROKER: celery broker, should be redis://redis:6379/0 * MYSQL_HOST:MySQL host * MYSQL_USER: MySQL username @@ -291,11 +290,11 @@ https://twitter.com/BennyThinks/status/1475836588542341124 ## Test instagram -https://www.instagram.com/p/ClBSqo3PkJw/ - -https://www.instagram.com/p/CaiAHoWDnrM/ - -https://www.instagram.com/p/CZtUDyyv1u1/ +* single image: https://www.instagram.com/p/CXpxSyOrWCA/ +* single video: https://www.instagram.com/p/Cah_7gnDVUW/ +* reels: https://www.instagram.com/p/C0ozGsjtY0W/ +* image carousel: https://www.instagram.com/p/C0ozPQ5o536/ +* video and image carousel: https://www.instagram.com/p/C0ozhsVo-m8/ # Donation diff --git a/apk.txt b/apk.txt deleted file mode 100644 index 76b90295..00000000 --- a/apk.txt +++ /dev/null @@ -1 +0,0 @@ -ffmpeg vnstat git aria2 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 4a0e5f78..ed071f7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pyrogram==1.4.16 +pyrogram==2.0.106 tgcrypto==1.2.5 yt-dlp==2023.11.16 APScheduler==3.10.4 @@ -11,7 +11,7 @@ flower==2.0.1 psutil==5.9.6 influxdb==5.3.1 beautifulsoup4==4.12.2 -fakeredis==2.20.0 +fakeredis==2.20.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.1 diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 2cbfc9ea..3fa8c205 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -12,14 +12,12 @@ from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 -def create_app(session: str, workers: int = PYRO_WORKERS) -> Client: - _app = Client( - session, +def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: + return Client( + name, APP_ID, APP_HASH, bot_token=TOKEN, workers=workers, ipv6=IPv6, ) - - return _app diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 84f51690..718503ac 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -12,8 +12,8 @@ from blinker import signal # general settings -WORKERS: int = int(os.getenv("WORKERS", 100)) -PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(64, (os.cpu_count() + 4) * 10))) +WORKERS: int = int(os.getenv("WORKERS", 10)) +PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, (os.cpu_count() or 0) + 4))) APP_ID: int = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") @@ -36,7 +36,6 @@ # celery related ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) -ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", False) BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4") MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql") @@ -49,7 +48,6 @@ IPv6 = os.getenv("IPv6", False) ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) - PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 69fb28a6..b762dcd7 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -75,7 +75,7 @@ def __init__(self): self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True) self.r.ping() except Exception: - logging.warning("Redis connection failed, using fake redis instead.") + logging.debug("Redis connection failed, using fake redis instead.") self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True) db_banner = "=" * 20 + "DB data" + "=" * 20 @@ -256,7 +256,7 @@ def __init__(self): host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) except Exception: - logging.warning("MySQL connection failed, using fake mysql instead.") + logging.debug("MySQL connection failed, using fake mysql instead.") self.con = FakeMySQL() self.con.ping(reconnect=True) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index d8e78b76..8fecc3bc 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -24,15 +24,10 @@ import filetype import requests import yt_dlp as ytdl +from pyrogram import types from tqdm import tqdm -from config import ( - AUDIO_FORMAT, - ENABLE_ARIA2, - ENABLE_FFMPEG, - TG_MAX_SIZE, - IPv6, -) +from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6 from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -40,8 +35,8 @@ apply_log_formatter() -def edit_text(bot_msg, text: str): - key = f"{bot_msg.chat.id}-{bot_msg.message_id}" +def edit_text(bot_msg: types.Message, text: str): + key = f"{bot_msg.chat.id}-{bot_msg.id}" # if the key exists, we shouldn't send edit message if not r.exists(key): time.sleep(random.random()) @@ -87,7 +82,7 @@ def remove_bash_color(text): def download_hook(d: dict, bot_msg): - # since we're using celery, server location may be located in different continent. + # since we're using celery, server location may be located in different region. # Therefore, we can't trigger the hook very often. # the key is user_id + download_link original_url = d["info_dict"]["original_url"] diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py index 978e6cce..819978db 100644 --- a/ytdlbot/limit.py +++ b/ytdlbot/limit.py @@ -155,8 +155,8 @@ def check_payment(self): cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'") data = cur.fetchall() - logging.info("Checking unpaid payment...%s", data) for row in data: + logging.info("Checking user payment %s", row) user_id = row[0] addr, index = row[1].split(",")[2:] try: diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 053347f9..faf64e25 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -7,11 +7,10 @@ __author__ = "Benny " +import asyncio import logging -import math import os import pathlib -import random import re import shutil import subprocess @@ -20,7 +19,7 @@ import time import traceback import typing -from hashlib import md5 +from typing import Any from urllib.parse import quote_plus import filetype @@ -30,9 +29,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from celery import Celery from celery.worker.control import Panel -from pyrogram import Client, idle, types -from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message -from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor +from pyrogram import Client, enums, idle, types from channel import Channel from client_init import create_app @@ -40,7 +37,6 @@ ARCHIVE_ID, BROKER, ENABLE_CELERY, - ENABLE_QUEUE, ENABLE_VIP, OWNER, RATE_LIMIT, @@ -66,37 +62,33 @@ bot_text = BotText() logging.getLogger("apscheduler.executors.default").propagate = False -# celery -A tasks worker --loglevel=info --pool=solo -# app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle') app = Celery("tasks", broker=BROKER) +bot = create_app("tasks") channel = Channel() -session = "ytdl-celery" -celery_client = create_app(session) - -def get_messages(chat_id, message_id): +def get_messages(chat_id: int, message_id: int): try: - return celery_client.get_messages(chat_id, message_id) + return bot.get_messages(chat_id, message_id) except ConnectionError as e: - logging.critical("WTH!!! %s", e) - celery_client.start() - return celery_client.get_messages(chat_id, message_id) + logging.critical("CRITICAL ERROR: %s", e) + bot.start() + return bot.get_messages(chat_id, message_id) @app.task(rate_limit=f"{RATE_LIMIT}/m") -def ytdl_download_task(chat_id, message_id, url: str): +def ytdl_download_task(chat_id: int, message_id: int, url: str): logging.info("YouTube celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - ytdl_normal_download(celery_client, bot_msg, url) + ytdl_normal_download(bot_msg, url) logging.info("YouTube celery tasks ended.") @app.task() -def audio_task(chat_id, message_id): +def audio_task(chat_id: int, message_id: int): logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) bot_msg = get_messages(chat_id, message_id) - normal_audio(celery_client, bot_msg) + normal_audio(bot_msg) logging.info("Audio celery tasks ended.") @@ -113,14 +105,14 @@ def get_unique_clink(original_url: str, user_id: int): @app.task() -def direct_download_task(chat_id, message_id, url): +def direct_download_task(chat_id: int, message_id: int, url: str): logging.info("Direct download celery tasks started for %s", url) bot_msg = get_messages(chat_id, message_id) - direct_normal_download(celery_client, bot_msg, url) + direct_normal_download(bot, bot_msg, url) logging.info("Direct download celery tasks ended.") -def forward_video(client, bot_msg, url: str): +def forward_video(bot_msg: types.Message, url: str): redis = Redis() chat_id = bot_msg.chat.id unique = get_unique_clink(url, chat_id) @@ -129,28 +121,27 @@ def forward_video(client, bot_msg, url: str): redis.update_metrics("cache_miss") return False - res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid) + res_msg = upload_processor(bot_msg, url, cached_fid) obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo caption, _ = gen_cap(bot_msg, url, obj) res_msg.edit_text(caption, reply_markup=gen_video_markup()) - bot_msg.edit_text(f"Download success!โœ…โœ…โœ…") + bot_msg.edit_text(f"Download success!โœ…") redis.update_metrics("cache_hit") return True -def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): +def ytdl_download_entrance(bot_msg: types.Message, url: str, mode=None): payment = Payment() chat_id = bot_msg.chat.id try: - if forward_video(client, bot_msg, url): + if forward_video(bot_msg, url): return mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: - async_task(ytdl_download_task, chat_id, bot_msg.message_id, url) - # ytdl_download_task.delay(chat_id, bot_msg.message_id, url) + ytdl_download_task.delay(chat_id, bot_msg.id, url) else: - ytdl_normal_download(client, bot_msg, url) + ytdl_normal_download(bot_msg, url) except Exception as e: logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True) @@ -159,23 +150,22 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): if ENABLE_CELERY: direct_normal_download(client, bot_msg, url) - # direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url) + # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) else: direct_normal_download(client, bot_msg, url) -def audio_entrance(client, bot_msg): +def audio_entrance(bot_msg: types.Message): if ENABLE_CELERY: - async_task(audio_task, bot_msg.chat.id, bot_msg.message_id) - # audio_task.delay(bot_msg.chat.id, bot_msg.message_id) + audio_task.delay(bot_msg.chat.id, bot_msg.id) else: - normal_audio(client, bot_msg) + normal_audio(bot_msg) def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): chat_id = bot_msg.chat.id headers = { - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36" + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.3987.149 Safari/537.36" } length = 0 @@ -206,7 +196,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, logging.info("Downloaded file %s", filename) st_size = os.stat(filepath).st_size - client.send_chat_action(chat_id, "upload_document") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) client.send_document( bot_msg.chat.id, filepath, @@ -217,7 +207,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, bot_msg.edit_text("Download success!โœ…") -def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]): +def normal_audio(bot_msg: typing.Union[types.Message, typing.Coroutine]): chat_id = bot_msg.chat.id # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text( @@ -225,59 +215,36 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor ) orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp: - client.send_chat_action(chat_id, "record_audio") + bot.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) # just try to download the audio using yt-dlp filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") - client.send_chat_action(chat_id, "upload_audio") + bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) for f in filepath: - client.send_audio(chat_id, f) + bot.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") Redis().update_metrics("audio_success") -def get_dl_source(): - worker_name = os.getenv("WORKER_NAME") - if worker_name: - return f"Downloaded by {worker_name}" - return "" - - -def upload_transfer_sh(bm, paths: list) -> str: - d = {p.name: (md5(p.name.encode("utf8")).hexdigest() + p.suffix, p.open("rb")) for p in paths} - monitor = MultipartEncoderMonitor(MultipartEncoder(fields=d), lambda x: upload_hook(x.bytes_read, x.len, bm)) - headers = {"Content-Type": monitor.content_type} - try: - req = requests.post("https://transfer.sh", data=monitor, headers=headers) - bm.edit_text(f"Download success!โœ…") - return re.sub(r"https://", "\nhttps://", req.text) - except requests.exceptions.RequestException as e: - return f"Upload failed!โŒ\n\n```{e}```" - - -def flood_owner_message(client, ex): - client.send_message(OWNER, f"CRITICAL INFO: {ex}") - - -def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): +def ytdl_normal_download(bot_msg: types.Message | typing.Any, url: str): chat_id = bot_msg.chat.id temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") - client.send_chat_action(chat_id, "upload_document") + bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) bot_msg.edit_text("Download complete. Sending now...") try: - upload_processor(client, bot_msg, url, video_paths) + upload_processor(bot_msg, url, video_paths) except pyrogram.errors.Flood as e: logging.critical("FloodWait from Telegram: %s", e) - client.send_message( + bot.send_message( chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.", + f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.", ) - flood_owner_message(client, e) - time.sleep(e.x) - upload_processor(client, bot_msg, url, video_paths) + bot.send_message(OWNER, f"CRITICAL INFO: {e}") + time.sleep(e.value) + upload_processor(bot_msg, url, video_paths) bot_msg.edit_text("Download success!โœ…") @@ -306,7 +273,7 @@ def generate_input_media(file_paths: list, cap: str) -> list: return input_media -def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): +def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): redis = Redis() # raise pyrogram.errors.exceptions.FloodWait(13) # if is str, it's a file id; else it's a list of paths @@ -316,7 +283,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1: # just generate the first for simplicity, send as media group(2-20) cap, meta = gen_cap(bot_msg, url, vp_or_fid[0]) - res_msg = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) + res_msg: list["types.Message"] | Any = bot.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) # TODO no cache for now return res_msg[0] elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1: @@ -335,7 +302,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): logging.info("Sending as document") try: # send as document could be sent as video even if it's a document - res_msg = client.send_document( + res_msg = bot.send_document( chat_id, vp_or_fid, caption=cap, @@ -347,7 +314,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): ) except ValueError: logging.error("Retry to send as video") - res_msg = client.send_video( + res_msg = bot.send_video( chat_id, vp_or_fid, supports_streaming=True, @@ -359,7 +326,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): ) elif settings[2] == "audio": logging.info("Sending as audio") - res_msg = client.send_audio( + res_msg = bot.send_audio( chat_id, vp_or_fid, caption=cap, @@ -370,7 +337,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): # settings==video logging.info("Sending as video") try: - res_msg = client.send_video( + res_msg = bot.send_video( chat_id, vp_or_fid, supports_streaming=True, @@ -384,7 +351,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): # try to send as annimation, photo try: logging.warning("Retry to send as animation") - res_msg = client.send_animation( + res_msg = bot.send_animation( chat_id, vp_or_fid, caption=cap, @@ -396,7 +363,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): except Exception: # this is likely a photo logging.warning("Retry to send as photo") - res_msg = client.send_photo( + res_msg = bot.send_photo( chat_id, vp_or_fid, caption=cap, @@ -409,7 +376,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]): redis.add_send_cache(unique, getattr(obj, "file_id", None)) redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.message_id) + bot.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) return res_msg @@ -441,7 +408,11 @@ def gen_cap(bm, url, video_path): remain = f"Download token count: free {free}, pay {pay}" else: remain = "" - worker = get_dl_source() + + if worker_name := os.getenv("WORKER_NAME"): + worker = f"Downloaded by {worker_name}" + else: + worker = "" cap = ( f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" @@ -450,10 +421,10 @@ def gen_cap(bm, url, video_path): def gen_video_markup(): - markup = InlineKeyboardMarkup( + markup = types.InlineKeyboardMarkup( [ [ # First row - InlineKeyboardButton( # Generates a callback query when pressed + types.InlineKeyboardButton( # Generates a callback query when pressed "convert to audio", callback_data="convert" ) ] @@ -472,7 +443,6 @@ def hot_patch(*args): app_path = pathlib.Path().cwd().parent logging.info("Hot patching on path %s...", app_path) - apk_install = "xargs apk add < apk.txt" pip_install = "pip install -r requirements.txt" unset = "git config --unset http.https://github.com/.extraheader" pull_unshallow = "git pull origin --unshallow" @@ -484,52 +454,32 @@ def hot_patch(*args): subprocess.call(pull, shell=True, cwd=app_path) logging.info("Code is updated, applying hot patch now...") - subprocess.call(apk_install, shell=True, cwd=app_path) subprocess.call(pip_install, shell=True, cwd=app_path) psutil.Process().kill() -def async_task(task_name, *args): - if not ENABLE_QUEUE: - task_name.delay(*args) - return - - t0 = time.time() - inspect = app.control.inspect() - worker_stats = inspect.stats() - route_queues = [] - padding = math.ceil(sum([i["pool"]["max-concurrency"] for i in worker_stats.values()]) / len(worker_stats)) - for worker_name, stats in worker_stats.items(): - route = worker_name.split("@")[1] - concurrency = stats["pool"]["max-concurrency"] - route_queues.extend([route] * (concurrency + padding)) - destination = random.choice(route_queues) - logging.info("Selecting worker %s from %s in %.2fs", destination, route_queues, time.time() - t0) - task_name.apply_async(args=args, queue=destination) +def purge_tasks(): + count = app.control.purge() + return f"purged {count} tasks." def run_celery(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) worker_name = os.getenv("WORKER_NAME", "") argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name] - if ENABLE_QUEUE: - argv.extend(["-Q", worker_name]) app.worker_main(argv) -def purge_tasks(): - count = app.control.purge() - return f"purged {count} tasks." - - if __name__ == "__main__": - # celery_client.start() + bot.start() print("Bootstrapping Celery worker now.....") - time.sleep(5) + time.sleep(3) threading.Thread(target=run_celery, daemon=True).start() - scheduler = BackgroundScheduler(timezone="Asia/Shanghai") + scheduler = BackgroundScheduler(timezone="Europe/London") scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() - celery_client.stop() + bot.stop() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index ceaa1c6d..9d5e046d 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -166,7 +166,6 @@ def updates_too_long_detector(self): "types.UpdatesTooLong", "Got shutdown from remote", "Code is updated", - 'Retrying "messages.GetMessages"', "OSError: Connection lost", "[Errno -3] Try again", "MISCONF", @@ -179,7 +178,7 @@ def updates_too_long_detector(self): def next_salt_detector(self): text = "Next salt in" - if self.logs.count(text) >= 4: + if self.logs.count(text) >= 5: logging.critical("Next salt crash: %s", self.func_name()) return True @@ -187,17 +186,17 @@ def msg_id_detector(self): text = "The msg_id is too low" if text in self.logs: logging.critical("msg id crash: %s ", self.func_name()) - for item in pathlib.Path(__file__).parent.glob("ytdl-*"): + for item in pathlib.Path(__file__).parent.glob("*.session"): + logging.warning("Removing session file: %s", item) item.unlink(missing_ok=True) time.sleep(3) return True - # def idle_detector(self): - # mtime = os.stat("/var/log/ytdl.log").st_mtime - # cur_ts = time.time() - # if cur_ts - mtime > 7200: - # logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name()) - # return True + def sqlite_locked_detector(self): + text = "sqlite3.OperationalError: database is locked" + if text in self.logs: + logging.critical("database is locked: %s ", self.func_name()) + return True def auto_restart(): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 83777db7..1c455b9a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -15,18 +15,17 @@ import tempfile import time import traceback -import typing from io import BytesIO +from typing import Any import pyrogram.errors import qrcode import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler -from pyrogram import Client, filters, types +from pyrogram import Client, enums, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions from pyrogram.raw import types as raw_types -from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup from tgbot_ping import get_runtime from youtubesearchpython import VideosSearch @@ -59,12 +58,11 @@ ) from utils import auto_restart, clean_tempfile, customize_logger, get_revision +logging.info("Authorized users are %s", AUTHORIZED_USER) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) logging.getLogger("apscheduler.executors.default").propagate = False -app = create_app(":memory:") - -logging.info("Authorized users are %s", AUTHORIZED_USER) +app = create_app("main") channel = Channel() @@ -73,7 +71,7 @@ def wrapper(client: Client, message: types.Message): chat_id = getattr(message.from_user, "id", None) # message type check - if message.chat.type != "private" and not message.text.lower().startswith("/ytdl"): + if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"): logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) return @@ -89,14 +87,11 @@ def wrapper(client: Client, message: types.Message): if REQUIRED_MEMBERSHIP: try: - member: typing.Union[types.ChatMember, typing.Coroutine] = app.get_chat_member( - REQUIRED_MEMBERSHIP, chat_id - ) + member: types.ChatMember | Any = app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id) if member.status not in [ - "creator", - "administrator", - "member", - "owner", + enums.ChatMemberStatus.ADMINISTRATOR, + enums.ChatMemberStatus.MEMBER, + enums.ChatMemberStatus.OWNER, ]: raise UserNotParticipant() else: @@ -116,11 +111,11 @@ def start_handler(client: Client, message: types.Message): payment = Payment() from_id = message.from_user.id logging.info("%s welcome to youtube-dl bot!", message.from_user.id) - client.send_chat_action(from_id, "typing") + client.send_chat_action(from_id, enums.ChatAction.TYPING) is_old_user = payment.check_old_user(from_id) if is_old_user: info = "" - elif ENABLE_VIP: + if ENABLE_VIP: free_token, pay_token, reset = payment.get_token(from_id) info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}" else: @@ -132,21 +127,21 @@ def start_handler(client: Client, message: types.Message): @app.on_message(filters.command(["help"])) def help_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, BotText.help, disable_web_page_preview=True) @app.on_message(filters.command(["about"])) def about_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, BotText.about) @app.on_message(filters.command(["sub"])) def subscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) if message.text == "/sub": result = channel.get_user_subscription(chat_id) else: @@ -161,7 +156,7 @@ def subscribe_handler(client: Client, message: types.Message): @app.on_message(filters.command(["unsub"])) def unsubscribe_handler(client: Client, message: types.Message): chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) text = message.text.split(" ") if len(text) == 1: client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True) @@ -181,7 +176,7 @@ def patch_handler(client: Client, message: types.Message): chat_id = message.chat.id if username == OWNER: celery_app.control.broadcast("hot_patch") - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) client.send_message(chat_id, "Oorah!") hot_patch() @@ -206,7 +201,7 @@ def purge_handler(client: Client, message: types.Message): def ping_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): bot_info = "ping unavailable." else: @@ -233,7 +228,7 @@ def sub_count_handler(client: Client, message: types.Message): def direct_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.from_user.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) url = re.sub(r"/direct\s*", "", message.text) logging.info("direct start %s", url) if not re.findall(r"^https?://", url.lower()): @@ -250,27 +245,27 @@ def direct_handler(client: Client, message: types.Message): def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id payment = Payment() - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) data = MySQL().get_user_settings(chat_id) set_mode = data[-1] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") mode_text = f"Download mode: **{set_mode}**" if message.chat.username == OWNER or payment.get_pay_token(chat_id): - extra = [InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] + extra = [types.InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] else: extra = [] - markup = InlineKeyboardMarkup( + markup = types.InlineKeyboardMarkup( [ [ # First row - InlineKeyboardButton("send as document", callback_data="document"), - InlineKeyboardButton("send as video", callback_data="video"), - InlineKeyboardButton("send as audio", callback_data="audio"), + types.InlineKeyboardButton("send as document", callback_data="document"), + types.InlineKeyboardButton("send as video", callback_data="video"), + types.InlineKeyboardButton("send as audio", callback_data="audio"), ], [ # second row - InlineKeyboardButton("High Quality", callback_data="high"), - InlineKeyboardButton("Medium Quality", callback_data="medium"), - InlineKeyboardButton("Low Quality", callback_data="low"), + types.InlineKeyboardButton("High Quality", callback_data="high"), + types.InlineKeyboardButton("Medium Quality", callback_data="medium"), + types.InlineKeyboardButton("Low Quality", callback_data="low"), ], extra, ] @@ -288,7 +283,7 @@ def settings_handler(client: Client, message: types.Message): def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) # currency USD token_count = message.text.replace("/buy", "").strip() if token_count.isdigit(): @@ -296,11 +291,11 @@ def buy_handler(client: Client, message: types.Message): else: price = 100 - markup = InlineKeyboardMarkup( + markup = types.InlineKeyboardMarkup( [ [ - InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"), - InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"), + types.InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"), + types.InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"), ], ] ) @@ -311,7 +306,7 @@ def buy_handler(client: Client, message: types.Message): def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery): callback_query.answer("Generating QR code...") chat_id = callback_query.message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) addr = TronTrx().get_payment_address(chat_id) with BytesIO() as bio: @@ -324,13 +319,13 @@ def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery) def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery): callback_query.answer("Generating invoice...") chat_id = callback_query.message.chat.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) data = callback_query.data price = int(data.split("-")[-1]) payload = f"{chat_id}-buy" invoice = generate_invoice(price, f"Buy {TOKEN_PRICE} download tokens", "Pay by card", payload) - app.send( + app.invoke( functions.messages.SendMedia( peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), media=invoice, @@ -384,10 +379,9 @@ def link_checker(url: str) -> str: def search_ytb(kw: str): - videosSearch = VideosSearch(kw, limit=10) - + videos_search = VideosSearch(kw, limit=10) text = "" - results = videosSearch.result()["result"] + results = videos_search.result()["result"] for item in results: title = item.get("title") link = item.get("link") @@ -402,7 +396,7 @@ def download_handler(client: Client, message: types.Message): redis = Redis() payment = Payment() chat_id = message.from_user.id - client.send_chat_action(chat_id, "typing") + client.send_chat_action(chat_id, enums.ChatAction.TYPING) redis.user_count(chat_id) if message.document: with tempfile.NamedTemporaryFile(mode="r+") as tf: @@ -431,9 +425,7 @@ def download_handler(client: Client, message: types.Message): if ENABLE_VIP and not payment.check_old_user(chat_id): free, pay, reset = payment.get_token(chat_id) if free + pay <= 0: - message.reply_text( - f"You don't have enough token. Please wait until {reset} or /buy more token.", quote=True - ) + message.reply_text(f"You don't have enough token. Please wait until {reset} or /buy .", quote=True) redis.update_metrics("reject_token") return else: @@ -444,22 +436,22 @@ def download_handler(client: Client, message: types.Message): text = BotText.get_receive_link_text() try: # raise pyrogram.errors.exceptions.FloodWait(10) - bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True) + bot_msg: types.Message | Any = message.reply_text(text, quote=True) except pyrogram.errors.Flood as e: f = BytesIO() f.write(str(e).encode()) f.write(b"Your job will be done soon. Just wait! Don't rush.") f.name = "Please don't flood me.txt" bot_msg = message.reply_document( - f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True + f, caption=f"Flood wait! Please wait {e} seconds...." f"Your job will start automatically", quote=True ) f.close() - client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e.x} seconds....") - time.sleep(e.x) + client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e} seconds....") + time.sleep(e.value) - client.send_chat_action(chat_id, "upload_video") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) bot_msg.chat = message.chat - ytdl_download_entrance(client, bot_msg, url) + ytdl_download_entrance(bot_msg, url) @app.on_callback_query(filters.regex(r"document|video|audio")) @@ -484,14 +476,13 @@ def download_resolution_callback(client: Client, callback_query: types.CallbackQ def audio_callback(client: Client, callback_query: types.CallbackQuery): redis = Redis() if not ENABLE_FFMPEG: - callback_query.answer("Audio conversion is disabled now.") + callback_query.answer("Request rejected.") callback_query.message.reply_text("Audio conversion is disabled now.") return callback_query.answer(f"Converting to audio...please wait patiently") redis.update_metrics("audio_request") - vmsg = callback_query.message - audio_entrance(client, vmsg) + audio_entrance(callback_query.message) @app.on_callback_query(filters.regex(r"Local|Celery")) @@ -509,14 +500,12 @@ def periodic_sub_check(): logging.info(f"periodic update:{video_url} - {uids}") for uid in uids: try: - bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message( - uid, f"{video_url} is out. Watch it on YouTube" - ) + bot_msg: types.Message | Any = app.send_message(uid, f"{video_url} is out. Watch it on YouTube") # ytdl_download_entrance(app, bot_msg, video_url, mode="direct") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) - except Exception as e: + except Exception: logging.error("Unknown error when sending message to user. %s", traceback.format_exc()) finally: time.sleep(random.random() * 3) @@ -527,7 +516,7 @@ def raw_update(client: Client, update, users, chats): payment = Payment() action = getattr(getattr(update, "message", None), "action", None) if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": - client.send( + client.invoke( functions.messages.SetBotPrecheckoutResults( query_id=update.query_id, success=True, @@ -549,14 +538,13 @@ def trx_notify(_, **kwargs): if __name__ == "__main__": - redis = Redis() MySQL() TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) if not IS_BACKUP_BOT: - scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0) + scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) # default quota allocation of 10,000 units per day From 62342ee35038a658b1e1cbffb30f5cb26f30baab Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 17 Dec 2023 14:55:37 +0100 Subject: [PATCH 150/297] fix local mode --- ytdlbot/tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index faf64e25..173f4317 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -71,7 +71,7 @@ def get_messages(chat_id: int, message_id: int): try: return bot.get_messages(chat_id, message_id) except ConnectionError as e: - logging.critical("CRITICAL ERROR: %s", e) + logging.critical("BOT IS NOT STARTED YET: %s", e) bot.start() return bot.get_messages(chat_id, message_id) @@ -141,6 +141,7 @@ def ytdl_download_entrance(bot_msg: types.Message, url: str, mode=None): if ENABLE_CELERY and mode in [None, "Celery"]: ytdl_download_task.delay(chat_id, bot_msg.id, url) else: + get_messages(chat_id, bot_msg.id) ytdl_normal_download(bot_msg, url) except Exception as e: logging.error("Failed to download %s, error: %s", url, e) From 1c3becee9d0155a1b6d1b99842758237e6f90e0a Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 17 Dec 2023 16:29:37 +0100 Subject: [PATCH 151/297] refine code --- Dockerfile | 2 - ytdlbot/config.py | 2 +- ytdlbot/tasks.py | 107 +++++++++++++++++++++++--------------------- ytdlbot/ytdl_bot.py | 7 ++- 4 files changed, 60 insertions(+), 58 deletions(-) diff --git a/Dockerfile b/Dockerfile index c07cd116..5d2408e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,6 @@ ENV TZ=Europe/London RUN apt update && apt install -y --no-install-recommends --no-install-suggests ffmpeg vnstat git aria2 COPY --from=builder /root/.local /usr/local -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo COPY . /ytdlbot CMD ["/usr/local/bin/supervisord", "-c" ,"/ytdlbot/conf/supervisor_main.conf"] diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 718503ac..f62da220 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -13,7 +13,7 @@ # general settings WORKERS: int = int(os.getenv("WORKERS", 10)) -PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, (os.cpu_count() or 0) + 4))) +PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, (os.cpu_count() or 0) + 10))) APP_ID: int = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 173f4317..be65f848 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -67,7 +67,8 @@ channel = Channel() -def get_messages(chat_id: int, message_id: int): +def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: + # this should only be called by celery tasks try: return bot.get_messages(chat_id, message_id) except ConnectionError as e: @@ -79,19 +80,27 @@ def get_messages(chat_id: int, message_id: int): @app.task(rate_limit=f"{RATE_LIMIT}/m") def ytdl_download_task(chat_id: int, message_id: int, url: str): logging.info("YouTube celery tasks started for %s", url) - bot_msg = get_messages(chat_id, message_id) - ytdl_normal_download(bot_msg, url) + bot_msg = retrieve_message(chat_id, message_id) + ytdl_normal_download(bot, bot_msg, url) logging.info("YouTube celery tasks ended.") @app.task() def audio_task(chat_id: int, message_id: int): logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) - bot_msg = get_messages(chat_id, message_id) - normal_audio(bot_msg) + bot_msg = retrieve_message(chat_id, message_id) + normal_audio(bot, bot_msg) logging.info("Audio celery tasks ended.") +@app.task() +def direct_download_task(chat_id: int, message_id: int, url: str): + logging.info("Direct download celery tasks started for %s", url) + bot_msg = retrieve_message(chat_id, message_id) + direct_normal_download(bot, bot_msg, url) + logging.info("Direct download celery tasks ended.") + + def get_unique_clink(original_url: str, user_id: int): payment = Payment() settings = payment.get_user_settings(user_id) @@ -104,45 +113,36 @@ def get_unique_clink(original_url: str, user_id: int): return unique -@app.task() -def direct_download_task(chat_id: int, message_id: int, url: str): - logging.info("Direct download celery tasks started for %s", url) - bot_msg = get_messages(chat_id, message_id) - direct_normal_download(bot, bot_msg, url) - logging.info("Direct download celery tasks ended.") - - -def forward_video(bot_msg: types.Message, url: str): - redis = Redis() - chat_id = bot_msg.chat.id - unique = get_unique_clink(url, chat_id) - cached_fid = redis.get_send_cache(unique) - if not cached_fid: - redis.update_metrics("cache_miss") - return False - - res_msg = upload_processor(bot_msg, url, cached_fid) +def forward_video(client, bot_msg: types.Message | Any, url: str, cached_fid: str): + res_msg = upload_processor(client, bot_msg, url, cached_fid) obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo caption, _ = gen_cap(bot_msg, url, obj) res_msg.edit_text(caption, reply_markup=gen_video_markup()) bot_msg.edit_text(f"Download success!โœ…") - redis.update_metrics("cache_hit") return True -def ytdl_download_entrance(bot_msg: types.Message, url: str, mode=None): +def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): + # in Local node and forward mode, we pass client from main + # in celery mode, we need to use our own client called bot payment = Payment() + redis = Redis() chat_id = bot_msg.chat.id + unique = get_unique_clink(url, chat_id) + cached_fid = redis.get_send_cache(unique) + try: - if forward_video(bot_msg, url): + if cached_fid: + forward_video(client, bot_msg, url, cached_fid) + redis.update_metrics("cache_hit") return + redis.update_metrics("cache_miss") mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: ytdl_download_task.delay(chat_id, bot_msg.id, url) else: - get_messages(chat_id, bot_msg.id) - ytdl_normal_download(bot_msg, url) + ytdl_normal_download(client, bot_msg, url) except Exception as e: logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True) @@ -156,11 +156,11 @@ def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message direct_normal_download(client, bot_msg, url) -def audio_entrance(bot_msg: types.Message): +def audio_entrance(client: Client, bot_msg: types.Message): if ENABLE_CELERY: audio_task.delay(bot_msg.chat.id, bot_msg.id) else: - normal_audio(bot_msg) + normal_audio(client, bot_msg) def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): @@ -208,7 +208,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, bot_msg.edit_text("Download success!โœ…") -def normal_audio(bot_msg: typing.Union[types.Message, typing.Coroutine]): +def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]): chat_id = bot_msg.chat.id # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text( @@ -216,36 +216,42 @@ def normal_audio(bot_msg: typing.Union[types.Message, typing.Coroutine]): ) orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp: - bot.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) + client.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) # just try to download the audio using yt-dlp filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") status_msg.edit_text("Sending audio now...") - bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) for f in filepath: - bot.send_audio(chat_id, f) + client.send_audio(chat_id, f) status_msg.edit_text("โœ… Conversion complete.") Redis().update_metrics("audio_success") -def ytdl_normal_download(bot_msg: types.Message | typing.Any, url: str): +def ytdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, url: str): + """ + This function is called by celery task or directly by bot + :param client: bot client, either from main or bot(celery) + :param bot_msg: bot message + :param url: url to download + """ chat_id = bot_msg.chat.id temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) video_paths = ytdl_download(url, temp_dir.name, bot_msg) logging.info("Download complete.") - bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) bot_msg.edit_text("Download complete. Sending now...") try: - upload_processor(bot_msg, url, video_paths) + upload_processor(client, bot_msg, url, video_paths) except pyrogram.errors.Flood as e: logging.critical("FloodWait from Telegram: %s", e) - bot.send_message( + client.send_message( chat_id, f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.", ) - bot.send_message(OWNER, f"CRITICAL INFO: {e}") + client.send_message(OWNER, f"CRITICAL INFO: {e}") time.sleep(e.value) - upload_processor(bot_msg, url, video_paths) + upload_processor(client, bot_msg, url, video_paths) bot_msg.edit_text("Download success!โœ…") @@ -274,7 +280,7 @@ def generate_input_media(file_paths: list, cap: str) -> list: return input_media -def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): +def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid: str | list): redis = Redis() # raise pyrogram.errors.exceptions.FloodWait(13) # if is str, it's a file id; else it's a list of paths @@ -284,7 +290,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1: # just generate the first for simplicity, send as media group(2-20) cap, meta = gen_cap(bot_msg, url, vp_or_fid[0]) - res_msg: list["types.Message"] | Any = bot.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) + res_msg: list["types.Message"] | Any = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) # TODO no cache for now return res_msg[0] elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1: @@ -303,7 +309,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): logging.info("Sending as document") try: # send as document could be sent as video even if it's a document - res_msg = bot.send_document( + res_msg = client.send_document( chat_id, vp_or_fid, caption=cap, @@ -315,7 +321,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): ) except ValueError: logging.error("Retry to send as video") - res_msg = bot.send_video( + res_msg = client.send_video( chat_id, vp_or_fid, supports_streaming=True, @@ -327,7 +333,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): ) elif settings[2] == "audio": logging.info("Sending as audio") - res_msg = bot.send_audio( + res_msg = client.send_audio( chat_id, vp_or_fid, caption=cap, @@ -338,7 +344,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): # settings==video logging.info("Sending as video") try: - res_msg = bot.send_video( + res_msg = client.send_video( chat_id, vp_or_fid, supports_streaming=True, @@ -352,7 +358,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): # try to send as annimation, photo try: logging.warning("Retry to send as animation") - res_msg = bot.send_animation( + res_msg = client.send_animation( chat_id, vp_or_fid, caption=cap, @@ -364,7 +370,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): except Exception: # this is likely a photo logging.warning("Retry to send as photo") - res_msg = bot.send_photo( + res_msg = client.send_photo( chat_id, vp_or_fid, caption=cap, @@ -377,7 +383,7 @@ def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list): redis.add_send_cache(unique, getattr(obj, "file_id", None)) redis.update_metrics("video_success") if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - bot.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) + client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) return res_msg @@ -473,9 +479,8 @@ def run_celery(): if __name__ == "__main__": - bot.start() print("Bootstrapping Celery worker now.....") - time.sleep(3) + time.sleep(5) threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Europe/London") diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 1c455b9a..30e30c78 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -451,7 +451,7 @@ def download_handler(client: Client, message: types.Message): client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) bot_msg.chat = message.chat - ytdl_download_entrance(bot_msg, url) + ytdl_download_entrance(client, bot_msg, url) @app.on_callback_query(filters.regex(r"document|video|audio")) @@ -482,7 +482,7 @@ def audio_callback(client: Client, callback_query: types.CallbackQuery): callback_query.answer(f"Converting to audio...please wait patiently") redis.update_metrics("audio_request") - audio_entrance(callback_query.message) + audio_entrance(client, callback_query.message) @app.on_callback_query(filters.regex(r"Local|Celery")) @@ -500,8 +500,7 @@ def periodic_sub_check(): logging.info(f"periodic update:{video_url} - {uids}") for uid in uids: try: - bot_msg: types.Message | Any = app.send_message(uid, f"{video_url} is out. Watch it on YouTube") - # ytdl_download_entrance(app, bot_msg, video_url, mode="direct") + app.send_message(uid, f"{video_url} is out. Watch it on YouTube") except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: logging.warning("User is blocked or deleted. %s", e) channel.deactivate_user_subscription(uid) From 34a6f9d8703d18012072088d0f23307857feaf40 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 17 Dec 2023 17:24:26 +0100 Subject: [PATCH 152/297] add connection lost --- ytdlbot/utils.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 9d5e046d..54ca4182 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -182,20 +182,10 @@ def next_salt_detector(self): logging.critical("Next salt crash: %s", self.func_name()) return True - def msg_id_detector(self): - text = "The msg_id is too low" + def connection_reset_detector(self): + text = "Send exception: ConnectionResetError Connection lost" if text in self.logs: - logging.critical("msg id crash: %s ", self.func_name()) - for item in pathlib.Path(__file__).parent.glob("*.session"): - logging.warning("Removing session file: %s", item) - item.unlink(missing_ok=True) - time.sleep(3) - return True - - def sqlite_locked_detector(self): - text = "sqlite3.OperationalError: database is locked" - if text in self.logs: - logging.critical("database is locked: %s ", self.func_name()) + logging.critical("connection lost: %s ", self.func_name()) return True From 08500c744c4137419689834767185b22a418cb0a Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 18 Dec 2023 17:49:23 +0100 Subject: [PATCH 153/297] increase worker, separate redis for backup bot --- requirements.txt | 2 +- ytdlbot/config.py | 9 ++++++--- ytdlbot/database.py | 11 ++++++----- ytdlbot/flower_tasks.py | 2 +- ytdlbot/utils.py | 1 - 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/requirements.txt b/requirements.txt index ed071f7e..02ea76cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ PyMySQL==1.1.0 celery==5.3.6 filetype==1.2.0 flower==2.0.1 -psutil==5.9.6 +psutil==5.9.7 influxdb==5.3.1 beautifulsoup4==4.12.2 fakeredis==2.20.1 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index f62da220..d81a9427 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -13,7 +13,7 @@ # general settings WORKERS: int = int(os.getenv("WORKERS", 10)) -PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, (os.cpu_count() or 0) + 10))) +PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", 100)) APP_ID: int = int(os.getenv("APP_ID", 198214)) APP_HASH = os.getenv("APP_HASH", "1234b90") TOKEN = os.getenv("TOKEN", "1234") @@ -35,8 +35,12 @@ REQUIRED_MEMBERSHIP: str = os.getenv("REQUIRED_MEMBERSHIP", "") # celery related +IS_BACKUP_BOT = os.getenv("IS_BACKUP_BOT") ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) -BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4") +if IS_BACKUP_BOT: + BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/1") +else: + BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/0") MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql") MYSQL_USER = os.getenv("MYSQL_USER", "root") @@ -53,7 +57,6 @@ ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) RATE_LIMIT = os.getenv("RATE_LIMIT", 120) -IS_BACKUP_BOT = os.getenv("IS_BACKUP_BOT") RCLONE_PATH = os.getenv("RCLONE") # This will set the value for the tmpfile path(download path) if it is set. # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. diff --git a/ytdlbot/database.py b/ytdlbot/database.py index b762dcd7..4f996cbc 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -25,7 +25,7 @@ from beautifultable import BeautifulTable from influxdb import InfluxDBClient -from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS +from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS, IS_BACKUP_BOT init_con = sqlite3.connect(":memory:", check_same_thread=False) @@ -71,12 +71,13 @@ def sub(sql): class Redis: def __init__(self): + db = 1 if IS_BACKUP_BOT else 0 try: - self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True) + self.r = redis.StrictRedis(host=REDIS, db=db, decode_responses=True) self.r.ping() except Exception: - logging.debug("Redis connection failed, using fake redis instead.") - self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True) + logging.warning("Redis connection failed, using fake redis instead.") + self.r = fakeredis.FakeStrictRedis(host=REDIS, db=db, decode_responses=True) db_banner = "=" * 20 + "DB data" + "=" * 20 quota_banner = "=" * 20 + "Celery" + "=" * 20 @@ -256,7 +257,7 @@ def __init__(self): host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) except Exception: - logging.debug("MySQL connection failed, using fake mysql instead.") + logging.warning("MySQL connection failed, using fake mysql instead.") self.con = FakeMySQL() self.con.ping(reconnect=True) diff --git a/ytdlbot/flower_tasks.py b/ytdlbot/flower_tasks.py index 31a40dc6..c791421a 100644 --- a/ytdlbot/flower_tasks.py +++ b/ytdlbot/flower_tasks.py @@ -11,4 +11,4 @@ from config import BROKER -app = Celery("tasks", broker=BROKER, timezone="Asia/Shanghai") +app = Celery("tasks", broker=BROKER, timezone="Europe/London") diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 54ca4182..349cbed6 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -37,7 +37,6 @@ def apply_log_formatter(): def customize_logger(logger: list): - apply_log_formatter() for log in logger: logging.getLogger(log).setLevel(level=logging.INFO) From c486fe83012c4e18818648e7fe6d1aa7d7e9a064 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 20 Dec 2023 20:09:34 +0100 Subject: [PATCH 154/297] 4 GiB Support (#322) * 4 GiB Support --- .gitignore | 2 + README.md | 39 ++++++++++++------- docker-compose.yml | 6 ++- ytdlbot/config.py | 6 +++ ytdlbot/database.py | 2 +- ytdlbot/downloader.py | 18 ++++++++- ytdlbot/premium.py | 91 +++++++++++++++++++++++++++++++++++++++++++ ytdlbot/tasks.py | 26 +++++++++++-- ytdlbot/utils.py | 2 +- ytdlbot/ytdl_bot.py | 8 ++++ 10 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 ytdlbot/premium.py diff --git a/.gitignore b/.gitignore index 6b3facfc..737187dd 100644 --- a/.gitignore +++ b/.gitignore @@ -173,3 +173,5 @@ reinforcement/* /ytdlbot/main.session /ytdlbot/tasks.session /ytdlbot/tasks.session-journal +/ytdlbot/premium.session +/dump.rdb diff --git a/README.md b/README.md index c2eec60f..7e66ad41 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,6 @@ YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ This Telegram bot allows you to download videos from YouTube and other supported websites, including Instagram! -
Deploy to heroku - -Deploy to Heroku - -If you are having trouble deploying, you can fork the project to your personal account and deploy it from there. - -**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no -longer be available.** -[Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) -
- # Usage [https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) @@ -24,7 +13,7 @@ longer be available.** Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. Send link directly to the bot. Any -Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) will work t0o. +Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) will work too. # Limitations of my bot @@ -50,6 +39,7 @@ own bot. See below instructions. 10. subscriptions to YouTube Channels 11. cache mechanism - download once for the same video. 12. instagram posts +13. 4 GiB file size support with Telegram Premium # Screenshots @@ -115,6 +105,19 @@ One line command to run the bot docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot ``` +## Heroku + +
Deploy to heroku + +Deploy to Heroku + +If you are having trouble deploying, you can fork the project to your personal account and deploy it from there. + +**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no +longer be available.** +[Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) +
+ # Complete deployment guide for docker-compose * contains every functionality @@ -172,6 +175,7 @@ You can configure all the following environment variables: * TMPFILE_PATH: tmpfile path(file download path) * TRONGRID_KEY: TronGrid key, better use your own key to avoid rate limit * TRON_MNEMONIC: Tron mnemonic, the default one is on nile testnet. +* PREMIUM_USER: premium user ID, it can help you to download files larger than 2 GiB ## 3.2 Set up init data @@ -248,8 +252,15 @@ On the other machine: docker-compose -f worker.yml up -d ``` -**โš ๏ธ You should not publish Redis directly on the internet. -Instead, you can use WireGuard to wrap it up for added security.** +**โš ๏ธ You should not publish Redis directly on the internet. โš ๏ธ** + +### 4.4 4 GiB Support + +1. Subscribe to Telegram Premium +2. Setup user id `PREMIUM_USER` in `ytdl.env` +3. Create session file by running `python premium.py` +4. Copy the session file `premium.session` to `data` directory +5. `docker-compose up -d premium` ## kubernetes diff --git a/docker-compose.yml b/docker-compose.yml index 3db5eeb5..d5658b48 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,9 +52,11 @@ services: ports: - "127.0.0.1:15555:5555" - instagram: + premium: image: bennythink/ytdlbot env_file: - env/ytdl.env restart: always - command: [ "/usr/local/bin/python", "/ytdlbot/ytdlbot/instagram.py" ] + volumes: + - ./data/premium.session:/ytdlbot/ytdlbot/premium.session + command: [ "/usr/local/bin/python", "/ytdlbot/ytdlbot/premium.py" ] diff --git a/ytdlbot/config.py b/ytdlbot/config.py index d81a9427..ee2b210d 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -77,3 +77,9 @@ # the default mnemonic is for nile testnet TRON_MNEMONIC = os.getenv("TRON_MNEMONIC", "cram floor today legend service drill pitch leaf car govern harvest soda") TRX_SIGNAL = signal("trx_received") + +PREMIUM_USER = int(os.getenv("PREMIUM_USER")) + + +class FileTooBig(Exception): + pass diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 4f996cbc..0dd6f8d5 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -25,7 +25,7 @@ from beautifultable import BeautifulTable from influxdb import InfluxDBClient -from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS, IS_BACKUP_BOT +from config import IS_BACKUP_BOT, MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS init_con = sqlite3.connect(":memory:", check_same_thread=False) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 8fecc3bc..210ecdda 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -27,7 +27,15 @@ from pyrogram import types from tqdm import tqdm -from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6 +from config import ( + AUDIO_FORMAT, + ENABLE_ARIA2, + ENABLE_FFMPEG, + PREMIUM_USER, + TG_MAX_SIZE, + FileTooBig, + IPv6, +) from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt @@ -92,7 +100,11 @@ def download_hook(d: dict, bot_msg): downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) if total > TG_MAX_SIZE: - raise Exception(f"Your download file size {sizeof_fmt(total)} is too large for Telegram.") + msg = f"Your download file size {sizeof_fmt(total)} is too large for Telegram." + if PREMIUM_USER: + raise FileTooBig(msg) + else: + raise Exception(msg) # percent = remove_bash_color(d.get("_percent_str", "N/A")) speed = remove_bash_color(d.get("_speed_str", "N/A")) @@ -199,6 +211,8 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: ydl.download([url]) video_paths = list(pathlib.Path(tempdir).glob("*")) break + except FileTooBig as e: + raise e except Exception: error = traceback.format_exc() logging.error("Download failed for %s - %s, try another way", format_, url) diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py new file mode 100644 index 00000000..e351f121 --- /dev/null +++ b/ytdlbot/premium.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - premium.py +# 2023-12-20 17:53 + +import json +import logging +import pathlib +import tempfile +from unittest.mock import MagicMock + +import yt_dlp +from pyrogram import Client, filters, types + +from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN +from limit import Payment +from utils import apply_log_formatter + +apply_log_formatter() +app = Client("premium", APP_ID, APP_HASH, workers=PYRO_WORKERS) + +BOT_ID = int(TOKEN.split(":")[0]) + + +@app.on_message(filters.user(BOT_ID) & filters.incoming) +async def hello(client: Client, message: types.Message): + text = message.text + try: + data = json.loads(text) + except json.decoder.JSONDecodeError: + return + url = data["url"] + user_id = data["user_id"] + + tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") + output = pathlib.Path(tempdir.name, "%(title).70s.%(ext)s").as_posix() + ydl_opts = {"restrictfilenames": False, "quiet": True, "outtmpl": output} + formats = [ + # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 + "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", + "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", + None, + ] + + for f in formats: + ydl_opts["format"] = f + logging.info("Downloading BIG FILE for %s with format %s", url, f) + try: + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + ydl.download([url]) + break + except Exception: + logging.error("Download failed for %s. Try other options...", url) + + payment = Payment() + settings = payment.get_user_settings(user_id) + video_path = next(pathlib.Path(tempdir.name).glob("*")) + if settings[2] == "video" or isinstance(settings[2], MagicMock): + logging.info("Sending as video") + await client.send_video( + BOT_ID, + video_path.as_posix(), + caption="Powered by ytdlbot", + supports_streaming=True, + file_name=f"{user_id}.mp4", + ) + elif settings[2] == "audio": + logging.info("Sending as audio") + await client.send_audio( + BOT_ID, + video_path.as_posix(), + caption="Powered by ytdlbot ", + file_name=f"{user_id}.mp3", + ) + elif settings[2] == "document": + logging.info("Sending as document") + await client.send_document( + BOT_ID, + video_path.as_posix(), + caption="Powered by ytdlbot", + file_name=f"{user_id}.mp4", + ) + else: + logging.error("Send type is not video or audio") + + tempdir.cleanup() + + +if __name__ == "__main__": + app.run() diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index be65f848..faede8e9 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -8,6 +8,7 @@ __author__ = "Benny " import asyncio +import json import logging import os import pathlib @@ -39,10 +40,12 @@ ENABLE_CELERY, ENABLE_VIP, OWNER, + PREMIUM_USER, RATE_LIMIT, RCLONE_PATH, TMPFILE_PATH, WORKERS, + FileTooBig, ) from constant import BotText from database import Redis @@ -50,7 +53,6 @@ from limit import Payment from utils import ( apply_log_formatter, - auto_restart, customize_logger, get_metadata, get_revision, @@ -81,7 +83,16 @@ def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: def ytdl_download_task(chat_id: int, message_id: int, url: str): logging.info("YouTube celery tasks started for %s", url) bot_msg = retrieve_message(chat_id, message_id) - ytdl_normal_download(bot, bot_msg, url) + try: + ytdl_normal_download(bot, bot_msg, url) + except FileTooBig as e: + # if you can go there, that means you have premium users set up + logging.warning("Seeking for help from premium user...") + bot_msg.edit_text(f"{e}\n\nPlease wait, I will try to send it as premium user") + data = {"url": url, "user_id": bot_msg.chat.id} + bot.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + except Exception: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) logging.info("YouTube celery tasks ended.") @@ -140,12 +151,19 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod redis.update_metrics("cache_miss") mode = mode or payment.get_user_settings(chat_id)[-1] if ENABLE_CELERY and mode in [None, "Celery"]: + # in celery mode, producer has lost control of this task. ytdl_download_task.delay(chat_id, bot_msg.id, url) else: ytdl_normal_download(client, bot_msg, url) + except FileTooBig as e: + logging.warning("Seeking for help from premium user...") + bot_msg.edit_text(f"{e}\n\nPlease wait, I will try to send it as premium user") + # this is only for normal node. Celery node will need to do it in celery tasks + data = {"url": url, "user_id": bot_msg.chat.id} + client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) except Exception as e: logging.error("Failed to download %s, error: %s", url, e) - bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True) + bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): @@ -484,7 +502,7 @@ def run_celery(): threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Europe/London") - scheduler.add_job(auto_restart, "interval", seconds=900) + # scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 349cbed6..1094c897 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -193,7 +193,7 @@ def auto_restart(): if not os.path.exists(log_path): return with open(log_path) as f: - logs = "".join(tail_log(f, lines=100)) + logs = "".join(tail_log(f, lines=50)) det = Detector(logs) method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 30e30c78..90ae9ac2 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -40,6 +40,7 @@ M3U8_SUPPORT, OWNER, PLAYLIST_SUPPORT, + PREMIUM_USER, PROVIDER_TOKEN, REQUIRED_MEMBERSHIP, TOKEN_PRICE, @@ -345,6 +346,13 @@ def redeem_handler(client: Client, message: types.Message): message.reply_text(msg, quote=True) +@app.on_message(filters.user(PREMIUM_USER) & filters.incoming & filters.caption) +def premium_forward(client: Client, message: types.Message): + media = message.video or message.audio or message.document + target_user = media.file_name.split(".")[0] + client.forward_messages(target_user, message.chat.id, message.id) + + def generate_invoice(amount: int, title: str, description: str, payload: str): invoice = raw_types.input_media_invoice.InputMediaInvoice( invoice=raw_types.invoice.Invoice( From 11648248cd2bff45d062dcba20a6d53cb08e74de Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 20 Dec 2023 21:02:10 +0100 Subject: [PATCH 155/297] add download hook --- README.md | 2 +- ytdlbot/config.py | 6 +++--- ytdlbot/premium.py | 13 ++++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7e66ad41..135b7dfd 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supp Due to limitations on servers and bandwidth, there are some restrictions on this free service. -* Each user is limited to 20 free downloads per 24-hour period +* Each user is limited to 10 free downloads per 24-hour period * Maximum of three subscriptions allowed for YouTube channels. If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your diff --git a/ytdlbot/config.py b/ytdlbot/config.py index ee2b210d..958379e5 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -56,7 +56,7 @@ M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -RATE_LIMIT = os.getenv("RATE_LIMIT", 120) +RATE_LIMIT = os.getenv("RATE_LIMIT", 60) RCLONE_PATH = os.getenv("RCLONE") # This will set the value for the tmpfile path(download path) if it is set. # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. @@ -71,14 +71,14 @@ AFD_TOKEN = os.getenv("AFD_TOKEN") AFD_USER_ID = os.getenv("AFD_USER_ID") PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 20) +FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 10) TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits TRONGRID_KEY = os.getenv("TRONGRID_KEY", "").split(",") # the default mnemonic is for nile testnet TRON_MNEMONIC = os.getenv("TRON_MNEMONIC", "cram floor today legend service drill pitch leaf car govern harvest soda") TRX_SIGNAL = signal("trx_received") -PREMIUM_USER = int(os.getenv("PREMIUM_USER")) +PREMIUM_USER = int(os.getenv("PREMIUM_USER", "0")) class FileTooBig(Exception): diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py index e351f121..255869da 100644 --- a/ytdlbot/premium.py +++ b/ytdlbot/premium.py @@ -23,6 +23,12 @@ BOT_ID = int(TOKEN.split(":")[0]) +def download_hook(d: dict): + downloaded = d.get("downloaded_bytes", 0) + total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) + print(downloaded, total) + + @app.on_message(filters.user(BOT_ID) & filters.incoming) async def hello(client: Client, message: types.Message): text = message.text @@ -35,7 +41,7 @@ async def hello(client: Client, message: types.Message): tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") output = pathlib.Path(tempdir.name, "%(title).70s.%(ext)s").as_posix() - ydl_opts = {"restrictfilenames": False, "quiet": True, "outtmpl": output} + ydl_opts = {"restrictfilenames": False, "quiet": True, "outtmpl": output, "progress_hooks": [download_hook]} formats = [ # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", @@ -50,12 +56,13 @@ async def hello(client: Client, message: types.Message): with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) break - except Exception: - logging.error("Download failed for %s. Try other options...", url) + except Exception as e: + logging.error("Download failed for %s: %s", url, e) payment = Payment() settings = payment.get_user_settings(user_id) video_path = next(pathlib.Path(tempdir.name).glob("*")) + logging.info("filesize: %s", video_path.stat().st_size) if settings[2] == "video" or isinstance(settings[2], MagicMock): logging.info("Sending as video") await client.send_video( From cde4f91d2a5d6118fc0fb96ef697c7e936096a5e Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 20 Dec 2023 21:09:56 +0100 Subject: [PATCH 156/297] add disclaimer --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 135b7dfd..a84fbc05 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ own bot. See below instructions. 12. instagram posts 13. 4 GiB file size support with Telegram Premium +> If you download files larger than 2 GiB, you agreed that this file will be uploaded by me. I know who you are and what +> you download. + # Screenshots ## Normal download From 78323a419f6b88f8591ee9f8d1eea87fa676a633 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 20 Dec 2023 21:14:31 +0100 Subject: [PATCH 157/297] upload hook --- ytdlbot/premium.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py index 255869da..90346284 100644 --- a/ytdlbot/premium.py +++ b/ytdlbot/premium.py @@ -29,6 +29,10 @@ def download_hook(d: dict): print(downloaded, total) +async def upload_hook(current, total): + print(current, total) + + @app.on_message(filters.user(BOT_ID) & filters.incoming) async def hello(client: Client, message: types.Message): text = message.text @@ -71,6 +75,7 @@ async def hello(client: Client, message: types.Message): caption="Powered by ytdlbot", supports_streaming=True, file_name=f"{user_id}.mp4", + progress=upload_hook, ) elif settings[2] == "audio": logging.info("Sending as audio") @@ -79,6 +84,7 @@ async def hello(client: Client, message: types.Message): video_path.as_posix(), caption="Powered by ytdlbot ", file_name=f"{user_id}.mp3", + progress=upload_hook, ) elif settings[2] == "document": logging.info("Sending as document") @@ -87,6 +93,7 @@ async def hello(client: Client, message: types.Message): video_path.as_posix(), caption="Powered by ytdlbot", file_name=f"{user_id}.mp4", + progress=upload_hook, ) else: logging.error("Send type is not video or audio") From 750c897dabdbb5f0249e47aa3f03d968d47988e1 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 20 Dec 2023 21:25:10 +0100 Subject: [PATCH 158/297] premium download consent --- ytdlbot/config.py | 2 +- ytdlbot/constant.py | 5 +++++ ytdlbot/tasks.py | 25 ++++++++++++++++--------- ytdlbot/ytdl_bot.py | 12 ++++++++++++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 958379e5..2a08923a 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -56,7 +56,7 @@ M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -RATE_LIMIT = os.getenv("RATE_LIMIT", 60) +RATE_LIMIT = os.getenv("RATE_LIMIT", 120) RCLONE_PATH = os.getenv("RCLONE") # This will set the value for the tmpfile path(download path) if it is set. # If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 14750a1d..3938d13d 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -83,6 +83,11 @@ class BotText: """ custom_text = os.getenv("CUSTOM_TEXT", "") + premium_warning = """ + Your file is too big, do you want me to try to send it as premium user? + Owner will know who you are and what you are downloading. You may be banned if you abuse this feature. + """ + @staticmethod def get_receive_link_text() -> str: reserved = get_func_queue("reserved") diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index faede8e9..ea951c77 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -8,7 +8,6 @@ __author__ = "Benny " import asyncio -import json import logging import os import pathlib @@ -40,7 +39,6 @@ ENABLE_CELERY, ENABLE_VIP, OWNER, - PREMIUM_USER, RATE_LIMIT, RCLONE_PATH, TMPFILE_PATH, @@ -53,6 +51,7 @@ from limit import Payment from utils import ( apply_log_formatter, + auto_restart, customize_logger, get_metadata, get_revision, @@ -79,6 +78,18 @@ def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: return bot.get_messages(chat_id, message_id) +def premium_button(): + markup = types.InlineKeyboardMarkup( + [ + [ + types.InlineKeyboardButton("Yes", callback_data="premium-yes"), + types.InlineKeyboardButton("No", callback_data="premium-no"), + ] + ] + ) + return markup + + @app.task(rate_limit=f"{RATE_LIMIT}/m") def ytdl_download_task(chat_id: int, message_id: int, url: str): logging.info("YouTube celery tasks started for %s", url) @@ -88,9 +99,7 @@ def ytdl_download_task(chat_id: int, message_id: int, url: str): except FileTooBig as e: # if you can go there, that means you have premium users set up logging.warning("Seeking for help from premium user...") - bot_msg.edit_text(f"{e}\n\nPlease wait, I will try to send it as premium user") - data = {"url": url, "user_id": bot_msg.chat.id} - bot.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=premium_button()) except Exception: bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) logging.info("YouTube celery tasks ended.") @@ -157,10 +166,8 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod ytdl_normal_download(client, bot_msg, url) except FileTooBig as e: logging.warning("Seeking for help from premium user...") - bot_msg.edit_text(f"{e}\n\nPlease wait, I will try to send it as premium user") # this is only for normal node. Celery node will need to do it in celery tasks - data = {"url": url, "user_id": bot_msg.chat.id} - client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=premium_button()) except Exception as e: logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) @@ -502,7 +509,7 @@ def run_celery(): threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Europe/London") - # scheduler.add_job(auto_restart, "interval", seconds=900) + scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 90ae9ac2..8cf2fce9 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -8,6 +8,7 @@ __author__ = "Benny " import contextlib +import json import logging import os import random @@ -316,6 +317,17 @@ def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery) client.send_photo(chat_id, bio, caption=f"Send any amount of TRX to `{addr}`") +@app.on_callback_query(filters.regex(r"premium.*")) +def premium_click(client: Client, callback_query: types.CallbackQuery): + data = callback_query.data + if data == "premium-yes": + replied = callback_query.message.reply_to_message + data = {"url": replied.text, "user_id": callback_query.message.chat.id} + client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + else: + callback_query.answer("Cancelled.") + + @app.on_callback_query(filters.regex(r"bot-payments-.*")) def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery): callback_query.answer("Generating invoice...") From ee65c371bb2d5f720de3a258a46ddf8101744e15 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 21 Dec 2023 08:05:29 +0100 Subject: [PATCH 159/297] disable 4g --- ytdlbot/ytdl_bot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 8cf2fce9..0ebaf5b3 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -8,7 +8,6 @@ __author__ = "Benny " import contextlib -import json import logging import os import random @@ -321,9 +320,10 @@ def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery) def premium_click(client: Client, callback_query: types.CallbackQuery): data = callback_query.data if data == "premium-yes": - replied = callback_query.message.reply_to_message - data = {"url": replied.text, "user_id": callback_query.message.chat.id} - client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + callback_query.answer("developing....please wait for next release") + # replied = callback_query.message.reply_to_message + # data = {"url": replied.text, "user_id": callback_query.message.chat.id} + # client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) else: callback_query.answer("Cancelled.") From 0f8c541b4bdbb22327aeac5b688031302eb984fc Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 21 Dec 2023 19:02:08 +0100 Subject: [PATCH 160/297] remove fakeredis --- ytdlbot/downloader.py | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index 210ecdda..d25e0c85 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -7,18 +7,18 @@ __author__ = "Benny " +import functools import logging import os import pathlib -import random import re import subprocess +import threading import time import traceback from io import StringIO from unittest.mock import MagicMock -import fakeredis import ffmpeg import ffpb import filetype @@ -39,17 +39,41 @@ from limit import Payment from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt -r = fakeredis.FakeStrictRedis() apply_log_formatter() +def debounce(wait_seconds): + """ + Thread-safe debounce decorator for functions that take a message with chat.id and msg.id attributes. + The function will only be called if it hasn't been called with the same chat.id and msg.id in the last 'wait_seconds'. + """ + + def decorator(func): + last_called = {} + lock = threading.Lock() + + @functools.wraps(func) + def wrapper(*args, **kwargs): + nonlocal last_called + now = time.time() + + # Assuming the first argument is the message object with chat.id and msg.id + bot_msg = args[0] + key = (bot_msg.chat.id, bot_msg.id) + + with lock: + if key not in last_called or now - last_called[key] >= wait_seconds: + last_called[key] = now + return func(*args, **kwargs) + + return wrapper + + return decorator + + +@debounce(5) def edit_text(bot_msg: types.Message, text: str): - key = f"{bot_msg.chat.id}-{bot_msg.id}" - # if the key exists, we shouldn't send edit message - if not r.exists(key): - time.sleep(random.random()) - r.set(key, "ok", ex=3) - bot_msg.edit_text(text) + bot_msg.edit_text(text) def tqdm_progress(desc, total, finished, speed="", eta=""): @@ -111,7 +135,6 @@ def download_hook(d: dict, bot_msg): eta = remove_bash_color(d.get("_eta_str", d.get("eta"))) text = tqdm_progress("Downloading...", total, downloaded, speed, eta) edit_text(bot_msg, text) - r.set(key, "ok", ex=5) def upload_hook(current, total, bot_msg): From 9f9ff0a79492e43616c41208a55fe86bee6f00d2 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 21 Dec 2023 19:02:21 +0100 Subject: [PATCH 161/297] add max_concurrent_transmissions --- .gitignore | 1 + ytdlbot/client_init.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 737187dd..56388a92 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,4 @@ reinforcement/* /ytdlbot/tasks.session-journal /ytdlbot/premium.session /dump.rdb +/ytdlbot/premium.session-journal diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 3fa8c205..76b316cd 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -9,7 +9,7 @@ from pyrogram import Client -from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 +from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, WORKERS, IPv6 def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: @@ -20,4 +20,6 @@ def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: bot_token=TOKEN, workers=workers, ipv6=IPv6, + max_concurrent_transmissions=max(1, WORKERS // 2), + # https://github.com/pyrogram/pyrogram/issues/1225#issuecomment-1446595489 ) From c2d5afb5894fb3f3571f22dda10ace57f0e54fe3 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 21 Dec 2023 19:36:47 +0100 Subject: [PATCH 162/297] limitations on premium download --- .gitignore | 1 + ytdlbot/constant.py | 4 +++- ytdlbot/database.py | 2 ++ ytdlbot/premium.py | 35 +++++++++++++++++------------------ ytdlbot/tasks.py | 19 ++++++++++++++++--- ytdlbot/ytdl_bot.py | 21 +++++++++++++++++---- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 56388a92..778fa339 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,4 @@ reinforcement/* /ytdlbot/premium.session /dump.rdb /ytdlbot/premium.session-journal +/ytdlbot/main.session-journal diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 3938d13d..5a2f8adf 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -85,7 +85,9 @@ class BotText: premium_warning = """ Your file is too big, do you want me to try to send it as premium user? - Owner will know who you are and what you are downloading. You may be banned if you abuse this feature. + This is an experimental feature so you can only use it once per day. + Also, the premium user will know who you are and what you are downloading. + You may be banned if you abuse this feature. """ @staticmethod diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 0dd6f8d5..4f5df0fe 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -182,6 +182,8 @@ def reset_today(self): if k.startswith("today"): self.r.hdel("metrics", k) + self.r.delete("premium") + def user_count(self, user_id): self.r.hincrby("metrics", user_id) diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py index 90346284..622b8936 100644 --- a/ytdlbot/premium.py +++ b/ytdlbot/premium.py @@ -8,14 +8,13 @@ import logging import pathlib import tempfile -from unittest.mock import MagicMock import yt_dlp from pyrogram import Client, filters, types from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN -from limit import Payment -from utils import apply_log_formatter +from limit import Payment, Redis +from utils import apply_log_formatter, sizeof_fmt apply_log_formatter() app = Client("premium", APP_ID, APP_HASH, workers=PYRO_WORKERS) @@ -26,11 +25,11 @@ def download_hook(d: dict): downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) - print(downloaded, total) + logging.info("Downloaded %s/%s, %.2f%% complete", downloaded, total, downloaded / total * 100) async def upload_hook(current, total): - print(current, total) + logging.info("Uploaded %s/%s, %.2f%% complete", current, total, current / total * 100) @app.on_message(filters.user(BOT_ID) & filters.incoming) @@ -66,18 +65,8 @@ async def hello(client: Client, message: types.Message): payment = Payment() settings = payment.get_user_settings(user_id) video_path = next(pathlib.Path(tempdir.name).glob("*")) - logging.info("filesize: %s", video_path.stat().st_size) - if settings[2] == "video" or isinstance(settings[2], MagicMock): - logging.info("Sending as video") - await client.send_video( - BOT_ID, - video_path.as_posix(), - caption="Powered by ytdlbot", - supports_streaming=True, - file_name=f"{user_id}.mp4", - progress=upload_hook, - ) - elif settings[2] == "audio": + logging.info("Final filesize is %s", sizeof_fmt(video_path.stat().st_size)) + if settings[2] == "audio": logging.info("Sending as audio") await client.send_audio( BOT_ID, @@ -96,9 +85,19 @@ async def hello(client: Client, message: types.Message): progress=upload_hook, ) else: - logging.error("Send type is not video or audio") + logging.info("Sending as video") + await client.send_video( + BOT_ID, + video_path.as_posix(), + caption="Powered by ytdlbot", + supports_streaming=True, + file_name=f"{user_id}.mp4", + progress=upload_hook, + ) tempdir.cleanup() + redis = Redis() + redis.r.hset("premium", user_id, 1) if __name__ == "__main__": diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index ea951c77..1ceb24a2 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -78,7 +78,12 @@ def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: return bot.get_messages(chat_id, message_id) -def premium_button(): +def premium_button(user_id): + redis = Redis() + used = redis.r.hget("premium", user_id) + ban = redis.r.hget("ban", user_id) + if used or ban: + return None markup = types.InlineKeyboardMarkup( [ [ @@ -99,7 +104,11 @@ def ytdl_download_task(chat_id: int, message_id: int, url: str): except FileTooBig as e: # if you can go there, that means you have premium users set up logging.warning("Seeking for help from premium user...") - bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=premium_button()) + markup = premium_button(chat_id) + if markup: + bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) + else: + bot_msg.edit_text(f"{e}\n\n Big file download is not available now, please try again later.") except Exception: bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) logging.info("YouTube celery tasks ended.") @@ -167,7 +176,11 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod except FileTooBig as e: logging.warning("Seeking for help from premium user...") # this is only for normal node. Celery node will need to do it in celery tasks - bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=premium_button()) + markup = premium_button(chat_id) + if markup: + bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) + else: + bot_msg.edit_text(f"{e}\n\n Big file download is not available now, please try again later.") except Exception as e: logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 0ebaf5b3..4d51839a 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -8,6 +8,7 @@ __author__ = "Benny " import contextlib +import json import logging import os import random @@ -320,12 +321,15 @@ def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery) def premium_click(client: Client, callback_query: types.CallbackQuery): data = callback_query.data if data == "premium-yes": - callback_query.answer("developing....please wait for next release") - # replied = callback_query.message.reply_to_message - # data = {"url": replied.text, "user_id": callback_query.message.chat.id} - # client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) + callback_query.answer("Seeking premium user...") + callback_query.message.edit_text("Please wait patiently...no progress bar will be shown.") + replied = callback_query.message.reply_to_message + data = {"url": replied.text, "user_id": callback_query.message.chat.id} + client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) else: callback_query.answer("Cancelled.") + original_text = callback_query.message.text + callback_query.message.edit_text(original_text.split("\n")[0]) @app.on_callback_query(filters.regex(r"bot-payments-.*")) @@ -365,6 +369,15 @@ def premium_forward(client: Client, message: types.Message): client.forward_messages(target_user, message.chat.id, message.id) +@app.on_message(filters.command(["ban"]) & filters.user(PREMIUM_USER)) +def ban_handler(client: Client, message: types.Message): + replied = message.reply_to_message.text + user_id = json.loads(replied).get("user_id") + redis = Redis() + redis.r.hset("ban", user_id, 1) + message.reply_text(f"Done, banned {user_id}.", quote=True) + + def generate_invoice(amount: int, title: str, description: str, payload: str): invoice = raw_types.input_media_invoice.InputMediaInvoice( invoice=raw_types.invoice.Invoice( From 613f36bef81e094630913b00464f1cd3332584aa Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 21 Dec 2023 19:47:53 +0100 Subject: [PATCH 163/297] small fix for 'NoneType' object has no attribute 'lower' --- ytdlbot/ytdl_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 4d51839a..103ee4f8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -73,7 +73,7 @@ def wrapper(client: Client, message: types.Message): chat_id = getattr(message.from_user, "id", None) # message type check - if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"): + if message.chat.type != enums.ChatType.PRIVATE and not getattr(message, "text", "").lower().startswith("/ytdl"): logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) return From d0e2025c7cfc63bffd5f50ef2f30ba9ed1a5dcad Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 22 Dec 2023 17:41:31 +0100 Subject: [PATCH 164/297] set premium at first --- ytdlbot/premium.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py index 622b8936..cb879cf5 100644 --- a/ytdlbot/premium.py +++ b/ytdlbot/premium.py @@ -42,6 +42,8 @@ async def hello(client: Client, message: types.Message): url = data["url"] user_id = data["user_id"] + redis = Redis() + redis.r.hset("premium", user_id, 1) tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") output = pathlib.Path(tempdir.name, "%(title).70s.%(ext)s").as_posix() ydl_opts = {"restrictfilenames": False, "quiet": True, "outtmpl": output, "progress_hooks": [download_hook]} @@ -96,8 +98,6 @@ async def hello(client: Client, message: types.Message): ) tempdir.cleanup() - redis = Redis() - redis.r.hset("premium", user_id, 1) if __name__ == "__main__": From 4ec8a8ea2a39b6edc702b8a9e33866b791f11fc2 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 22 Dec 2023 20:22:56 +0100 Subject: [PATCH 165/297] premium 4GB max fix --- README.md | 4 ++-- ytdlbot/config.py | 6 ++++-- ytdlbot/constant.py | 2 ++ ytdlbot/downloader.py | 11 +++++++---- ytdlbot/premium.py | 8 +++++--- ytdlbot/tasks.py | 14 +++++++++++--- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a84fbc05..f42575a0 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ Due to limitations on servers and bandwidth, there are some restrictions on this * Each user is limited to 10 free downloads per 24-hour period * Maximum of three subscriptions allowed for YouTube channels. +* Files bigger than 2 GiB will require at least 1 download token. -If you need more downloads, you can purchase additional tokens. Additionally, you have the option of deploying your -own bot. See below instructions. +If you need more downloads, you can buy download tokens. # Features diff --git a/ytdlbot/config.py b/ytdlbot/config.py index 2a08923a..dc160aa6 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -20,8 +20,10 @@ REDIS = os.getenv("REDIS", "redis") -TG_MAX_SIZE = 2000 * 1024 * 1024 -# TG_MAX_SIZE = 10 * 1024 * 1024 +TG_PREMIUM_MAX_SIZE = 4000 * 1024 * 1024 +TG_NORMAL_MAX_SIZE = 2000 * 1024 * 1024 +# TG_NORMAL_MAX_SIZE = 10 * 1024 * 1024 + EXPIRE = 24 * 3600 diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 5a2f8adf..68ebdec2 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -45,6 +45,8 @@ class BotText: 4. Download for paid user will be automatically changed to Local mode to avoid queuing. +5. Paid user can download files larger than 2GB. + **Price:** valid permanently 1. 1 USD == {TOKEN_PRICE} tokens diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index d25e0c85..c16e09eb 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -32,7 +32,8 @@ ENABLE_ARIA2, ENABLE_FFMPEG, PREMIUM_USER, - TG_MAX_SIZE, + TG_NORMAL_MAX_SIZE, + TG_PREMIUM_MAX_SIZE, FileTooBig, IPv6, ) @@ -123,7 +124,9 @@ def download_hook(d: dict, bot_msg): if d["status"] == "downloading": downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) - if total > TG_MAX_SIZE: + if total > TG_PREMIUM_MAX_SIZE: + raise Exception(f"There's no way to handle a file of {sizeof_fmt(total)}.") + if total > TG_NORMAL_MAX_SIZE: msg = f"Your download file size {sizeof_fmt(total)} is too large for Telegram." if PREMIUM_USER: raise FileTooBig(msg) @@ -292,10 +295,10 @@ def split_large_video(video_paths: list): split = False for original_video in video_paths: size = os.stat(original_video).st_size - if size > TG_MAX_SIZE: + if size > TG_NORMAL_MAX_SIZE: split = True logging.warning("file is too large %s, splitting...", size) - subprocess.check_output(f"sh split-video.sh {original_video} {TG_MAX_SIZE * 0.95} ".split()) + subprocess.check_output(f"sh split-video.sh {original_video} {TG_NORMAL_MAX_SIZE * 0.95} ".split()) os.remove(original_video) if split and original_video: diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py index cb879cf5..b6365e80 100644 --- a/ytdlbot/premium.py +++ b/ytdlbot/premium.py @@ -68,12 +68,13 @@ async def hello(client: Client, message: types.Message): settings = payment.get_user_settings(user_id) video_path = next(pathlib.Path(tempdir.name).glob("*")) logging.info("Final filesize is %s", sizeof_fmt(video_path.stat().st_size)) + caption = "Powered by @benny_ytdlbot " if settings[2] == "audio": logging.info("Sending as audio") await client.send_audio( BOT_ID, video_path.as_posix(), - caption="Powered by ytdlbot ", + caption=caption, file_name=f"{user_id}.mp3", progress=upload_hook, ) @@ -82,7 +83,7 @@ async def hello(client: Client, message: types.Message): await client.send_document( BOT_ID, video_path.as_posix(), - caption="Powered by ytdlbot", + caption=caption, file_name=f"{user_id}.mp4", progress=upload_hook, ) @@ -91,13 +92,14 @@ async def hello(client: Client, message: types.Message): await client.send_video( BOT_ID, video_path.as_posix(), - caption="Powered by ytdlbot", + caption=caption, supports_streaming=True, file_name=f"{user_id}.mp4", progress=upload_hook, ) tempdir.cleanup() + logging.info("Finished sending %s", url) if __name__ == "__main__": diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 1ceb24a2..4fd63f2b 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -80,10 +80,18 @@ def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: def premium_button(user_id): redis = Redis() + payment = Payment() used = redis.r.hget("premium", user_id) ban = redis.r.hget("ban", user_id) - if used or ban: + paid_token = payment.get_pay_token(user_id) + + if ban: return None + # vip mode: vip user can use once per day, normal user can't use + # non vip mode: everyone can use once per day + if used or (ENABLE_VIP and paid_token == 0): + return None + markup = types.InlineKeyboardMarkup( [ [ @@ -108,7 +116,7 @@ def ytdl_download_task(chat_id: int, message_id: int, url: str): if markup: bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) else: - bot_msg.edit_text(f"{e}\n\n Big file download is not available now, please try again later.") + bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") except Exception: bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) logging.info("YouTube celery tasks ended.") @@ -180,7 +188,7 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod if markup: bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) else: - bot_msg.edit_text(f"{e}\n\n Big file download is not available now, please try again later.") + bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") except Exception as e: logging.error("Failed to download %s, error: %s", url, e) bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) From 682d7db2d6616a65855d675d078e848222f6a9b0 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 23 Dec 2023 13:03:45 +0100 Subject: [PATCH 166/297] disable tasks auto restart --- ytdlbot/tasks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 4fd63f2b..fed004e4 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -51,7 +51,6 @@ from limit import Payment from utils import ( apply_log_formatter, - auto_restart, customize_logger, get_metadata, get_revision, @@ -530,7 +529,7 @@ def run_celery(): threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Europe/London") - scheduler.add_job(auto_restart, "interval", seconds=900) + # scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() From b3cabe3c3b4c674d37a083ea078428aef465aeee Mon Sep 17 00:00:00 2001 From: cpanel10x Date: Mon, 25 Dec 2023 01:03:52 +0700 Subject: [PATCH 167/297] =?UTF-8?q?Update=20database.py=20-=20pymysql.err.?= =?UTF-8?q?InterfaceError:=20(0,=E2=80=9D)=20error=20(#325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check if the connection exists, and reconnect if disconnected. --- ytdlbot/database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 4f5df0fe..f836987c 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -258,6 +258,7 @@ def __init__(self): self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) + self.con.ping(reconnect = True) except Exception: logging.warning("MySQL connection failed, using fake mysql instead.") self.con = FakeMySQL() From 4b885cf5dff88ec4a511ff3d7a5624ba9c9d0a68 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 25 Dec 2023 12:43:16 +0100 Subject: [PATCH 168/297] auto restart 10 lines --- ytdlbot/tasks.py | 3 ++- ytdlbot/utils.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index fed004e4..4fd63f2b 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -51,6 +51,7 @@ from limit import Payment from utils import ( apply_log_formatter, + auto_restart, customize_logger, get_metadata, get_revision, @@ -529,7 +530,7 @@ def run_celery(): threading.Thread(target=run_celery, daemon=True).start() scheduler = BackgroundScheduler(timezone="Europe/London") - # scheduler.add_job(auto_restart, "interval", seconds=900) + scheduler.add_job(auto_restart, "interval", seconds=900) scheduler.start() idle() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 1094c897..64301c3d 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -193,7 +193,7 @@ def auto_restart(): if not os.path.exists(log_path): return with open(log_path) as f: - logs = "".join(tail_log(f, lines=50)) + logs = "".join(tail_log(f, lines=10)) det = Detector(logs) method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] From b82e48ca7424913b2ee0f42243099a3a012e700c Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 30 Dec 2023 13:05:01 +0100 Subject: [PATCH 169/297] more restart time for worker --- conf/supervisor_worker.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/supervisor_worker.conf b/conf/supervisor_worker.conf index dbad141c..af39ec0c 100644 --- a/conf/supervisor_worker.conf +++ b/conf/supervisor_worker.conf @@ -15,8 +15,8 @@ command=python tasks.py autorestart=true priority=900 stopasgroup=true -startsecs = 5 -startretries = 5 +startsecs = 10 +startretries = 100 redirect_stderr=true stdout_logfile_maxbytes = 50MB From 6c37e82b3dfaf58bad80dcf96d83d0a4c2bdc457 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 30 Dec 2023 14:58:03 +0100 Subject: [PATCH 170/297] add some debug symbols --- requirements.txt | 2 ++ ytdlbot/ytdl_bot.py | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/requirements.txt b/requirements.txt index 02ea76cb..a5cbd757 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,3 +27,5 @@ mnemonic==0.20 qrcode==7.4.2 blinker==1.7.0 flask===3.0.0 +objgraph==3.6.0 +pympler==1.0.1 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 103ee4f8..6d0491c4 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -19,10 +19,12 @@ from io import BytesIO from typing import Any +import objgraph import pyrogram.errors import qrcode import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler +from pympler import tracker from pyrogram import Client, enums, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions @@ -569,8 +571,17 @@ def trx_notify(_, **kwargs): app.send_message(user_id, text) +def track_memory(): + print("\n\nobjgraph result:") + objgraph.show_growth() + objgraph.show_most_common_types() + print("\n\npympler result:") + tr.print_diff() + + if __name__ == "__main__": MySQL() + tr = tracker.SummaryTracker() TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) scheduler.add_job(auto_restart, "interval", seconds=600) @@ -579,6 +590,7 @@ def trx_notify(_, **kwargs): scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) + scheduler.add_job(track_memory, "interval", seconds=120, max_instances=1) # default quota allocation of 10,000 units per day scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() From 882b313a181a945037902607a732576da75ac34c Mon Sep 17 00:00:00 2001 From: cpanel10x Date: Sat, 30 Dec 2023 20:59:25 +0700 Subject: [PATCH 171/297] Clean resond from yt-dlp download errror (#328) --- ytdlbot/tasks.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 4fd63f2b..e522f83a 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -118,7 +118,11 @@ def ytdl_download_task(chat_id: int, message_id: int, url: str): else: bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") except Exception: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) + error_msg = traceback.format_exc().split("yt_dlp.utils.DownloadError: ERROR: ") + if len(error_msg) > 1: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg[-1]}", disable_web_page_preview=True) + else: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) logging.info("YouTube celery tasks ended.") @@ -191,7 +195,11 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") except Exception as e: logging.error("Failed to download %s, error: %s", url, e) - bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) + error_msg = traceback.format_exc().split("yt_dlp.utils.DownloadError: ERROR: ") + if len(error_msg) > 1: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg[-1]}", disable_web_page_preview=True) + else: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): From 0ef589f9c03abfa5771dd6ac36b202055b766754 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 30 Dec 2023 22:27:19 +0100 Subject: [PATCH 172/297] Revert "add some debug symbols" This reverts commit 6c37e82b3dfaf58bad80dcf96d83d0a4c2bdc457. --- requirements.txt | 2 -- ytdlbot/ytdl_bot.py | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/requirements.txt b/requirements.txt index a5cbd757..02ea76cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,5 +27,3 @@ mnemonic==0.20 qrcode==7.4.2 blinker==1.7.0 flask===3.0.0 -objgraph==3.6.0 -pympler==1.0.1 diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 6d0491c4..103ee4f8 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -19,12 +19,10 @@ from io import BytesIO from typing import Any -import objgraph import pyrogram.errors import qrcode import yt_dlp from apscheduler.schedulers.background import BackgroundScheduler -from pympler import tracker from pyrogram import Client, enums, filters, types from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions @@ -571,17 +569,8 @@ def trx_notify(_, **kwargs): app.send_message(user_id, text) -def track_memory(): - print("\n\nobjgraph result:") - objgraph.show_growth() - objgraph.show_most_common_types() - print("\n\npympler result:") - tr.print_diff() - - if __name__ == "__main__": MySQL() - tr = tracker.SummaryTracker() TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) scheduler.add_job(auto_restart, "interval", seconds=600) @@ -590,7 +579,6 @@ def track_memory(): scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) - scheduler.add_job(track_memory, "interval", seconds=120, max_instances=1) # default quota allocation of 10,000 units per day scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() From 7689c97b78f10819d393c78714d6f304497de358 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 30 Dec 2023 22:28:46 +0100 Subject: [PATCH 173/297] add gc --- ytdlbot/database.py | 2 +- ytdlbot/ytdl_bot.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index f836987c..b622ca7f 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -258,7 +258,7 @@ def __init__(self): self.con = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" ) - self.con.ping(reconnect = True) + self.con.ping(reconnect=True) except Exception: logging.warning("MySQL connection failed, using fake mysql instead.") self.con = FakeMySQL() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 103ee4f8..99810d61 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -8,6 +8,7 @@ __author__ = "Benny " import contextlib +import gc import json import logging import os @@ -575,6 +576,7 @@ def trx_notify(_, **kwargs): scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) + scheduler.add_job(gc.collect, "interval", seconds=3600) if not IS_BACKUP_BOT: scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) From 3edcfa903a1eae5d8481ede0a48667d8a9b01886 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 30 Dec 2023 22:36:55 +0100 Subject: [PATCH 174/297] fix influxdb data collect --- ytdlbot/database.py | 2 +- ytdlbot/ytdl_bot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index b622ca7f..25fa4753 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -317,7 +317,7 @@ def get_worker_data() -> dict: password = os.getenv("FLOWER_PASSWORD", "123456abc") token = base64.b64encode(f"{username}:{password}".encode()).decode() headers = {"Authorization": f"Basic {token}"} - r = requests.get("https://celery.dmesg.app/dashboard?json=1", headers=headers) + r = requests.get("https://celery.dmesg.app/workers?json=1", headers=headers) if r.status_code != 200: return dict(data=[]) return r.json() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 99810d61..aba88ddc 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -573,7 +573,7 @@ def trx_notify(_, **kwargs): if __name__ == "__main__": MySQL() TRX_SIGNAL.connect(trx_notify) - scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6}) + scheduler = BackgroundScheduler(timezone="Europe/London") scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) scheduler.add_job(gc.collect, "interval", seconds=3600) From a8c4db9ee58bde3d3672447519b38883848676af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 12:49:00 +0100 Subject: [PATCH 175/297] Bump yt-dlp from 2023.11.16 to 2023.12.30 (#329) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2023.11.16 to 2023.12.30. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2023.11.16...2023.12.30) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 02ea76cb..f35d810b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==2.0.106 tgcrypto==1.2.5 -yt-dlp==2023.11.16 +yt-dlp==2023.12.30 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 7962d9db00407a8263914f4a704ad7444d870f8f Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 1 Jan 2024 19:31:55 +0100 Subject: [PATCH 176/297] change influxdb server --- ytdlbot/database.py | 11 ++++++++++- ytdlbot/ytdl_bot.py | 2 -- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 25fa4753..a91d7c59 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -305,7 +305,16 @@ def set_user_settings(self, user_id: int, field: str, value: str): class InfluxDB: def __init__(self): - self.client = InfluxDBClient(host=os.getenv("INFLUX_HOST", "192.168.7.233"), database="celery") + self.client = InfluxDBClient( + host=os.getenv("INFLUX_HOST"), + path=os.getenv("INFLUX_PATH"), + port=443, + username="nova", + password=os.getenv("INFLUX_PASS"), + database="celery", + ssl=True, + verify_ssl=True, + ) self.data = None def __del__(self): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index aba88ddc..ea9f82da 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -8,7 +8,6 @@ __author__ = "Benny " import contextlib -import gc import json import logging import os @@ -576,7 +575,6 @@ def trx_notify(_, **kwargs): scheduler = BackgroundScheduler(timezone="Europe/London") scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) - scheduler.add_job(gc.collect, "interval", seconds=3600) if not IS_BACKUP_BOT: scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) From 09352a1e61e997b54399000a5451c98fa325b5eb Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 9 Jan 2024 19:37:19 +0100 Subject: [PATCH 177/297] deps --- conf/supervisor_worker.conf | 2 +- requirements.txt | 2 +- ytdlbot/utils.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/supervisor_worker.conf b/conf/supervisor_worker.conf index af39ec0c..5ca84301 100644 --- a/conf/supervisor_worker.conf +++ b/conf/supervisor_worker.conf @@ -15,7 +15,7 @@ command=python tasks.py autorestart=true priority=900 stopasgroup=true -startsecs = 10 +startsecs = 5 startretries = 100 redirect_stderr=true diff --git a/requirements.txt b/requirements.txt index f35d810b..027c2687 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 tronpy==0.4.0 -mnemonic==0.20 +mnemonic==0.21 qrcode==7.4.2 blinker==1.7.0 flask===3.0.0 diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 64301c3d..b3a580f0 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -183,7 +183,7 @@ def next_salt_detector(self): def connection_reset_detector(self): text = "Send exception: ConnectionResetError Connection lost" - if text in self.logs: + if self.logs.count(text) >= 5: logging.critical("connection lost: %s ", self.func_name()) return True From aa66ecd6d8650270bf2c6b195f1dcd4db9af232a Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 22 Jan 2024 18:01:12 +0100 Subject: [PATCH 178/297] fix #335 #334 --- README.md | 2 +- ytdlbot/keep_alive.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 ytdlbot/keep_alive.py diff --git a/README.md b/README.md index f42575a0..ffe13c25 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you need more downloads, you can buy download tokens. 9. celery worker distribution - faster than before. 10. subscriptions to YouTube Channels 11. cache mechanism - download once for the same video. -12. instagram posts +12. instagram posts(only available for my bot) 13. 4 GiB file size support with Telegram Premium > If you download files larger than 2 GiB, you agreed that this file will be uploaded by me. I know who you are and what diff --git a/ytdlbot/keep_alive.py b/ytdlbot/keep_alive.py new file mode 100644 index 00000000..9cf90068 --- /dev/null +++ b/ytdlbot/keep_alive.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - keep_alive.py +# 2024-01-22 17:59 + +import time + +while True: + print("I'm still alive") + time.sleep(10) From ebd17fe9d48810065e0674bef484c61b15cbdcef Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 9 Feb 2024 19:01:44 +0100 Subject: [PATCH 179/297] bumps --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 027c2687..f420c548 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,10 +8,10 @@ PyMySQL==1.1.0 celery==5.3.6 filetype==1.2.0 flower==2.0.1 -psutil==5.9.7 +psutil==5.9.8 influxdb==5.3.1 -beautifulsoup4==4.12.2 -fakeredis==2.20.1 +beautifulsoup4==4.12.3 +fakeredis==2.21.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.1 @@ -26,4 +26,4 @@ tronpy==0.4.0 mnemonic==0.21 qrcode==7.4.2 blinker==1.7.0 -flask===3.0.0 +flask===3.0.2 From 0f6f0cc34eca4536c7cbbf4a92254e56a82f7ee1 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 10 Feb 2024 19:29:56 +0100 Subject: [PATCH 180/297] add inline mode fix #345 --- README.md | 3 +++ ytdlbot/database.py | 34 ++++++++++++++++++++++++++-- ytdlbot/tasks.py | 6 ++++- ytdlbot/ytdl_bot.py | 54 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 92 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ffe13c25..d44acbbf 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ If you need more downloads, you can buy download tokens. 11. cache mechanism - download once for the same video. 12. instagram posts(only available for my bot) 13. 4 GiB file size support with Telegram Premium +14. History and inline mode support > If you download files larger than 2 GiB, you agreed that this file will be uploaded by me. I know who you are and what > you download. @@ -285,6 +286,8 @@ unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. uncache - Delete cache for this link, owner only. purge - Delete all tasks, owner only. +show_history - Show download history +clear_history - Clear download history ``` # Test data diff --git a/ytdlbot/database.py b/ytdlbot/database.py index a91d7c59..26baa067 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -225,6 +225,7 @@ class MySQL: resolution varchar(128) null, method varchar(64) null, mode varchar(32) default 'Celery' null, + history varchar(10) default 'OFF' null, constraint settings_pk primary key (user_id) ); @@ -252,6 +253,14 @@ class MySQL: is_valid boolean default 1 null ) CHARSET=utf8mb4; """ + history_sql = """ + create table if not exists history + ( + user_id bigint null, + link varchar(256) null, + title varchar(512) null + ) CHARSET=utf8mb4; + """ def __init__(self): try: @@ -273,6 +282,7 @@ def init_db(self): self.cur.execute(self.settings_sql) self.cur.execute(self.channel_sql) self.cur.execute(self.subscribe_sql) + self.cur.execute(self.history_sql) self.con.commit() def __del__(self): @@ -282,7 +292,7 @@ def get_user_settings(self, user_id: int) -> tuple: self.cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) data = self.cur.fetchone() if data is None: - return 100, "high", "video", "Celery" + return 100, "high", "video", "Celery", "OFF" return data def set_user_settings(self, user_id: int, field: str, value: str): @@ -297,11 +307,31 @@ def set_user_settings(self, user_id: int, field: str, value: str): if field == "method": method = value resolution = "high" - cur.execute("INSERT INTO settings VALUES (%s,%s,%s,%s)", (user_id, resolution, method, "Celery")) + cur.execute("INSERT INTO settings VALUES (%s,%s,%s,%s,%s)", (user_id, resolution, method, "Celery", "OFF")) else: cur.execute(f"UPDATE settings SET {field} =%s WHERE user_id = %s", (value, user_id)) self.con.commit() + def show_history(self, user_id: int): + self.cur.execute("SELECT link,title FROM history WHERE user_id = %s", (user_id,)) + data = self.cur.fetchall() + return "\n".join([f"{i[0]} {i[1]}" for i in data]) + + def clear_history(self, user_id: int): + self.cur.execute("DELETE FROM history WHERE user_id = %s", (user_id,)) + self.con.commit() + + def add_history(self, user_id: int, link: str, title: str): + self.cur.execute("INSERT INTO history VALUES (%s,%s,%s)", (user_id, link, title)) + self.con.commit() + + def search_history(self, user_id: int, kw: str): + self.cur.execute("SELECT * FROM history WHERE user_id = %s AND title like %s", (user_id, f"%{kw}%")) + data = self.cur.fetchall() + if data: + return data + return None + class InfluxDB: def __init__(self): diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index e522f83a..ee91bd28 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -46,7 +46,7 @@ FileTooBig, ) from constant import BotText -from database import Redis +from database import Redis, MySQL from downloader import edit_text, tqdm_progress, upload_hook, ytdl_download from limit import Payment from utils import ( @@ -295,6 +295,10 @@ def ytdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, ur logging.info("Download complete.") client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) bot_msg.edit_text("Download complete. Sending now...") + data = MySQL().get_user_settings(chat_id) + if data[4] == "ON": + logging.info("Adding to history...") + MySQL().add_history(chat_id, url, pathlib.Path(video_paths[0]).name) try: upload_processor(client, bot_msg, url, video_paths) except pyrogram.errors.Flood as e: diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ea9f82da..36625cda 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -226,6 +226,24 @@ def sub_count_handler(client: Client, message: types.Message): client.send_document(chat_id, f) +@app.on_message(filters.command(["show_history"])) +def show_history(client: Client, message: types.Message): + chat_id = message.chat.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + data = MySQL().show_history(chat_id) + if data: + client.send_message(chat_id, data, disable_web_page_preview=True) + else: + client.send_message(chat_id, "No history found.") + + +@app.on_message(filters.command(["clear_history"])) +def clear_history(client: Client, message: types.Message): + chat_id = message.chat.id + MySQL().clear_history(chat_id) + message.reply_text("History cleared.", quote=True) + + @app.on_message(filters.command(["direct"])) def direct_handler(client: Client, message: types.Message): redis = Redis() @@ -249,9 +267,9 @@ def settings_handler(client: Client, message: types.Message): payment = Payment() client.send_chat_action(chat_id, enums.ChatAction.TYPING) data = MySQL().get_user_settings(chat_id) - set_mode = data[-1] + set_mode = data[3] text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") - mode_text = f"Download mode: **{set_mode}**" + mode_text = f"Download mode: **{set_mode}**\nHistory record: {data[4]}" if message.chat.username == OWNER or payment.get_pay_token(chat_id): extra = [types.InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] else: @@ -269,6 +287,9 @@ def settings_handler(client: Client, message: types.Message): types.InlineKeyboardButton("Medium Quality", callback_data="medium"), types.InlineKeyboardButton("Low Quality", callback_data="low"), ], + [ + types.InlineKeyboardButton("Toggle History", callback_data=f"history-{data[4]}"), + ], extra, ] ) @@ -505,6 +526,35 @@ def download_resolution_callback(client: Client, callback_query: types.CallbackQ callback_query.answer(f"Your default download quality was set to {callback_query.data}") +@app.on_callback_query(filters.regex(r"history.*")) +def set_history_callback(client: Client, callback_query: types.CallbackQuery): + chat_id = callback_query.message.chat.id + data = callback_query.data.split("-")[-1] + + r = "OFF" if data == "ON" else "ON" + logging.info("Setting %s file type to %s", chat_id, data) + MySQL().set_user_settings(chat_id, "history", r) + callback_query.answer("History setting updated.") + + +@app.on_inline_query() +def inline_query(client: Client, inline_query: types.InlineQuery): + kw = inline_query.query + user_id = inline_query.from_user.id + data = MySQL().search_history(user_id, kw) + if data: + results = [ + types.InlineQueryResultArticle( + id=str(i), + title=item[1], + description=item[2], + input_message_content=types.InputTextMessageContent(item[1]), + ) + for i, item in enumerate(data) + ] + client.answer_inline_query(inline_query.id, results) + + @app.on_callback_query(filters.regex(r"convert")) def audio_callback(client: Client, callback_query: types.CallbackQuery): redis = Redis() From cd8dae87d21ef04bd1ea214e832f1baf2d76ecf1 Mon Sep 17 00:00:00 2001 From: Daniel <42929363+deusyu@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:10:39 +0800 Subject: [PATCH 181/297] Update tasks.py (#356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tasks.py fix 'Downloaded by ๐Ÿคฉ' * Update tasks.py --- ytdlbot/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index ee91bd28..860129e0 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -179,7 +179,7 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod redis.update_metrics("cache_hit") return redis.update_metrics("cache_miss") - mode = mode or payment.get_user_settings(chat_id)[-1] + mode = mode or payment.get_user_settings(chat_id)[3] if ENABLE_CELERY and mode in [None, "Celery"]: # in celery mode, producer has lost control of this task. ytdl_download_task.delay(chat_id, bot_msg.id, url) From 885f93f11ef256dab05901e19288819794ca2553 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 14:42:42 +0100 Subject: [PATCH 182/297] Bump fakeredis from 2.21.0 to 2.21.1 (#354) Bumps [fakeredis](https://github.com/cunla/fakeredis-py) from 2.21.0 to 2.21.1. - [Release notes](https://github.com/cunla/fakeredis-py/releases) - [Commits](https://github.com/cunla/fakeredis-py/compare/v2.21.0...v2.21.1) --- updated-dependencies: - dependency-name: fakeredis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f420c548..3a80b902 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==2.0.1 psutil==5.9.8 influxdb==5.3.1 beautifulsoup4==4.12.3 -fakeredis==2.21.0 +fakeredis==2.21.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.1 From 5dbc4fbe7a7c53f850d7d613c86ed1c17613199a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 14:42:50 +0100 Subject: [PATCH 183/297] Bump tqdm from 4.66.1 to 4.66.2 (#352) Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.2. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.2) --- updated-dependencies: - dependency-name: tqdm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3a80b902..048fddb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.1 requests==2.31.0 -tqdm==4.66.1 +tqdm==4.66.2 requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 From 5be9d65f38b395a72352cb57f04cd5f6990a7a45 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 25 Feb 2024 15:03:44 +0100 Subject: [PATCH 184/297] bs4 exact match --- ytdlbot/channel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py index 52d2236b..fd70a1ec 100644 --- a/ytdlbot/channel.py +++ b/ytdlbot/channel.py @@ -66,7 +66,7 @@ def extract_canonical_link(url: str) -> str: html_doc = requests.get(url, headers=headers, cookies=cookie, timeout=5).text soup = BeautifulSoup(html_doc, "html.parser") for prop in props: - element = soup.find("link", rel=prop) + element = soup.find(lambda tag: tag.name == "link" and tag.get("rel") == ["prop"]) try: href = element["href"] if href not in ["null", "", None, "https://consent.youtube.com/m"]: From e297ceb64d50cd1cd24386499b524685c88dfca3 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 1 Mar 2024 21:40:17 +0100 Subject: [PATCH 185/297] use yt-dlp with commit hash --- Dockerfile | 2 +- requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d2408e6..737a0c4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11 as builder ADD requirements.txt /tmp/ -RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt +RUN apt update && apt install -y git && pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt FROM python:3.11-slim diff --git a/requirements.txt b/requirements.txt index 048fddb5..fb5b8293 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==2.0.106 tgcrypto==1.2.5 -yt-dlp==2023.12.30 +git+https://github.com/yt-dlp/yt-dlp@413d3675804599bc8fe419c19e36490fd8f0b30f APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 @@ -14,7 +14,7 @@ beautifulsoup4==4.12.3 fakeredis==2.21.1 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==5.0.1 +redis==5.0.2 requests==2.31.0 tqdm==4.66.2 requests-toolbelt==1.0.0 From 5f9329eea06caaf7c3f63308e1764f76aefbf1fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 22:05:24 +0100 Subject: [PATCH 186/297] Bump redis from 5.0.1 to 5.0.2 (#359) Bumps [redis](https://github.com/redis/redis-py) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.0.1...v5.0.2) --- updated-dependencies: - dependency-name: redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 76172fb0b73afbe329a48f9d8e05850a3d088757 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 3 Mar 2024 17:31:32 +0100 Subject: [PATCH 187/297] auth key --- ytdlbot/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index b3a580f0..29a09c58 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -158,6 +158,13 @@ def func_name(): return pyinspect.stack()[1][3] return "N/A" + def auth_key_detector(self): + text = "Server sent transport error: 404 (auth key not found)" + if self.logs.count(text) >= 3: + logging.critical("auth key not found: %s", self.func_name()) + os.unlink("*.session") + return True + def updates_too_long_detector(self): # If you're seeing this, that means you have logged more than 10 device # and the earliest account was kicked out. Restart the program could get you back in. From aab8abd74c056305da1eab33ef97f21d8bf4f98c Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 7 Apr 2024 20:14:34 +0200 Subject: [PATCH 188/297] disable buy --- ytdlbot/ytdl_bot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 36625cda..6713ec2f 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -307,6 +307,8 @@ def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message.reply_text("Not available now.") + return # currency USD token_count = message.text.replace("/buy", "").strip() if token_count.isdigit(): From 0547fc69a203a930b7ecdf05c92ed5e27db90ec5 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:22:44 +0530 Subject: [PATCH 189/297] Enhanced download functionality for yt-dlp unsupported links (#363) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improved download ability for things that yt-dlp doesnโ€™t support out of the box * yt-dlp bump to new release 2024.03.10 * Update README.md * Update README.md * Update README.md * sp_ytdl_download: remove aria2 and clean up --- README.md | 37 ++++++++------ requirements.txt | 2 +- ytdlbot/downloader.py | 20 ++------ ytdlbot/sp_downloader.py | 105 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 32 deletions(-) create mode 100644 ytdlbot/sp_downloader.py diff --git a/README.md b/README.md index d44acbbf..386bba66 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![docker image](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml/badge.svg)](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml) -YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ +**YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ** -This Telegram bot allows you to download videos from YouTube and other supported websites, including Instagram! +This Telegram bot allows you to download videos from YouTube and [other supported websites](#supported-websites). # Usage @@ -12,18 +12,14 @@ This Telegram bot allows you to download videos from YouTube and other supported Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. -Send link directly to the bot. Any -Websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) will work too. +Just send a link directly to the bot. -# Limitations of my bot +# Supported websites -Due to limitations on servers and bandwidth, there are some restrictions on this free service. - -* Each user is limited to 10 free downloads per 24-hour period -* Maximum of three subscriptions allowed for YouTube channels. -* Files bigger than 2 GiB will require at least 1 download token. - -If you need more downloads, you can buy download tokens. +* YouTube ๐Ÿ˜… +* Any websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) +* Instagram (Videos, Photos, Reels, IGTV & carousel) +* Pixeldrain # Features @@ -42,8 +38,21 @@ If you need more downloads, you can buy download tokens. 13. 4 GiB file size support with Telegram Premium 14. History and inline mode support -> If you download files larger than 2 GiB, you agreed that this file will be uploaded by me. I know who you are and what -> you download. +> [!NOTE] +> **For users of [my official bot](https://t.me/benny_ytdlbot)**\ +> Files larger than 2 GiB will be automatically uploaded by me(My Premium Account). By utilizing our service for such downloads, you consent to this process. \ +> That means I know who you are and what you download. \ +> Rest assured that we handle your personal information with the utmost care. +> +> ## Limitations +> Due to limitations on servers and bandwidth, there are some restrictions on this free service. +> * Each user is limited to 10 free downloads per 24-hour period +> * Maximum of three subscriptions allowed for YouTube channels. +> * Files bigger than 2 GiB will require at least 1 download token. +> +> If you need more downloads, you can buy download tokens. +> +> **Thank you for using the [official bot](https://t.me/benny_ytdlbot).** # Screenshots diff --git a/requirements.txt b/requirements.txt index fb5b8293..f7c615cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pyrogram==2.0.106 tgcrypto==1.2.5 -git+https://github.com/yt-dlp/yt-dlp@413d3675804599bc8fe419c19e36490fd8f0b30f +yt-dlp==2024.03.10 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index c16e09eb..db14de64 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -27,6 +27,8 @@ from pyrogram import types from tqdm import tqdm +from sp_downloader import sp_dl + from config import ( AUDIO_FORMAT, ENABLE_ARIA2, @@ -220,7 +222,7 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: None, ] adjust_formats(chat_id, url, formats, hijack) - if download_instagram(url, tempdir): + if sp_dl(url, tempdir): return list(pathlib.Path(tempdir).glob("*")) address = ["::", "0.0.0.0"] if IPv6 else [None] @@ -303,19 +305,3 @@ def split_large_video(video_paths: list): if split and original_video: return [i for i in pathlib.Path(original_video).parent.glob("*")] - - -def download_instagram(url: str, tempdir: str): - if not url.startswith("https://www.instagram.com"): - return False - - resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() - if url_results := resp.get("data"): - for link in url_results: - content = requests.get(link, stream=True).content - ext = filetype.guess_extension(content) - save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") - with open(save_path, "wb") as f: - f.write(content) - - return True diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py new file mode 100644 index 00000000..24edf01e --- /dev/null +++ b/ytdlbot/sp_downloader.py @@ -0,0 +1,105 @@ +#!/usr/local/bin/python3 +# coding: utf-8 + +# ytdlbot - sp_downloader.py +# 3/16/24 16:32 +# + +__author__ = "Benny , SanujaNS " + +import pathlib +import logging +import traceback +import re +import requests +from tqdm import tqdm +import json +from bs4 import BeautifulSoup +from urllib.parse import parse_qs, urlparse +import filetype +import yt_dlp as ytdl + +from config import ( + IPv6, +) + +user_agent = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.128 Safari/537.36" +) + + +def sp_dl(url: str, tempdir: str): + """Specific link downloader""" + domain = urlparse(url).hostname + if not any( + x in domain + for x in [ + "www.instagram.com", + "pixeldrain.com", + "mediafire.com", + ] + ): + return False + if "www.instagram.com" in domain: + return instagram(url, tempdir) + elif "pixeldrain.com" in domain: + return pixeldrain(url, tempdir) + elif "www.xasiat.com" in domain: + return xasiat(url, tempdir) + + +def sp_ytdl_download(url: str, tempdir: str): + output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() + ydl_opts = { + "outtmpl": output, + "restrictfilenames": False, + "quiet": True, + "format": None, + } + + address = ["::", "0.0.0.0"] if IPv6 else [None] + error = None + video_paths = None + for addr in address: + ydl_opts["source_address"] = addr + try: + logging.info("Downloading %s", url) + with ytdl.YoutubeDL(ydl_opts) as ydl: + ydl.download([url]) + video_paths = list(pathlib.Path(tempdir).glob("*")) + break + except Exception: + error = traceback.format_exc() + logging.error("Download failed for %s - %s", url) + + if not video_paths: + raise Exception(error) + + return video_paths + + +def instagram(url: str, tempdir: str): + resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() + if url_results := resp.get("data"): + for link in url_results: + content = requests.get(link, stream=True).content + ext = filetype.guess_extension(content) + save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") + with open(save_path, "wb") as f: + f.write(content) + + return True + +def pixeldrain(url: str, tempdir: str): + user_page_url_regex = r'https://pixeldrain.com/u/(\w+)' + match = re.match(user_page_url_regex, url) + if match: + url = 'https://pixeldrain.com/api/file/{}?download'.format(match.group(1)) + sp_ytdl_download(url, tempdir) + else: + return url + + return True + +def xasiat(url: str, tempdir: str): + return False \ No newline at end of file From a86e0736c7b5e7d0d3f6273bbb2ae8928e8c52af Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 8 Apr 2024 18:01:07 +0200 Subject: [PATCH 190/297] reformat and bumps --- requirements.txt | 8 ++++---- ytdlbot/sp_downloader.py | 30 +++++++++++++----------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/requirements.txt b/requirements.txt index f7c615cc..134d0706 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,10 +11,10 @@ flower==2.0.1 psutil==5.9.8 influxdb==5.3.1 beautifulsoup4==4.12.3 -fakeredis==2.21.1 +fakeredis==2.21.3 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==5.0.2 +redis==5.0.3 requests==2.31.0 tqdm==4.66.2 requests-toolbelt==1.0.0 @@ -22,8 +22,8 @@ ffpb==0.4.1 youtube-search-python==1.6.6 token-bucket==0.3.0 coloredlogs==15.0.1 -tronpy==0.4.0 +tronpy==0.5.0 mnemonic==0.21 qrcode==7.4.2 blinker==1.7.0 -flask===3.0.2 +flask===3.0.3 diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py index 24edf01e..0bbe4ae9 100644 --- a/ytdlbot/sp_downloader.py +++ b/ytdlbot/sp_downloader.py @@ -5,27 +5,23 @@ # 3/16/24 16:32 # -__author__ = "Benny , SanujaNS " +__author__ = "SanujaNS " -import pathlib import logging -import traceback +import pathlib import re -import requests -from tqdm import tqdm -import json -from bs4 import BeautifulSoup -from urllib.parse import parse_qs, urlparse +import traceback +from urllib.parse import urlparse + import filetype +import requests import yt_dlp as ytdl from config import ( IPv6, ) -user_agent = ( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.128 Safari/537.36" -) +user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.128 Safari/537.36" def sp_dl(url: str, tempdir: str): @@ -90,16 +86,16 @@ def instagram(url: str, tempdir: str): return True + def pixeldrain(url: str, tempdir: str): - user_page_url_regex = r'https://pixeldrain.com/u/(\w+)' + user_page_url_regex = r"https://pixeldrain.com/u/(\w+)" match = re.match(user_page_url_regex, url) if match: - url = 'https://pixeldrain.com/api/file/{}?download'.format(match.group(1)) - sp_ytdl_download(url, tempdir) + url = "https://pixeldrain.com/api/file/{}?download".format(match.group(1)) + return sp_ytdl_download(url, tempdir) else: return url - - return True + def xasiat(url: str, tempdir: str): - return False \ No newline at end of file + return False From fe628adbf93f21c4361810602afe78f64c043376 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 9 Apr 2024 17:59:42 +0200 Subject: [PATCH 191/297] use fork of pyrogram --- requirements.txt | 2 +- ytdlbot/client_init.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 134d0706..2145f9a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pyrogram==2.0.106 +git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 yt-dlp==2024.03.10 APScheduler==3.10.4 diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py index 76b316cd..7702cd51 100644 --- a/ytdlbot/client_init.py +++ b/ytdlbot/client_init.py @@ -9,7 +9,7 @@ from pyrogram import Client -from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, WORKERS, IPv6 +from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: @@ -20,6 +20,6 @@ def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: bot_token=TOKEN, workers=workers, ipv6=IPv6, - max_concurrent_transmissions=max(1, WORKERS // 2), + # max_concurrent_transmissions=max(1, WORKERS // 2), # https://github.com/pyrogram/pyrogram/issues/1225#issuecomment-1446595489 ) From cfc798330a9231de427db948e1eeb0d7e33654a4 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 9 Apr 2024 18:31:37 +0200 Subject: [PATCH 192/297] update text --- README.md | 14 +++++++++----- docker-compose.yml | 16 ---------------- ytdlbot/constant.py | 3 ++- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 386bba66..6558635e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ This Telegram bot allows you to download videos from YouTube and [other supported websites](#supported-websites). +**Celery mode won't work and I don't know why. So I am shutting down this bot** + # Usage [https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) @@ -31,7 +33,7 @@ Just send a link directly to the bot. 6. payment support: afdian, buy me a coffee, Telegram Payment and Tron(TRX) 7. different video resolutions 8. sending as file or streaming as video -9. celery worker distribution - faster than before. +9. celery worker distribution - faster than before. **NOT WORKING** 10. subscriptions to YouTube Channels 11. cache mechanism - download once for the same video. 12. instagram posts(only available for my bot) @@ -64,7 +66,7 @@ Just send a link directly to the bot. ![](assets/instagram.png) -## celery +## celery **NOT WORKING** ![](assets/2.jpeg) @@ -157,7 +159,7 @@ vim env/ytdl.env You can configure all the following environment variables: -* WORKERS: workers count for celery +* WORKERS: workers count for celery **NOT WORKING** * PYRO_WORKERS: number of workers for pyrogram, default is 100 * APP_ID: **REQUIRED**, get it from https://core.telegram.org/ * APP_HASH: **REQUIRED** @@ -168,8 +170,8 @@ You can configure all the following environment variables: * OWNER: owner username * AUTHORIZED_USER: only authorized users can use the bot * REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot -* ENABLE_CELERY: celery mode, default: disable -* BROKER: celery broker, should be redis://redis:6379/0 +* ENABLE_CELERY: celery mode, default: disable **NOT WORKING** +* BROKER: celery broker, should be redis://redis:6379/0 **NOT WORKING** * MYSQL_HOST:MySQL host * MYSQL_USER: MySQL username * MYSQL_PASS: MySQL password @@ -210,6 +212,7 @@ mysql -u root -p ### 3.2.2 Setup flower db in `ytdlbot/ytdlbot/data` Required if you enable celery and want to monitor the workers. +**NOT WORKING** ```shell {} ~ python3 @@ -253,6 +256,7 @@ docker-compose up -d mysql redis ytdl ### 4.3 Celery worker mode +**NOT WORKING** Firstly, set `ENABLE_CELERY` to true. And then, on one machine: ```shell diff --git a/docker-compose.yml b/docker-compose.yml index d5658b48..e0b6ec13 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,22 +35,6 @@ services: - redis volumes: - ./data/vnstat/:/var/lib/vnstat/ - labels: - - "com.centurylinklabs.watchtower.enable=true" - - flower: - image: bennythink/ytdlbot - env_file: - - env/ytdl.env - restart: unless-stopped - command: [ "/usr/local/bin/celery", - "-A", "flower_tasks", "flower", - "--basic_auth=benny:123456", - "--address=0.0.0.0", "--persistent","--purge_offline_workers=3600" ] - volumes: - - ./data/flower:/ytdlbot/ytdlbot/flower - ports: - - "127.0.0.1:15555:5555" premium: image: bennythink/ytdlbot diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 68ebdec2..35913ca7 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -24,7 +24,8 @@ class BotText: start = """ Welcome to YouTube Download bot. Type /help for more information. - Backup bot: @benny_2ytdlbot + **I am shutting down this bot because it doesn't work on celery mode.** + Join https://t.me/+OGRC8tp9-U9mZDZl for updates.""" help = f""" From fb51f12eec4d31e20b4e8a104e313fa3d0c7b8d3 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 9 Apr 2024 18:46:28 +0200 Subject: [PATCH 193/297] update readme --- README.md | 7 ++++--- ytdlbot/constant.py | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6558635e..6007c79e 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,14 @@ This Telegram bot allows you to download videos from YouTube and [other supported websites](#supported-websites). -**Celery mode won't work and I don't know why. So I am shutting down this bot** +**Celery mode won't work and I don't know why. So I may shutting down this bot soon.** # Usage -[https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) +* EU(recommended): [https://t.me/benny_2ytdlbot](https://t.me/benny_2ytdlbot) +* Asia:[https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) -Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. +* Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. Just send a link directly to the bot. diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 35913ca7..16773ca0 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -23,8 +23,9 @@ class BotText: start = """ - Welcome to YouTube Download bot. Type /help for more information. - **I am shutting down this bot because it doesn't work on celery mode.** + Welcome to YouTube Download bot. Type /help for more information. Recommend to use EU Bot + EU๐Ÿ‡ช๐Ÿ‡บ: @benny_2ytdlbot + SG๐Ÿ‡ธ๐Ÿ‡ฌ๏ผš@benny_ytdlbot Join https://t.me/+OGRC8tp9-U9mZDZl for updates.""" From 1940f2c849f50ede118cbd8b99deb200af332463 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 10 Apr 2024 18:29:31 +0200 Subject: [PATCH 194/297] bump --- requirements.txt | 2 +- ytdlbot/config.py | 6 +----- ytdlbot/database.py | 4 ++-- ytdlbot/ytdl_bot.py | 12 +++++------- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2145f9a4..2b80a826 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.03.10 +yt-dlp==2024.04.9 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 diff --git a/ytdlbot/config.py b/ytdlbot/config.py index dc160aa6..b9e62376 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -37,12 +37,8 @@ REQUIRED_MEMBERSHIP: str = os.getenv("REQUIRED_MEMBERSHIP", "") # celery related -IS_BACKUP_BOT = os.getenv("IS_BACKUP_BOT") ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) -if IS_BACKUP_BOT: - BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/1") -else: - BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/0") +BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/1") MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql") MYSQL_USER = os.getenv("MYSQL_USER", "root") diff --git a/ytdlbot/database.py b/ytdlbot/database.py index 26baa067..6e3356d8 100644 --- a/ytdlbot/database.py +++ b/ytdlbot/database.py @@ -25,7 +25,7 @@ from beautifultable import BeautifulTable from influxdb import InfluxDBClient -from config import IS_BACKUP_BOT, MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS +from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS init_con = sqlite3.connect(":memory:", check_same_thread=False) @@ -71,7 +71,7 @@ def sub(sql): class Redis: def __init__(self): - db = 1 if IS_BACKUP_BOT else 0 + db = 1 try: self.r = redis.StrictRedis(host=REDIS, db=db, decode_responses=True) self.r.ping() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 6713ec2f..ab7504f3 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -37,7 +37,6 @@ ENABLE_CELERY, ENABLE_FFMPEG, ENABLE_VIP, - IS_BACKUP_BOT, M3U8_SUPPORT, OWNER, PLAYLIST_SUPPORT, @@ -627,12 +626,11 @@ def trx_notify(_, **kwargs): scheduler = BackgroundScheduler(timezone="Europe/London") scheduler.add_job(auto_restart, "interval", seconds=600) scheduler.add_job(clean_tempfile, "interval", seconds=120) - if not IS_BACKUP_BOT: - scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) - scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) - scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) - # default quota allocation of 10,000 units per day - scheduler.add_job(periodic_sub_check, "interval", seconds=3600) + scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) + scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) + # scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) + # default quota allocation of 10,000 units per day + # scheduler.add_job(periodic_sub_check, "interval", seconds=3600) scheduler.start() banner = f""" โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ From fc0745dbba0a9db30abeee6f32978ac293b5ddfc Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 19 Apr 2024 22:50:51 +0530 Subject: [PATCH 195/297] add support for krakenfiles and improve selection process (#370) --- README.md | 13 +++++++++++ ytdlbot/sp_downloader.py | 50 ++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 6007c79e..ed6dbfec 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Just send a link directly to the bot. * Any websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) * Instagram (Videos, Photos, Reels, IGTV & carousel) * Pixeldrain +* KrakenFiles # Features @@ -305,6 +306,7 @@ clear_history - Clear download history ``` # Test data +
Tap to expand ## Test video @@ -327,6 +329,17 @@ https://twitter.com/BennyThinks/status/1475836588542341124 * image carousel: https://www.instagram.com/p/C0ozPQ5o536/ * video and image carousel: https://www.instagram.com/p/C0ozhsVo-m8/ +## Test Pixeldrain + +https://pixeldrain.com/u/765ijw9i + +## Test KrakenFiles + +https://krakenfiles.com/view/oqmSTF0T5t/file.html + +
+
+ # Donation Found this bot useful? You can donate to support the development of this bot. diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py index 0bbe4ae9..3d028a25 100644 --- a/ytdlbot/sp_downloader.py +++ b/ytdlbot/sp_downloader.py @@ -27,21 +27,16 @@ def sp_dl(url: str, tempdir: str): """Specific link downloader""" domain = urlparse(url).hostname - if not any( - x in domain - for x in [ - "www.instagram.com", - "pixeldrain.com", - "mediafire.com", - ] - ): - return False - if "www.instagram.com" in domain: - return instagram(url, tempdir) - elif "pixeldrain.com" in domain: - return pixeldrain(url, tempdir) - elif "www.xasiat.com" in domain: - return xasiat(url, tempdir) + domain_to_function = { + "www.instagram.com": instagram, + "pixeldrain.com": pixeldrain, + "krakenfiles.com": krakenfiles, + } + for domain_key, function in domain_to_function.items(): + if domain_key in domain: + return function(url, tempdir) + + return False def sp_ytdl_download(url: str, tempdir: str): @@ -97,5 +92,26 @@ def pixeldrain(url: str, tempdir: str): return url -def xasiat(url: str, tempdir: str): - return False +def krakenfiles(url: str, tempdir: str): + resp = requests.get(url) + html = resp.content + soup = BeautifulSoup(html, 'html.parser') + link_parts = [] + token_parts = [] + for form_tag in soup.find_all('form'): + action = form_tag.get('action') + if action and 'krakenfiles.com' in action: + link_parts.append(action) + input_tag = form_tag.find('input', {'name': 'token'}) + if input_tag: + value = input_tag.get('value') + token_parts.append(value) + for link_part, token_part in zip(link_parts, token_parts): + link = f'https:{link_part}' + data = { + 'token': token_part + } + response = requests.post(link, data=data) + json_data = response.json() + url = json_data['url'] + return sp_ytdl_download(url, tempdir) From 575e94c7efb0b523651c0394ec527d2234971a06 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 22 Apr 2024 20:31:50 +0200 Subject: [PATCH 196/297] Revert "disable buy" This reverts commit aab8abd74c056305da1eab33ef97f21d8bf4f98c. --- ytdlbot/ytdl_bot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ab7504f3..f59f121e 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -306,8 +306,6 @@ def buy_handler(client: Client, message: types.Message): # process as chat.id, not from_user.id chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message.reply_text("Not available now.") - return # currency USD token_count = message.text.replace("/buy", "").strip() if token_count.isdigit(): From c5681d04d174fad8345407f96b60821fbd8813de Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 22 Apr 2024 20:32:34 +0200 Subject: [PATCH 197/297] bump --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2b80a826..7bebf4a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,13 +5,13 @@ APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 PyMySQL==1.1.0 -celery==5.3.6 +celery==5.4.0 filetype==1.2.0 flower==2.0.1 psutil==5.9.8 -influxdb==5.3.1 +influxdb==5.3.2 beautifulsoup4==4.12.3 -fakeredis==2.21.3 +fakeredis==2.22.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.3 From 90b911c0b9edef1308ade6cee8cedfc032916f29 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 3 May 2024 22:11:40 +0530 Subject: [PATCH 198/297] Rewrote spdl functionality to feel more integrated and add support for terabox.com links (#376) * rewrote spdl to feel more integrated and add support for terabox.com links * small typo fix and terabox link extraction improvement * another tweak to improve terabox link extraction --- README.md | 15 +++- ytdlbot/constant.py | 10 ++- ytdlbot/downloader.py | 5 -- ytdlbot/sp_downloader.py | 159 +++++++++++++++++++++++++++++++++------ ytdlbot/tasks.py | 66 ++++++++++++++++ ytdlbot/utils.py | 7 ++ ytdlbot/ytdl_bot.py | 17 +++++ 7 files changed, 246 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ed6dbfec..5ecda72b 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,13 @@ Just send a link directly to the bot. * YouTube ๐Ÿ˜… * Any websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) -* Instagram (Videos, Photos, Reels, IGTV & carousel) -* Pixeldrain -* KrakenFiles + + ### Specific link downloader (Use /spdl for these links) + * Instagram (Videos, Photos, Reels, IGTV & carousel) + * Pixeldrain + * KrakenFiles + * Terabox (file/~~folders~~) (you need to add cookies txt in ytdlbot folder with name) + [terabox.txt](https://github.com/ytdl-org/youtube-dl#how-do-i-pass-cookies-to-youtube-dl). # Features @@ -292,6 +296,7 @@ start - Let's start about - What's this bot? ping - Bot running status help - Help +spdl - Use to download specific link downloader links ytdl - Download video in group settings - Set your preference buy - Buy token @@ -337,6 +342,10 @@ https://pixeldrain.com/u/765ijw9i https://krakenfiles.com/view/oqmSTF0T5t/file.html +## Test TeraBox + +https://terabox.com/s/1mpgNshrZVl6KuH717Hs23Q +

diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py index 16773ca0..d412653d 100644 --- a/ytdlbot/constant.py +++ b/ytdlbot/constant.py @@ -29,10 +29,14 @@ class BotText: Join https://t.me/+OGRC8tp9-U9mZDZl for updates.""" - help = f""" -1. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. + help = """ +1. For YouTube and any websites supported by yt-dlp, just send the link and we will download and send it to you. -2. Source code: https://github.com/tgbot-collection/ytdlbot +2. For specific links use `/spdl {URL}`. More info at https://github.com/SanujaNS/ytdlbot-telegram#supported-websites + +3. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. + +4. Wanna deploy it yourself?\nHere's the source code: https://github.com/tgbot-collection/ytdlbot """ about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index db14de64..fd354ae4 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -27,8 +27,6 @@ from pyrogram import types from tqdm import tqdm -from sp_downloader import sp_dl - from config import ( AUDIO_FORMAT, ENABLE_ARIA2, @@ -222,9 +220,6 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: None, ] adjust_formats(chat_id, url, formats, hijack) - if sp_dl(url, tempdir): - return list(pathlib.Path(tempdir).glob("*")) - address = ["::", "0.0.0.0"] if IPv6 else [None] error = None video_paths = None diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py index 3d028a25..12f55c66 100644 --- a/ytdlbot/sp_downloader.py +++ b/ytdlbot/sp_downloader.py @@ -7,41 +7,83 @@ __author__ = "SanujaNS " +import functools +import os +import json import logging import pathlib import re import traceback -from urllib.parse import urlparse +from urllib.parse import urlparse, parse_qs +from pyrogram import types +from tqdm import tqdm import filetype import requests +from bs4 import BeautifulSoup import yt_dlp as ytdl from config import ( + PREMIUM_USER, + TG_NORMAL_MAX_SIZE, + TG_PREMIUM_MAX_SIZE, + FileTooBig, IPv6, ) - -user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.128 Safari/537.36" +from downloader import ( + edit_text, + remove_bash_color, + ProgressBar, + tqdm_progress, + download_hook, + upload_hook, +) +from limit import Payment +from utils import sizeof_fmt, parse_cookie_file -def sp_dl(url: str, tempdir: str): +def sp_dl(url: str, tempdir: str, bm, **kwargs) -> list: """Specific link downloader""" domain = urlparse(url).hostname - domain_to_function = { - "www.instagram.com": instagram, - "pixeldrain.com": pixeldrain, - "krakenfiles.com": krakenfiles, - } - for domain_key, function in domain_to_function.items(): - if domain_key in domain: - return function(url, tempdir) + if "youtube.com" in domain or "youtu.be" in domain: + raise ValueError("ERROR: This is ytdl bot for Youtube links just send the link.") + elif "www.instagram.com" in domain: + return instagram(url, tempdir, bm, **kwargs) + elif "pixeldrain.com" in domain: + return pixeldrain(url, tempdir, bm, **kwargs) + elif "krakenfiles.com" in domain: + return krakenfiles(url, tempdir, bm, **kwargs) + elif any( + x in domain + for x in [ + "terabox.com", + "nephobox.com", + "4funbox.com", + "mirrobox.com", + "momerybox.com", + "teraboxapp.com", + "1024tera.com", + "terabox.app", + "gibibox.com", + "goaibox.com", + ] + ): + return terabox(url, tempdir, bm, **kwargs) + else: + raise ValueError(f"Invalid URL: No specific link function found for {url}") - return False + return [] -def sp_ytdl_download(url: str, tempdir: str): - output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() +def sp_ytdl_download(url: str, tempdir: str, bm, filename=None, **kwargs) -> list: + payment = Payment() + chat_id = bm.chat.id + if filename: + output = pathlib.Path(tempdir, filename).as_posix() + else: + output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() ydl_opts = { + "progress_hooks": [lambda d: download_hook(d, bm)], "outtmpl": output, "restrictfilenames": False, "quiet": True, @@ -59,6 +101,8 @@ def sp_ytdl_download(url: str, tempdir: str): ydl.download([url]) video_paths = list(pathlib.Path(tempdir).glob("*")) break + except FileTooBig as e: + raise e except Exception: error = traceback.format_exc() logging.error("Download failed for %s - %s", url) @@ -69,8 +113,9 @@ def sp_ytdl_download(url: str, tempdir: str): return video_paths -def instagram(url: str, tempdir: str): +def instagram(url: str, tempdir: str, bm, **kwargs): resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() + video_paths = [] if url_results := resp.get("data"): for link in url_results: content = requests.get(link, stream=True).content @@ -78,21 +123,22 @@ def instagram(url: str, tempdir: str): save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") with open(save_path, "wb") as f: f.write(content) - - return True + video_paths.append(save_path) + + return video_paths -def pixeldrain(url: str, tempdir: str): +def pixeldrain(url: str, tempdir: str, bm, **kwargs): user_page_url_regex = r"https://pixeldrain.com/u/(\w+)" match = re.match(user_page_url_regex, url) if match: url = "https://pixeldrain.com/api/file/{}?download".format(match.group(1)) - return sp_ytdl_download(url, tempdir) + return sp_ytdl_download(url, tempdir, bm, **kwargs) else: return url -def krakenfiles(url: str, tempdir: str): +def krakenfiles(url: str, tempdir: str, bm, **kwargs): resp = requests.get(url) html = resp.content soup = BeautifulSoup(html, 'html.parser') @@ -114,4 +160,73 @@ def krakenfiles(url: str, tempdir: str): response = requests.post(link, data=data) json_data = response.json() url = json_data['url'] - return sp_ytdl_download(url, tempdir) + return sp_ytdl_download(url, tempdir, bm, **kwargs) + + +def find_between(s, start, end): + return (s.split(start))[1].split(end)[0] + +def terabox(url: str, tempdir: str, bm, **kwargs): + cookies_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'terabox.txt') + cookies = parse_cookie_file(cookies_file) + + headers = { + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'en-US,en;q=0.9,hi;q=0.8', + 'Connection': 'keep-alive', + 'DNT': '1', + 'Host': 'www.terabox.app', + 'Sec-Fetch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-Site': 'none', + 'Sec-Fetch-User': '?1', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + } + + session = requests.Session() + session.headers.update(headers) + session.cookies.update(cookies) + temp_req = session.get(url) + request_url = urlparse(temp_req.url) + surl = parse_qs(request_url.query).get('surl') + req = session.get(temp_req.url) + respo = req.text + js_token = find_between(respo, "fn%28%22", "%22%29") + logid = find_between(respo, "dp-logid=", "&") + bdstoken = find_between(respo, 'bdstoken":"', '"') + + params = { + 'app_id': '250528', + 'web': '1', + 'channel': 'dubox', + 'clienttype': '0', + 'jsToken': js_token, + 'dp-logid': logid, + 'page': '1', + 'num': '20', + 'by': 'name', + 'order': 'asc', + 'site_referer': temp_req.url, + 'shorturl': surl, + 'root': '1,', + } + + req2 = session.get('https://www.terabox.app/share/list', params=params) + response_data2 = req2.json() + file_name = response_data2['list'][0]['server_filename'] + sizebytes = int(response_data2['list'][0]['size']) + if sizebytes > 48 * 1024 * 1024: + direct_link = response_data2['list'][0]['dlink'] + url = direct_link.replace('d.terabox.app', 'd3.terabox.app') + else: + direct_link_response = session.head(response_data2['list'][0]['dlink']) + direct_link_response_headers = direct_link_response.headers + direct_link = direct_link_response_headers['Location'] + url = direct_link + + return sp_ytdl_download(url, tempdir, bm, filename=file_name, **kwargs) \ No newline at end of file diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 860129e0..b605a999 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -48,6 +48,7 @@ from constant import BotText from database import Redis, MySQL from downloader import edit_text, tqdm_progress, upload_hook, ytdl_download +from sp_downloader import sp_dl from limit import Payment from utils import ( apply_log_formatter, @@ -210,6 +211,38 @@ def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message direct_normal_download(client, bot_msg, url) +def spdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): + payment = Payment() + redis = Redis() + chat_id = bot_msg.chat.id + unique = get_unique_clink(url, chat_id) + cached_fid = redis.get_send_cache(unique) + + try: + if cached_fid: + forward_video(client, bot_msg, url, cached_fid) + redis.update_metrics("cache_hit") + return + redis.update_metrics("cache_miss") + mode = mode or payment.get_user_settings(chat_id)[3] + spdl_normal_download(client, bot_msg, url) + except FileTooBig as e: + logging.warning("Seeking for help from premium user...") + # this is only for normal node. Celery node will need to do it in celery tasks + markup = premium_button(chat_id) + if markup: + bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) + else: + bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") + except ValueError as e: + logging.error("Invalid URL provided: %s", e) + bot_msg.edit_text(f"Download failed!โŒ\n\n{e}", disable_web_page_preview=True) + except Exception as e: + logging.error("Failed to download %s, error: %s", url, e) + error_msg = "Sorry, Something went wrong." + bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg}", disable_web_page_preview=True) + + def audio_entrance(client: Client, bot_msg: types.Message): if ENABLE_CELERY: audio_task.delay(bot_msg.chat.id, bot_msg.id) @@ -321,6 +354,39 @@ def ytdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, ur temp_dir.cleanup() +def spdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, url: str): + chat_id = bot_msg.chat.id + temp_dir = tempfile.TemporaryDirectory(prefix="spdl-", dir=TMPFILE_PATH) + + video_paths = sp_dl(url, temp_dir.name, bot_msg) + logging.info("Download complete.") + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + bot_msg.edit_text("Download complete. Sending now...") + data = MySQL().get_user_settings(chat_id) + if data[4] == "ON": + logging.info("Adding to history...") + MySQL().add_history(chat_id, url, pathlib.Path(video_paths[0]).name) + try: + upload_processor(client, bot_msg, url, video_paths) + except pyrogram.errors.Flood as e: + logging.critical("FloodWait from Telegram: %s", e) + client.send_message( + chat_id, + f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.", + ) + client.send_message(OWNER, f"CRITICAL INFO: {e}") + time.sleep(e.value) + upload_processor(client, bot_msg, url, video_paths) + + bot_msg.edit_text("Download success!โœ…") + + if RCLONE_PATH: + for item in os.listdir(temp_dir.name): + logging.info("Copying %s to %s", item, RCLONE_PATH) + shutil.copy(os.path.join(temp_dir.name, item), RCLONE_PATH) + temp_dir.cleanup() + + def generate_input_media(file_paths: list, cap: str) -> list: input_media = [] for path in file_paths: diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 29a09c58..38de9618 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -21,6 +21,7 @@ import coloredlogs import ffmpeg import psutil +from http.cookiejar import MozillaCookieJar from config import TMPFILE_PATH from flower_tasks import app @@ -219,5 +220,11 @@ def clean_tempfile(): shutil.rmtree(item, ignore_errors=True) +def parse_cookie_file(cookiefile): + jar = MozillaCookieJar(cookiefile) + jar.load() + return {cookie.name: cookie.value for cookie in jar} + + if __name__ == "__main__": auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index f59f121e..680d1c05 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -56,6 +56,7 @@ hot_patch, purge_tasks, ytdl_download_entrance, + spdl_download_entrance, ) from utils import auto_restart, clean_tempfile, customize_logger, get_revision @@ -259,6 +260,22 @@ def direct_handler(client: Client, message: types.Message): redis.update_metrics("direct_request") direct_download_entrance(client, bot_msg, url) +@app.on_message(filters.command(["spdl"])) +def spdl_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + url = re.sub(r"/spdl\s*", "", message.text) + logging.info("spdl start %s", url) + if not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("direct_request") + spdl_download_entrance(client, bot_msg, url) + @app.on_message(filters.command(["settings"])) def settings_handler(client: Client, message: types.Message): From 6d5894a71b5be6ca0e12be1f134950fedfde80be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 18:42:41 +0200 Subject: [PATCH 199/297] Bump tqdm from 4.66.2 to 4.66.4 (#378) Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.2 to 4.66.4. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.66.2...v4.66.4) --- updated-dependencies: - dependency-name: tqdm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7bebf4a5..dd4edf7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.3 requests==2.31.0 -tqdm==4.66.2 +tqdm==4.66.4 requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 From b30b751dbf83f45c2c4ae5be8226a3fd9d2d0d8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 18:42:48 +0200 Subject: [PATCH 200/297] Bump blinker from 1.7.0 to 1.8.1 (#377) Bumps [blinker](https://github.com/pallets-eco/blinker) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/pallets-eco/blinker/releases) - [Changelog](https://github.com/pallets-eco/blinker/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets-eco/blinker/compare/1.7.0...1.8.1) --- updated-dependencies: - dependency-name: blinker dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dd4edf7a..685cb62d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,5 +25,5 @@ coloredlogs==15.0.1 tronpy==0.5.0 mnemonic==0.21 qrcode==7.4.2 -blinker==1.7.0 +blinker==1.8.1 flask===3.0.3 From 80ed7353c40e8f5ba901d3351440f4117ad6c41f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 18:42:55 +0200 Subject: [PATCH 201/297] Bump redis from 5.0.3 to 5.0.4 (#375) Bumps [redis](https://github.com/redis/redis-py) from 5.0.3 to 5.0.4. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.0.3...v5.0.4) --- updated-dependencies: - dependency-name: redis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 685cb62d..aa6cdb59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ beautifulsoup4==4.12.3 fakeredis==2.22.0 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==5.0.3 +redis==5.0.4 requests==2.31.0 tqdm==4.66.4 requests-toolbelt==1.0.0 From b9612487bf02bc59e20a5abceb6aa08568a8a8ba Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 28 May 2024 23:28:10 +0530 Subject: [PATCH 202/297] Update sp_downloader.py: fix progress bar for Instagram downloads (#379) * rewrote spdl to feel more integrated and add support for terabox.com links * small typo fix and terabox link extraction improvement * another tweak to improve terabox link extraction * Update sp_downloader.py Add progress bar to spdl Instagram downloads * improvement on instagram download to show filename while downloading * reformat --- ytdlbot/sp_downloader.py | 116 +++++++++++++++++++++------------------ ytdlbot/utils.py | 16 ++++++ 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py index 12f55c66..18355549 100644 --- a/ytdlbot/sp_downloader.py +++ b/ytdlbot/sp_downloader.py @@ -39,7 +39,7 @@ upload_hook, ) from limit import Payment -from utils import sizeof_fmt, parse_cookie_file +from utils import sizeof_fmt, parse_cookie_file, extract_code_from_instagram_url def sp_dl(url: str, tempdir: str, bm, **kwargs) -> list: @@ -115,16 +115,28 @@ def sp_ytdl_download(url: str, tempdir: str, bm, filename=None, **kwargs) -> lis def instagram(url: str, tempdir: str, bm, **kwargs): resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() + code = extract_code_from_instagram_url(url) + counter = 1 video_paths = [] if url_results := resp.get("data"): for link in url_results: - content = requests.get(link, stream=True).content + req = requests.get(link, stream=True) + length = int(req.headers.get("content-length")) + content = req.content ext = filetype.guess_extension(content) - save_path = pathlib.Path(tempdir, f"{id(link)}.{ext}") - with open(save_path, "wb") as f: - f.write(content) + filename = f"{code}_{counter}.{ext}" + save_path = pathlib.Path(tempdir, filename) + chunk_size = 4096 + downloaded = 0 + for chunk in req.iter_content(chunk_size): + text = tqdm_progress(f"Downloading: {filename}", length, downloaded) + edit_text(bm, text) + with open(save_path, "ab") as fp: + fp.write(chunk) + downloaded += len(chunk) video_paths.append(save_path) - + counter += 1 + return video_paths @@ -141,25 +153,25 @@ def pixeldrain(url: str, tempdir: str, bm, **kwargs): def krakenfiles(url: str, tempdir: str, bm, **kwargs): resp = requests.get(url) html = resp.content - soup = BeautifulSoup(html, 'html.parser') + soup = BeautifulSoup(html, "html.parser") link_parts = [] token_parts = [] - for form_tag in soup.find_all('form'): - action = form_tag.get('action') - if action and 'krakenfiles.com' in action: + for form_tag in soup.find_all("form"): + action = form_tag.get("action") + if action and "krakenfiles.com" in action: link_parts.append(action) - input_tag = form_tag.find('input', {'name': 'token'}) + input_tag = form_tag.find("input", {"name": "token"}) if input_tag: - value = input_tag.get('value') + value = input_tag.get("value") token_parts.append(value) for link_part, token_part in zip(link_parts, token_parts): - link = f'https:{link_part}' + link = f"https:{link_part}" data = { - 'token': token_part + "token": token_part } response = requests.post(link, data=data) json_data = response.json() - url = json_data['url'] + url = json_data["url"] return sp_ytdl_download(url, tempdir, bm, **kwargs) @@ -167,25 +179,25 @@ def find_between(s, start, end): return (s.split(start))[1].split(end)[0] def terabox(url: str, tempdir: str, bm, **kwargs): - cookies_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'terabox.txt') + cookies_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "terabox.txt") cookies = parse_cookie_file(cookies_file) headers = { - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', - 'Accept-Encoding': 'gzip, deflate, br', - 'Accept-Language': 'en-US,en;q=0.9,hi;q=0.8', - 'Connection': 'keep-alive', - 'DNT': '1', - 'Host': 'www.terabox.app', - 'Sec-Fetch-Dest': 'document', - 'Sec-Fetch-Mode': 'navigate', - 'Sec-Fetch-Site': 'none', - 'Sec-Fetch-User': '?1', - 'Upgrade-Insecure-Requests': '1', - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', - 'sec-ch-ua': '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "en-US,en;q=0.9,hi;q=0.8", + "Connection": "keep-alive", + "DNT": "1", + "Host": "www.terabox.app", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + "sec-ch-ua": "'Not A(Brand';v='99', 'Google Chrome';v='121', 'Chromium';v='121'", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "'Windows'", } session = requests.Session() @@ -193,7 +205,7 @@ def terabox(url: str, tempdir: str, bm, **kwargs): session.cookies.update(cookies) temp_req = session.get(url) request_url = urlparse(temp_req.url) - surl = parse_qs(request_url.query).get('surl') + surl = parse_qs(request_url.query).get("surl") req = session.get(temp_req.url) respo = req.text js_token = find_between(respo, "fn%28%22", "%22%29") @@ -201,32 +213,32 @@ def terabox(url: str, tempdir: str, bm, **kwargs): bdstoken = find_between(respo, 'bdstoken":"', '"') params = { - 'app_id': '250528', - 'web': '1', - 'channel': 'dubox', - 'clienttype': '0', - 'jsToken': js_token, - 'dp-logid': logid, - 'page': '1', - 'num': '20', - 'by': 'name', - 'order': 'asc', - 'site_referer': temp_req.url, - 'shorturl': surl, - 'root': '1,', + "app_id": "250528", + "web": "1", + "channel": "dubox", + "clienttype": "0", + "jsToken": js_token, + "dp-logid": logid, + "page": "1", + "num": "20", + "by": "name", + "order": "asc", + "site_referer": temp_req.url, + "shorturl": surl, + "root": "1,", } - req2 = session.get('https://www.terabox.app/share/list', params=params) + req2 = session.get("https://www.terabox.app/share/list", params=params) response_data2 = req2.json() - file_name = response_data2['list'][0]['server_filename'] - sizebytes = int(response_data2['list'][0]['size']) + file_name = response_data2["list"][0]["server_filename"] + sizebytes = int(response_data2["list"][0]["size"]) if sizebytes > 48 * 1024 * 1024: - direct_link = response_data2['list'][0]['dlink'] - url = direct_link.replace('d.terabox.app', 'd3.terabox.app') + direct_link = response_data2["list"][0]["dlink"] + url = direct_link.replace("d.terabox.app", "d3.terabox.app") else: - direct_link_response = session.head(response_data2['list'][0]['dlink']) + direct_link_response = session.head(response_data2["list"][0]["dlink"]) direct_link_response_headers = direct_link_response.headers - direct_link = direct_link_response_headers['Location'] + direct_link = direct_link_response_headers["Location"] url = direct_link return sp_ytdl_download(url, tempdir, bm, filename=file_name, **kwargs) \ No newline at end of file diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 38de9618..d4995256 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -17,6 +17,7 @@ import tempfile import time import uuid +import re import coloredlogs import ffmpeg @@ -226,5 +227,20 @@ def parse_cookie_file(cookiefile): return {cookie.name: cookie.value for cookie in jar} +def extract_code_from_instagram_url(url): + # Regular expression patterns + patterns = [ + r"/p/([a-zA-Z0-9_-]+)/", # Posts + r"/reel/([a-zA-Z0-9_-]+)/" # Reels + ] + + for pattern in patterns: + match = re.search(pattern, url) + if match: + return match.group(1) + + return None + + if __name__ == "__main__": auto_restart() From 2e458231e9aab4b797c2da4b3c01c0aeb73d7159 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 12:31:11 +0200 Subject: [PATCH 203/297] Bump yt-dlp from 2024.04.9 to 2024.5.27 (#390) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.04.9 to 2024.5.27. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.04.09...2024.05.27) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aa6cdb59..fc7956bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.04.9 +yt-dlp==2024.5.27 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From d1e702f111592bf7a2729bb789060bdb3a1009b9 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 20 Jun 2024 00:59:15 +0530 Subject: [PATCH 204/297] Fix `ping` command by separating detailed output into a new `stats` command; `ping` now displays only the ping result. (#398) * rewrote spdl to feel more integrated and add support for terabox.com links * small typo fix and terabox link extraction improvement * another tweak to improve terabox link extraction * Update sp_downloader.py Add progress bar to spdl Instagram downloads * improvement on instagram download to show filename while downloading * reformat * Fix `ping` command by separating detailed output into a new `stats` command --- README.md | 5 +++-- ytdlbot/ytdl_bot.py | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ecda72b..6f349da4 100644 --- a/README.md +++ b/README.md @@ -294,18 +294,19 @@ refer guide here [kubernetes](k8s.md) ``` start - Let's start about - What's this bot? -ping - Bot running status help - Help spdl - Use to download specific link downloader links ytdl - Download video in group +direct - Download file directly settings - Set your preference buy - Buy token -direct - Download file directly sub - Subscribe to YouTube Channel unsub - Unsubscribe from YouTube Channel sub_count - Check subscription status, owner only. uncache - Delete cache for this link, owner only. purge - Delete all tasks, owner only. +ping - Ping the Bot +stats - Bot running status show_history - Show download history clear_history - Clear download history ``` diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 680d1c05..c9878c26 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -11,6 +11,7 @@ import json import logging import os +import threading import random import re import tempfile @@ -201,11 +202,34 @@ def purge_handler(client: Client, message: types.Message): @app.on_message(filters.command(["ping"])) def ping_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.chat.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_sent = False + def send_message_and_measure_ping(): + start_time = int(round(time.time() * 1000)) + reply = client.send_message(chat_id, "Starting Ping...") + end_time = int(round(time.time() * 1000)) + ping_time = int(round(end_time - start_time)) + message_sent = True + if message_sent: + message.reply_text(f"Ping: {ping_time:.2f} ms", quote=True) + time.sleep(0.5) + client.edit_message_text(chat_id=reply.chat.id, message_id=reply.id, text="Ping Calculation Complete.") + time.sleep(1) + client.delete_messages(chat_id=reply.chat.id, message_ids=reply.id) + + thread = threading.Thread(target=send_message_and_measure_ping) + thread.start() + + +@app.on_message(filters.command(["stats"])) +def stats_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): - bot_info = "ping unavailable." + bot_info = "Stats Unavailable." else: bot_info = get_runtime("ytdlbot_ytdl_1", "YouTube-dl") if message.chat.username == OWNER: From 18d807aaac536dd51efcc474f18f2ed535bc21aa Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 30 Jun 2024 13:51:31 +0530 Subject: [PATCH 205/297] Shortening URL in the caption if it's too long and some cleanup (#400) * remove unnecessary line * shortening URL in the caption if it's too long * Revert "shortening URL in the caption if it's too long" This reverts commit 6feb473dcfcf7ce511462883605a2d33814e0af0. * shortening URL in the caption if it's too long * section for advance users --- ytdlbot/config.py | 28 +++++++++++++--------------- ytdlbot/tasks.py | 14 +++++++++++++- ytdlbot/utils.py | 7 +++++++ ytdlbot/ytdl_bot.py | 1 - 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/ytdlbot/config.py b/ytdlbot/config.py index b9e62376..6bec4bb2 100644 --- a/ytdlbot/config.py +++ b/ytdlbot/config.py @@ -20,13 +20,6 @@ REDIS = os.getenv("REDIS", "redis") -TG_PREMIUM_MAX_SIZE = 4000 * 1024 * 1024 -TG_NORMAL_MAX_SIZE = 2000 * 1024 * 1024 -# TG_NORMAL_MAX_SIZE = 10 * 1024 * 1024 - - -EXPIRE = 24 * 3600 - ENABLE_VIP = os.getenv("VIP", False) OWNER = os.getenv("OWNER", "BennyThink") @@ -44,23 +37,16 @@ MYSQL_USER = os.getenv("MYSQL_USER", "root") MYSQL_PASS = os.getenv("MYSQL_PASS", "root") -AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") ARCHIVE_ID = os.getenv("ARCHIVE_ID") -IPv6 = os.getenv("IPv6", False) ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) +AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) -RATE_LIMIT = os.getenv("RATE_LIMIT", 120) RCLONE_PATH = os.getenv("RCLONE") -# This will set the value for the tmpfile path(download path) if it is set. -# If TMPFILE is not set, it will return None and use systemโ€™s default temporary file path. -# Please ensure that the directory exists and you have necessary permissions to write to it. -# If you don't know what this is just leave it as it is. -TMPFILE_PATH = os.getenv("TMPFILE") # payment settings AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") @@ -70,6 +56,7 @@ AFD_USER_ID = os.getenv("AFD_USER_ID") PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 10) +EXPIRE = 24 * 3600 TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits TRONGRID_KEY = os.getenv("TRONGRID_KEY", "").split(",") # the default mnemonic is for nile testnet @@ -78,6 +65,17 @@ PREMIUM_USER = int(os.getenv("PREMIUM_USER", "0")) +# For advance users +# Please do not change, if you don't know what these are. +TG_PREMIUM_MAX_SIZE = 4000 * 1024 * 1024 +TG_NORMAL_MAX_SIZE = 2000 * 1024 * 1024 +CAPTION_URL_LENGTH_LIMIT = 150 +IPv6 = os.getenv("IPv6", False) +RATE_LIMIT = os.getenv("RATE_LIMIT", 120) +# This will set the value for the tmpfile path(download path). If not, will return None and use systemโ€™s default path. +# Please ensure that the directory exists and you have necessary permissions to write to it. +TMPFILE_PATH = os.getenv("TMPFILE_PATH") + class FileTooBig(Exception): pass diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index b605a999..070d19ec 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -44,6 +44,7 @@ TMPFILE_PATH, WORKERS, FileTooBig, + CAPTION_URL_LENGTH_LIMIT, ) from constant import BotText from database import Redis, MySQL @@ -57,6 +58,7 @@ get_metadata, get_revision, sizeof_fmt, + shorten_url, ) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) @@ -544,8 +546,18 @@ def gen_cap(bm, url, video_path): worker = f"Downloaded by {worker_name}" else: worker = "" + # Shorten the URL if necessary + try: + if len(url) > CAPTION_URL_LENGTH_LIMIT: + url_for_cap = shorten_url(url, CAPTION_URL_LENGTH_LIMIT) + else: + url_for_cap = url + except Exception as e: + logging.warning(f"Error shortening URL: {e}") + url_for_cap = url + cap = ( - f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" + f"{user_info}\n{file_name}\n\n{url_for_cap}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" ) return cap, meta diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index d4995256..6ebe2287 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -242,5 +242,12 @@ def extract_code_from_instagram_url(url): return None +def shorten_url(url, CAPTION_URL_LENGTH_LIMIT): + #Shortens a URL by cutting it to a specified length. + shortened_url = url[:CAPTION_URL_LENGTH_LIMIT - 3] + "..." + + return shortened_url + + if __name__ == "__main__": auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index c9878c26..4cd44c35 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -202,7 +202,6 @@ def purge_handler(client: Client, message: types.Message): @app.on_message(filters.command(["ping"])) def ping_handler(client: Client, message: types.Message): - redis = Redis() chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) message_sent = False From 1490970541520c6ab08a85814e7f832db1e0166d Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 3 Jul 2024 11:38:06 +0530 Subject: [PATCH 206/297] Bump yt-dlp from 2024.5.27 to 2024.7.2 (#402) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fc7956bc..b519e955 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.5.27 +yt-dlp==2024.7.2 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 0964958bed9f432404a9c7d3d22f78846923c225 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:42:32 +0530 Subject: [PATCH 207/297] Bump requests from 2.31.0 to 2.32.3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b519e955..705f7448 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ fakeredis==2.22.0 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.4 -requests==2.31.0 +requests==2.32.3 tqdm==4.66.4 requests-toolbelt==1.0.0 ffpb==0.4.1 From 4159905615365090876e0f2131d4f91563b1f9b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:45:03 +0200 Subject: [PATCH 208/297] Bump yt-dlp from 2024.7.2 to 2024.7.7 (#405) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.7.2 to 2024.7.7. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.07.02...2024.07.07) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 705f7448..5dc42566 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.7.2 +yt-dlp==2024.7.7 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From edc8d5b9ff4b6cb858b0175a3a081710a350cb17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:56:54 +0200 Subject: [PATCH 209/297] Bump yt-dlp from 2024.7.7 to 2024.7.9 (#406) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.7.7 to 2024.7.9. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.07.07...2024.07.09) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5dc42566..8d133a7c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.7.7 +yt-dlp==2024.7.9 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 78972c55b2bfd7d68cf99f8c8aa3c727a69111b1 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 17 Jul 2024 22:50:35 +0530 Subject: [PATCH 210/297] leech option for direct download using aria2 (#408) * leech option for direct download using aria2 * Improved and fixed some bugs of leech function --- README.md | 4 ++- ytdlbot/tasks.py | 85 +++++++++++++++++++++++++++++++++++++++++---- ytdlbot/utils.py | 17 +++++++++ ytdlbot/ytdl_bot.py | 37 ++++++++++++++++---- 4 files changed, 129 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6f349da4..a7c2760e 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Just send a link directly to the bot. 12. instagram posts(only available for my bot) 13. 4 GiB file size support with Telegram Premium 14. History and inline mode support +15. Supports multiple download engines (yt-dlp, aria2, requests). > [!NOTE] > **For users of [my official bot](https://t.me/benny_ytdlbot)**\ @@ -297,7 +298,8 @@ about - What's this bot? help - Help spdl - Use to download specific link downloader links ytdl - Download video in group -direct - Download file directly +leech - Download file using aria2 +direct - Download file using requests settings - Set your preference buy - Buy token sub - Subscribe to YouTube Channel diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 070d19ec..4e0b192a 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -59,6 +59,7 @@ get_revision, sizeof_fmt, shorten_url, + extract_filename, ) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) @@ -145,6 +146,14 @@ def direct_download_task(chat_id: int, message_id: int, url: str): logging.info("Direct download celery tasks ended.") +@app.task() +def leech_download_task(chat_id: int, message_id: int, url: str): + logging.info("Leech download celery tasks started for %s", url) + bot_msg = retrieve_message(chat_id, message_id) + leech_normal_download(bot, bot_msg, url) + logging.info("Leech download celery tasks ended.") + + def get_unique_clink(original_url: str, user_id: int): payment = Payment() settings = payment.get_user_settings(user_id) @@ -213,6 +222,14 @@ def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message direct_normal_download(client, bot_msg, url) +def leech_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): + if ENABLE_CELERY: + leech_normal_download(client, bot_msg, url) + # leech_normal_download.delay(bot_msg.chat.id, bot_msg.id, url) + else: + leech_normal_download(client, bot_msg, url) + + def spdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): payment = Payment() redis = Redis() @@ -255,7 +272,7 @@ def audio_entrance(client: Client, bot_msg: types.Message): def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): chat_id = bot_msg.chat.id headers = { - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.3987.149 Safari/537.36" + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" } length = 0 @@ -263,15 +280,11 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, try: req = requests.get(url, headers=headers, stream=True) length = int(req.headers.get("content-length")) - filename = re.findall("filename=(.+)", req.headers.get("content-disposition"))[0] - except TypeError: - filename = getattr(req, "url", "").rsplit("/")[-1] except Exception as e: bot_msg.edit_text(f"Download failed!โŒ\n\n```{e}```", disable_web_page_preview=True) return - if not filename: - filename = quote_plus(url) + filename = extract_filename(req) with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as f: filepath = f"{f}/{filename}" @@ -297,6 +310,66 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, bot_msg.edit_text("Download success!โœ…") +def leech_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): + chat_id = bot_msg.chat.id + temp_dir = tempfile.TemporaryDirectory(prefix="leech_dl-", dir=TMPFILE_PATH) + tempdir = temp_dir.name + UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + response = None + video_paths = None + # Download process using aria2c + try: + bot_msg.edit_text(f"Download Starting...", disable_web_page_preview=True) + # Command to download the link using aria2c + command = [ + "aria2c", + "-U", + UA, + "--max-tries=5", + "--console-log-level=warn", + "-d", + tempdir, + url, + ] + # Run the command using subprocess.Popen + process = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + line = "" + max_iterations = 100 # Set a reasonable maximum number of iterations + iteration = 0 + + while process.poll() is None and iteration < max_iterations: + line = process.stdout.readline().decode("utf-8") + if line.startswith("[#"): + line = line.strip() + bot_msg.edit_text(f"Downloading... \n\n`{line}`", disable_web_page_preview=True) + break + iteration += 1 + + if iteration >= max_iterations: + bot_msg.edit_text("Something went wrong. Please try again.", disable_web_page_preview=True) + except Exception as e: + bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`", disable_web_page_preview=True) + return + # Get filename and extension correctly after download + filepath = list(pathlib.Path(tempdir).glob("*")) + file_path_obj = filepath[0] + path_obj = pathlib.Path(file_path_obj) + filename = path_obj.name + logging.info("Downloaded file %s", filename) + bot_msg.edit_text(f"Download Complete", disable_web_page_preview=True) + ext = filetype.guess_extension(file_path_obj) + # Rename file if it doesn't have extension + if ext is not None: + if not filename.endswith(ext): + new_filename = f"{tempdir}/{filename}.{ext}" + os.rename(file_path_obj, new_filename) + # Get file path of the downloaded file to upload + video_paths = list(pathlib.Path(tempdir).glob("*")) + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + upload_processor(client, bot_msg, url, video_paths) + bot_msg.edit_text("Download success!โœ…") + + def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]): chat_id = bot_msg.chat.id # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 6ebe2287..00d126b3 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -22,6 +22,7 @@ import coloredlogs import ffmpeg import psutil +from urllib.parse import quote_plus from http.cookiejar import MozillaCookieJar from config import TMPFILE_PATH @@ -249,5 +250,21 @@ def shorten_url(url, CAPTION_URL_LENGTH_LIMIT): return shortened_url +def extract_filename(response): + try: + content_disposition = response.headers.get("content-disposition") + if content_disposition: + filename = re.findall("filename=(.+)", content_disposition)[0] + return filename + except (TypeError, IndexError): + pass # Handle potential exceptions during extraction + + # Fallback if Content-Disposition header is missing + filename = response.url.rsplit("/")[-1] + if not filename: + filename = quote_plus(response.url) + return filename + + if __name__ == "__main__": auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 4cd44c35..c64165f7 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -46,6 +46,7 @@ REQUIRED_MEMBERSHIP, TOKEN_PRICE, TRX_SIGNAL, + ENABLE_ARIA2, ) from constant import BotText from database import InfluxDB, MySQL, Redis @@ -54,6 +55,7 @@ from tasks import ( audio_entrance, direct_download_entrance, + leech_download_entrance, hot_patch, purge_tasks, ytdl_download_entrance, @@ -267,6 +269,23 @@ def clear_history(client: Client, message: types.Message): message.reply_text("History cleared.", quote=True) +@app.on_message(filters.command(["spdl"])) +def spdl_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + url = re.sub(r"/spdl\s*", "", message.text) + logging.info("spdl start %s", url) + if not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("spdl_request") + spdl_download_entrance(client, bot_msg, url) + + @app.on_message(filters.command(["direct"])) def direct_handler(client: Client, message: types.Message): redis = Redis() @@ -283,21 +302,25 @@ def direct_handler(client: Client, message: types.Message): redis.update_metrics("direct_request") direct_download_entrance(client, bot_msg, url) -@app.on_message(filters.command(["spdl"])) -def spdl_handler(client: Client, message: types.Message): + +@app.on_message(filters.command(["leech"])) +def leech_handler(client: Client, message: types.Message): + if not ENABLE_ARIA2: + message.reply_text("Aria2 Not Enabled.", quote=True) + return redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - url = re.sub(r"/spdl\s*", "", message.text) - logging.info("spdl start %s", url) + url = re.sub(r"/leech\s*", "", message.text) + logging.info("leech using aria2 start %s", url) if not re.findall(r"^https?://", url.lower()): redis.update_metrics("bad_request") - message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + message.reply_text("Send me a correct LINK.", quote=True) return bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("direct_request") - spdl_download_entrance(client, bot_msg, url) + redis.update_metrics("leech_request") + leech_download_entrance(client, bot_msg, url) @app.on_message(filters.command(["settings"])) From 32be202a4b712a6cf5ec481d71f5a8b53f88e624 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:14:17 +0530 Subject: [PATCH 211/297] improve /direct command (#410) --- ytdlbot/tasks.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 4e0b192a..1554ad6b 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -298,6 +298,12 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, downloaded += len(chunk) logging.info("Downloaded file %s", filename) st_size = os.stat(filepath).st_size + ext = filetype.guess_extension(filepath) + # Rename file if it doesn't have extension + if ext is not None and not filepath.endswith(ext): + new_filename = f"{filepath}.{ext}" + os.rename(filepath, new_filename) + filepath = new_filename client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) client.send_document( @@ -359,10 +365,9 @@ def leech_normal_download(client: Client, bot_msg: typing.Union[types.Message, t bot_msg.edit_text(f"Download Complete", disable_web_page_preview=True) ext = filetype.guess_extension(file_path_obj) # Rename file if it doesn't have extension - if ext is not None: - if not filename.endswith(ext): - new_filename = f"{tempdir}/{filename}.{ext}" - os.rename(file_path_obj, new_filename) + if ext is not None and not filename.endswith(ext): + new_filename = f"{tempdir}/{filename}.{ext}" + os.rename(file_path_obj, new_filename) # Get file path of the downloaded file to upload video_paths = list(pathlib.Path(tempdir).glob("*")) client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) From 9cfdf0a4ee1fc0fbc3ddb8117f5913086a212b95 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 3 Aug 2024 13:45:49 +0530 Subject: [PATCH 212/297] Bump yt-dlp from 2024.7.9 to 2024.8.1 (#415) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8d133a7c..7a8219aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.7.9 +yt-dlp==2024.8.1 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From c8c8978740dc1385c4e21b06682c4d066066e5c2 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 3 Aug 2024 14:13:34 +0200 Subject: [PATCH 213/297] send as file regardless of streamable #412 --- ytdlbot/downloader.py | 2 +- ytdlbot/utils.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index fd354ae4..c368736e 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -22,7 +22,6 @@ import ffmpeg import ffpb import filetype -import requests import yt_dlp as ytdl from pyrogram import types from tqdm import tqdm @@ -219,6 +218,7 @@ def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", None, ] + # This method will alter formats if necessary adjust_formats(chat_id, url, formats, hijack) address = ["::", "0.0.0.0"] if IPv6 else [None] error = None diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 00d126b3..bd30ca43 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -12,18 +12,18 @@ import logging import os import pathlib +import re import shutil import subprocess import tempfile import time import uuid -import re +from http.cookiejar import MozillaCookieJar +from urllib.parse import quote_plus import coloredlogs import ffmpeg import psutil -from urllib.parse import quote_plus -from http.cookiejar import MozillaCookieJar from config import TMPFILE_PATH from flower_tasks import app @@ -77,6 +77,9 @@ def adjust_formats(user_id: int, url: str, formats: list, hijack=None): if settings[2] == "audio": formats.insert(0, "bestaudio[ext=m4a]") + if settings[2] == "document": + formats.clear() + def get_metadata(video_path): width, height, duration = 1280, 720, 0 @@ -230,24 +233,21 @@ def parse_cookie_file(cookiefile): def extract_code_from_instagram_url(url): # Regular expression patterns - patterns = [ - r"/p/([a-zA-Z0-9_-]+)/", # Posts - r"/reel/([a-zA-Z0-9_-]+)/" # Reels - ] - + patterns = [r"/p/([a-zA-Z0-9_-]+)/", r"/reel/([a-zA-Z0-9_-]+)/"] # Posts # Reels + for pattern in patterns: match = re.search(pattern, url) if match: return match.group(1) - + return None def shorten_url(url, CAPTION_URL_LENGTH_LIMIT): - #Shortens a URL by cutting it to a specified length. - shortened_url = url[:CAPTION_URL_LENGTH_LIMIT - 3] + "..." + # Shortens a URL by cutting it to a specified length. + shortened_url = url[: CAPTION_URL_LENGTH_LIMIT - 3] + "..." - return shortened_url + return shortened_url def extract_filename(response): From 03e3010ad3ae85e8b5a7655fca9f40ee2a4b7f03 Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 3 Aug 2024 14:15:08 +0200 Subject: [PATCH 214/297] bump everything --- requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7a8219aa..f4f7df89 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,17 +4,17 @@ yt-dlp==2024.8.1 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 -PyMySQL==1.1.0 +PyMySQL==1.1.1 celery==5.4.0 filetype==1.2.0 flower==2.0.1 -psutil==5.9.8 +psutil==6.0.0 influxdb==5.3.2 beautifulsoup4==4.12.3 -fakeredis==2.22.0 +fakeredis==2.23.4 supervisor==4.2.5 tgbot-ping==1.0.7 -redis==5.0.4 +redis==5.0.8 requests==2.32.3 tqdm==4.66.4 requests-toolbelt==1.0.0 @@ -25,5 +25,5 @@ coloredlogs==15.0.1 tronpy==0.5.0 mnemonic==0.21 qrcode==7.4.2 -blinker==1.8.1 +blinker==1.8.2 flask===3.0.3 From c39b0207b0b3e017caa9d05b00d50353e01cdd2a Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 4 Aug 2024 10:35:55 +0200 Subject: [PATCH 215/297] fix download hook --- ytdlbot/downloader.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py index c368736e..6844d5d6 100644 --- a/ytdlbot/downloader.py +++ b/ytdlbot/downloader.py @@ -114,12 +114,6 @@ def remove_bash_color(text): def download_hook(d: dict, bot_msg): - # since we're using celery, server location may be located in different region. - # Therefore, we can't trigger the hook very often. - # the key is user_id + download_link - original_url = d["info_dict"]["original_url"] - key = f"{bot_msg.chat.id}-{original_url}" - if d["status"] == "downloading": downloaded = d.get("downloaded_bytes", 0) total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) @@ -136,6 +130,7 @@ def download_hook(d: dict, bot_msg): speed = remove_bash_color(d.get("_speed_str", "N/A")) eta = remove_bash_color(d.get("_eta_str", d.get("eta"))) text = tqdm_progress("Downloading...", total, downloaded, speed, eta) + # debounce in here edit_text(bot_msg, text) From f07d522ebb2b3af33e8e8f1371f94d177a25bec3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:07:16 +0200 Subject: [PATCH 216/297] Bump yt-dlp from 2024.8.1 to 2024.8.6 (#418) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.8.1 to 2024.8.6. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.08.01...2024.08.06) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f4f7df89..1edc9760 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.8.1 +yt-dlp==2024.8.6 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 1f3871d013aefa4ac4dce1df29589d12c636da5e Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 6 Aug 2024 19:17:35 +0200 Subject: [PATCH 217/297] fix download as file #412 --- ytdlbot/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index bd30ca43..1d74c006 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -78,7 +78,7 @@ def adjust_formats(user_id: int, url: str, formats: list, hijack=None): formats.insert(0, "bestaudio[ext=m4a]") if settings[2] == "document": - formats.clear() + formats.insert(0, None) def get_metadata(video_path): From 0881eee131bd1702db1568ca651ffd35cc1ae66c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:26:35 +0200 Subject: [PATCH 218/297] Bump fakeredis from 2.23.4 to 2.24.1 (#421) Bumps [fakeredis](https://github.com/cunla/fakeredis-py) from 2.23.4 to 2.24.1. - [Release notes](https://github.com/cunla/fakeredis-py/releases) - [Commits](https://github.com/cunla/fakeredis-py/compare/v2.23.4...v2.24.1) --- updated-dependencies: - dependency-name: fakeredis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1edc9760..f414a964 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ flower==2.0.1 psutil==6.0.0 influxdb==5.3.2 beautifulsoup4==4.12.3 -fakeredis==2.23.4 +fakeredis==2.24.1 supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.8 From 2c4cb0353e2bac2aa997885ff0d2a82fee5db393 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:26:44 +0200 Subject: [PATCH 219/297] Bump tqdm from 4.66.4 to 4.66.5 (#416) Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.4 to 4.66.5. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.66.4...v4.66.5) --- updated-dependencies: - dependency-name: tqdm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f414a964..86a856d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ supervisor==4.2.5 tgbot-ping==1.0.7 redis==5.0.8 requests==2.32.3 -tqdm==4.66.4 +tqdm==4.66.5 requests-toolbelt==1.0.0 ffpb==0.4.1 youtube-search-python==1.6.6 From 07632b1e68c2236ed53242efdf946692b6fe5075 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:57:16 +0530 Subject: [PATCH 220/297] Rename option for `/direct` command (#423) --- ytdlbot/tasks.py | 13 ++++++++----- ytdlbot/ytdl_bot.py | 5 ++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 1554ad6b..7db8c73c 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -214,12 +214,12 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) -def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): +def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, custom_filename): if ENABLE_CELERY: - direct_normal_download(client, bot_msg, url) + direct_normal_download(client, bot_msg, url, custom_filename) # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) else: - direct_normal_download(client, bot_msg, url) + direct_normal_download(client, bot_msg, url, custom_filename) def leech_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): @@ -269,7 +269,7 @@ def audio_entrance(client: Client, bot_msg: types.Message): normal_audio(client, bot_msg) -def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): +def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, custom_filename): chat_id = bot_msg.chat.id headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" @@ -284,7 +284,10 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, bot_msg.edit_text(f"Download failed!โŒ\n\n```{e}```", disable_web_page_preview=True) return - filename = extract_filename(req) + if custom_filename: + filename = custom_filename + else: + filename = extract_filename(req) with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as f: filepath = f"{f}/{filename}" diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index c64165f7..ce16f280 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -298,9 +298,12 @@ def direct_handler(client: Client, message: types.Message): message.reply_text("Send me a DIRECT LINK.", quote=True) return + url_parts = url.split(" -n ", maxsplit=1) + url = url_parts[0].strip() # Assuming space after -n + custom_filename = url_parts[1].strip() if len(url_parts) > 1 else None bot_msg = message.reply_text("Request received.", quote=True) redis.update_metrics("direct_request") - direct_download_entrance(client, bot_msg, url) + direct_download_entrance(client, bot_msg, url, custom_filename) @app.on_message(filters.command(["leech"])) From 2f4ebdd7b87a0adea6e1ded9a81610b779960755 Mon Sep 17 00:00:00 2001 From: BIKASH MAITY Date: Tue, 10 Sep 2024 19:54:09 +0530 Subject: [PATCH 221/297] Update README.md (#426) . --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7c2760e..fea9daa5 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ To deploy this bot, follow these steps: * Use export APP_ID=111 APP_HASH=111 TOKEN=123 5. Finally, run the bot with * ```Python - python ytdl_bot.py + python ytdlbot/ytdl_bot.py ``` ## Docker From 1a60460a32c75948ebd50775a875d14947f586ad Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:54:43 +0530 Subject: [PATCH 222/297] Fix issue #424(/ytdl not working in group) (#425) * Fix issue #424(/ytdl not working in group) * Improve bot usage in groups * typo fix --- ytdlbot/tasks.py | 12 ++-- ytdlbot/utils.py | 17 ++++++ ytdlbot/ytdl_bot.py | 144 ++++++++++++++++++++++++++------------------ 3 files changed, 107 insertions(+), 66 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 7db8c73c..00a1c379 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -214,12 +214,12 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) -def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, custom_filename): +def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, new_name): if ENABLE_CELERY: - direct_normal_download(client, bot_msg, url, custom_filename) + direct_normal_download(client, bot_msg, url, new_name) # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) else: - direct_normal_download(client, bot_msg, url, custom_filename) + direct_normal_download(client, bot_msg, url, new_name) def leech_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): @@ -269,7 +269,7 @@ def audio_entrance(client: Client, bot_msg: types.Message): normal_audio(client, bot_msg) -def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, custom_filename): +def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, new_name): chat_id = bot_msg.chat.id headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" @@ -284,8 +284,8 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, bot_msg.edit_text(f"Download failed!โŒ\n\n```{e}```", disable_web_page_preview=True) return - if custom_filename: - filename = custom_filename + if new_name: + filename = new_name else: filename = extract_filename(req) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 1d74c006..1075578c 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -266,5 +266,22 @@ def extract_filename(response): return filename +def extract_url_and_name(message_text): + # Regular expression to match the URL + url_pattern = r'(https?://[^\s]+)' + # Regular expression to match the new name after '-n' + name_pattern = r'-n\s+([^\s]+)' + + # Find the URL in the message_text + url_match = re.search(url_pattern, message_text) + url = url_match.group(0) if url_match else None + + # Find the new name in the message_text + name_match = re.search(name_pattern, message_text) + new_name = name_match.group(1) if name_match else None + + return url, new_name + + if __name__ == "__main__": auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ce16f280..ebcdc0ad 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -61,7 +61,13 @@ ytdl_download_entrance, spdl_download_entrance, ) -from utils import auto_restart, clean_tempfile, customize_logger, get_revision +from utils import ( + auto_restart, + clean_tempfile, + customize_logger, + get_revision, + extract_url_and_name +) logging.info("Authorized users are %s", AUTHORIZED_USER) customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) @@ -219,7 +225,7 @@ def send_message_and_measure_ping(): client.edit_message_text(chat_id=reply.chat.id, message_id=reply.id, text="Ping Calculation Complete.") time.sleep(1) client.delete_messages(chat_id=reply.chat.id, message_ids=reply.id) - + thread = threading.Thread(target=send_message_and_measure_ping) thread.start() @@ -269,63 +275,6 @@ def clear_history(client: Client, message: types.Message): message.reply_text("History cleared.", quote=True) -@app.on_message(filters.command(["spdl"])) -def spdl_handler(client: Client, message: types.Message): - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - url = re.sub(r"/spdl\s*", "", message.text) - logging.info("spdl start %s", url) - if not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("spdl_request") - spdl_download_entrance(client, bot_msg, url) - - -@app.on_message(filters.command(["direct"])) -def direct_handler(client: Client, message: types.Message): - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - url = re.sub(r"/direct\s*", "", message.text) - logging.info("direct start %s", url) - if not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Send me a DIRECT LINK.", quote=True) - return - - url_parts = url.split(" -n ", maxsplit=1) - url = url_parts[0].strip() # Assuming space after -n - custom_filename = url_parts[1].strip() if len(url_parts) > 1 else None - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("direct_request") - direct_download_entrance(client, bot_msg, url, custom_filename) - - -@app.on_message(filters.command(["leech"])) -def leech_handler(client: Client, message: types.Message): - if not ENABLE_ARIA2: - message.reply_text("Aria2 Not Enabled.", quote=True) - return - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - url = re.sub(r"/leech\s*", "", message.text) - logging.info("leech using aria2 start %s", url) - if not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Send me a correct LINK.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("leech_request") - leech_download_entrance(client, bot_msg, url) - - @app.on_message(filters.command(["settings"])) def settings_handler(client: Client, message: types.Message): chat_id = message.chat.id @@ -509,6 +458,81 @@ def search_ytb(kw: str): return text +@app.on_message(filters.command(["spdl"])) +def spdl_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("spdl start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("spdl_request") + spdl_download_entrance(client, bot_msg, url) + + +@app.on_message(filters.command(["direct"])) +def direct_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("direct start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Send me a DIRECT LINK.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("direct_request") + direct_download_entrance(client, bot_msg, url, new_name) + + +@app.on_message(filters.command(["leech"])) +def leech_handler(client: Client, message: types.Message): + if not ENABLE_ARIA2: + message.reply_text("Aria2 Not Enabled.", quote=True) + return + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("leech using aria2 start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Send me a correct LINK.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("leech_request") + leech_download_entrance(client, bot_msg, url) + + +@app.on_message(filters.command(["ytdl"])) +def ytdl_handler(client: Client, message: types.Message): + redis = Redis() + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("ytdl start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + redis.update_metrics("bad_request") + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + redis.update_metrics("ytdl_request") + ytdl_download_entrance(client, bot_msg, url) + + @app.on_message(filters.incoming & (filters.text | filters.document)) @private_use def download_handler(client: Client, message: types.Message): @@ -524,7 +548,7 @@ def download_handler(client: Client, message: types.Message): contents = open(tf.name, "r").read() # don't know why urls = contents.split() else: - urls = [re.sub(r"/ytdl\s*", "", message.text)] + urls = [message.text] logging.info("start %s", urls) for url in urls: From dd3e42ff911d388755b9e4014bf7503689349c29 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:26:15 +0530 Subject: [PATCH 223/297] Improved `/stats` command (#427) * Improved `stats` command * Update clean_tempfile to properly clean --- ytdlbot/utils.py | 20 ++++++++++++++--- ytdlbot/ytdl_bot.py | 54 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 1075578c..4ec317d4 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -52,6 +52,16 @@ def sizeof_fmt(num: int, suffix="B"): return "%.1f%s%s" % (num, "Yi", suffix) +def timeof_fmt(seconds: int): + periods = [("d", 86400), ("h", 3600), ("m", 60), ("s", 1)] + result = "" + for period_name, period_seconds in periods: + if seconds >= period_seconds: + period_value, seconds = divmod(seconds, period_seconds) + result += f"{int(period_value)}{period_name}" + return result + + def is_youtube(url: str): if url.startswith("https://www.youtube.com/") or url.startswith("https://youtu.be/"): return True @@ -220,9 +230,13 @@ def auto_restart(): def clean_tempfile(): - for item in pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()).glob("ytdl-*"): - if time.time() - item.stat().st_ctime > 3600: - shutil.rmtree(item, ignore_errors=True) + patterns = ["ytdl*", "spdl*", "leech*", "direct*"] + temp_path = pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()) + + for pattern in patterns: + for item in temp_path.glob(pattern): + if time.time() - item.stat().st_ctime > 3600: + shutil.rmtree(item, ignore_errors=True) def parse_cookie_file(cookiefile): diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index ebcdc0ad..7e296743 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -11,6 +11,7 @@ import json import logging import os +import psutil import threading import random import re @@ -62,6 +63,8 @@ spdl_download_entrance, ) from utils import ( + sizeof_fmt, + timeof_fmt, auto_restart, clean_tempfile, customize_logger, @@ -232,18 +235,52 @@ def send_message_and_measure_ping(): @app.on_message(filters.command(["stats"])) def stats_handler(client: Client, message: types.Message): - redis = Redis() chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""): - bot_info = "Stats Unavailable." - else: - bot_info = get_runtime("ytdlbot_ytdl_1", "YouTube-dl") + cpu_usage = psutil.cpu_percent() + total, used, free, disk = psutil.disk_usage("/") + swap = psutil.swap_memory() + memory = psutil.virtual_memory() + boot_time = psutil.boot_time() + + owner_stats = ( + "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" + f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" + f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" + f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" + f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" + f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" + f"Memory Total: {sizeof_fmt(memory.total)}\n" + f"Memory Free: {sizeof_fmt(memory.available)}\n" + f"Memory Used: {sizeof_fmt(memory.used)}\n" + f"SWAP Total: {sizeof_fmt(swap.total)} | SWAP Usage: {swap.percent}%\n\n" + f"Total Disk Space: {sizeof_fmt(total)}\n" + f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" + f"Physical Cores: {psutil.cpu_count(logical=False)}\n" + f"Total Cores: {psutil.cpu_count(logical=True)}\n\n" + f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" + f"โฒ๏ธOS Uptime: {timeof_fmt(time.time() - boot_time)}\n" + ) + + user_stats = ( + "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" + f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" + f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" + f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" + f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" + f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" + f"Memory Total: {sizeof_fmt(memory.total)}\n" + f"Memory Free: {sizeof_fmt(memory.available)}\n" + f"Memory Used: {sizeof_fmt(memory.used)}\n" + f"Total Disk Space: {sizeof_fmt(total)}\n" + f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" + f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" + ) + if message.chat.username == OWNER: - stats = BotText.ping_worker()[:1000] - client.send_document(chat_id, redis.generate_file(), caption=f"{bot_info}\n\n{stats}") + message.reply_text(owner_stats, quote=True) else: - client.send_message(chat_id, f"{bot_info.split('CPU')[0]}") + message.reply_text(user_stats, quote=True) @app.on_message(filters.command(["sub_count"])) @@ -709,6 +746,7 @@ def trx_notify(_, **kwargs): if __name__ == "__main__": + botStartTime = time.time() MySQL() TRX_SIGNAL.connect(trx_notify) scheduler = BackgroundScheduler(timezone="Europe/London") From 324b18dfe23bf7965b8d54161fbd0f789d84f3d0 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 26 Sep 2024 17:59:13 +0200 Subject: [PATCH 224/297] disable buy --- ytdlbot/ytdl_bot.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py index 7e296743..28cda9e4 100644 --- a/ytdlbot/ytdl_bot.py +++ b/ytdlbot/ytdl_bot.py @@ -10,17 +10,16 @@ import contextlib import json import logging -import os -import psutil -import threading import random import re import tempfile +import threading import time import traceback from io import BytesIO from typing import Any +import psutil import pyrogram.errors import qrcode import yt_dlp @@ -29,7 +28,6 @@ from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant from pyrogram.raw import functions from pyrogram.raw import types as raw_types -from tgbot_ping import get_runtime from youtubesearchpython import VideosSearch from channel import Channel @@ -69,7 +67,7 @@ clean_tempfile, customize_logger, get_revision, - extract_url_and_name + extract_url_and_name, ) logging.info("Authorized users are %s", AUTHORIZED_USER) @@ -216,6 +214,7 @@ def ping_handler(client: Client, message: types.Message): chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) message_sent = False + def send_message_and_measure_ping(): start_time = int(round(time.time() * 1000)) reply = client.send_message(chat_id, "Starting Ping...") @@ -355,6 +354,8 @@ def settings_handler(client: Client, message: types.Message): @app.on_message(filters.command(["buy"])) def buy_handler(client: Client, message: types.Message): + return client.send_message(message.chat.id, "This feature will comback soon...") + # process as chat.id, not from_user.id chat_id = message.chat.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) @@ -500,7 +501,7 @@ def spdl_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text + message_text = message.text url, new_name = extract_url_and_name(message_text) logging.info("spdl start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): @@ -518,7 +519,7 @@ def direct_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text + message_text = message.text url, new_name = extract_url_and_name(message_text) logging.info("direct start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): @@ -539,7 +540,7 @@ def leech_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text + message_text = message.text url, new_name = extract_url_and_name(message_text) logging.info("leech using aria2 start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): @@ -557,7 +558,7 @@ def ytdl_handler(client: Client, message: types.Message): redis = Redis() chat_id = message.from_user.id client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text + message_text = message.text url, new_name = extract_url_and_name(message_text) logging.info("ytdl start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): From fa88ea5ade6c63369378a86079fb400812c884b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:21:54 +0200 Subject: [PATCH 225/297] Bump yt-dlp from 2024.8.6 to 2024.9.27 (#438) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.8.6 to 2024.9.27. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.08.06...2024.09.27) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 86a856d6..6538b83f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.8.6 +yt-dlp==2024.9.27 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From fcfbf3550217ccc148dd1589f4a67ab2c3d89bfb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:18:43 +0200 Subject: [PATCH 226/297] Bump yt-dlp from 2024.9.27 to 2024.10.7 (#445) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.9.27 to 2024.10.7. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.09.27...2024.10.07) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6538b83f..bd8fa1cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.9.27 +yt-dlp==2024.10.7 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From afb2e5841f75b102f9a6fb6dcba84eac92138205 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 09:05:44 +0100 Subject: [PATCH 227/297] Bump yt-dlp from 2024.10.7 to 2024.10.22 (#447) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2024.10.7 to 2024.10.22. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.10.07...2024.10.22) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bd8fa1cd..610c453d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.10.7 +yt-dlp==2024.10.22 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From ba43e79e88abdafc5f8ceb5a552ce625489a61fe Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:07:17 +0530 Subject: [PATCH 228/297] Bump yt-dlp from 2024.10.22 to 2024.11.04 #451 (#453) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 610c453d..03e3d98c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.10.22 +yt-dlp==2024.11.04 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 829c62bf0ed14831c8a107fba69428b378cf6cca Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:40:10 +0530 Subject: [PATCH 229/297] yt-dlp's Python dependencies must be installed using the default group, beginning with 2024.11.04 release (#455) * Bump yt-dlp from 2024.10.22 to 2024.11.04 #451 * yt-dlp's Python dependencies installing using the default group --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 03e3d98c..a5d53624 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp==2024.11.04 +yt-dlp[default]==2024.11.04 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 05232543dee4cd3846587d2edd4203a765cbbf50 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:36:35 +0530 Subject: [PATCH 230/297] Improve caption (Fix #456) (#457) * Improve caption (Fix #456) * Entities for send_media_group --- ytdlbot/tasks.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py index 00a1c379..4178e870 100644 --- a/ytdlbot/tasks.py +++ b/ytdlbot/tasks.py @@ -470,7 +470,7 @@ def spdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, ur temp_dir.cleanup() -def generate_input_media(file_paths: list, cap: str) -> list: +def generate_input_media(file_paths: list, cap: str, entities: list) -> list: input_media = [] for path in file_paths: mime = filetype.guess_mime(path) @@ -483,7 +483,10 @@ def generate_input_media(file_paths: list, cap: str) -> list: else: input_media.append(pyrogram.types.InputMediaDocument(media=path)) + # Add caption and entities of the first media input_media[0].caption = cap + input_media[0].caption_entities = entities + return input_media @@ -496,17 +499,17 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid markup = gen_video_markup() if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1: # just generate the first for simplicity, send as media group(2-20) - cap, meta = gen_cap(bot_msg, url, vp_or_fid[0]) - res_msg: list["types.Message"] | Any = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap)) + cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid[0]) + res_msg: list["types.Message"] | Any = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap, entities)) # TODO no cache for now return res_msg[0] elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1: # normal download, just contains one file in video_paths vp_or_fid = vp_or_fid[0] - cap, meta = gen_cap(bot_msg, url, vp_or_fid) + cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid) else: # just a file id as string - cap, meta = gen_cap(bot_msg, url, vp_or_fid) + cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid) settings = payment.get_user_settings(chat_id) if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): @@ -520,6 +523,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid chat_id, vp_or_fid, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), reply_markup=markup, @@ -533,6 +537,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid vp_or_fid, supports_streaming=True, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), reply_markup=markup, @@ -544,6 +549,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid chat_id, vp_or_fid, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), ) @@ -556,6 +562,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid vp_or_fid, supports_streaming=True, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), reply_markup=markup, @@ -569,6 +576,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid chat_id, vp_or_fid, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), reply_markup=markup, @@ -581,6 +589,7 @@ def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid chat_id, vp_or_fid, caption=cap, + caption_entities=entities, progress=upload_hook, progress_args=(bot_msg,), ) @@ -641,7 +650,25 @@ def gen_cap(bm, url, video_path): f"{user_info}\n{file_name}\n\n{url_for_cap}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" ) - return cap, meta + + offset = len(f"{user_info}\n`{file_name}`\n\n") + + entities = [ + # For the filename + types.MessageEntity( + type=enums.MessageEntityType.CODE, + offset=len(f"{user_info}\n"), + length=len(file_name) + ), + # For the URL + types.MessageEntity( + type=enums.MessageEntityType.URL, + offset=offset, + length=len(url_for_cap) + ) + ] + + return cap, entities, meta def gen_video_markup(): From 926c372eed1fa46728a3d0df095146fafaea8c43 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:30:45 +0530 Subject: [PATCH 231/297] Improve `extract_code_from_instagram_url` function (#458) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve `extract_code_from_instagram_url` function * Bump yt-dlp from 2024.11.04 to 2024.11.18 (Tested โœ…) --- requirements.txt | 2 +- ytdlbot/utils.py | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index a5d53624..77ee94a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp[default]==2024.11.04 +yt-dlp[default]==2024.11.18 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py index 4ec317d4..59d5c93e 100644 --- a/ytdlbot/utils.py +++ b/ytdlbot/utils.py @@ -247,12 +247,28 @@ def parse_cookie_file(cookiefile): def extract_code_from_instagram_url(url): # Regular expression patterns - patterns = [r"/p/([a-zA-Z0-9_-]+)/", r"/reel/([a-zA-Z0-9_-]+)/"] # Posts # Reels + patterns = [ + # Instagram stories highlights + r'/stories/highlights/([a-zA-Z0-9_-]+)/', + # Posts + r"/p/([a-zA-Z0-9_-]+)/", + # Reels + r"/reel/([a-zA-Z0-9_-]+)/", + # TV + r"/tv/([a-zA-Z0-9_-]+)/", + # Threads post (both with @username and without) + r'(?:https?://)?(?:www\.)?(?:threads\.net)(?:/[@\w.]+)?(?:/post)?/([\w-]+)(?:/?\?.*)?$' + ] for pattern in patterns: match = re.search(pattern, url) if match: - return match.group(1) + if pattern == patterns[0]: # Check if it's the stories highlights pattern + # Return the URL as it is + return url + else: + # Return the code part (first group) + return match.group(1) return None From 2a893857f29716259aa262985d67033434bcbdc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:56:13 +0100 Subject: [PATCH 232/297] Bump yt-dlp[default] from 2024.11.18 to 2024.12.3 (#465) Bumps [yt-dlp[default]](https://github.com/yt-dlp/yt-dlp) from 2024.11.18 to 2024.12.3. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.11.18...2024.12.03) --- updated-dependencies: - dependency-name: yt-dlp[default] dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77ee94a2..a8440a9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp[default]==2024.11.18 +yt-dlp[default]==2024.12.3 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 825d817e404c2b4fed9379350bd08c56008ad1c9 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:16:20 +0530 Subject: [PATCH 233/297] Upgrade to CodeQL Action v3 (#470) --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 05a8e31f..c7cea334 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # โ„น๏ธ Command-line programs to run using the OS shell. # ๐Ÿ“š https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 3adb096b99df9c2b387eaa2043571ac84545fc9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:46:29 +0100 Subject: [PATCH 234/297] Bump yt-dlp[default] from 2024.12.3 to 2024.12.13 (#469) * Bump yt-dlp[default] from 2024.12.3 to 2024.12.13 Bumps [yt-dlp[default]](https://github.com/yt-dlp/yt-dlp) from 2024.12.3 to 2024.12.13. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2024.12.03...2024.12.13) --- updated-dependencies: - dependency-name: yt-dlp[default] dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * support for impersonating browser requests in yt-dlp https://github.com/yt-dlp/yt-dlp#impersonation --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a8440a9a..11c2b63c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp[default]==2024.12.3 +yt-dlp[default,curl-cffi]==2024.12.13 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 02bd9f00ab009e31cf5c024f0a55b298ed6582db Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:19:34 +0530 Subject: [PATCH 235/297] Bump yt-dlp to 2024.12.23 (#474) * Upgrade to CodeQL Action v3 * Bump yt-dlp to 2024.12.23 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 11c2b63c..947abcce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp[default,curl-cffi]==2024.12.13 +yt-dlp[default,curl-cffi]==2024.12.23 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From 100b14d10a0c235c6e39e44980b39050d453791d Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:44:27 +0530 Subject: [PATCH 236/297] Bump yt-dlp to 2025.1.12 (#475) * Upgrade to CodeQL Action v3 * Bump yt-dlp to 2024.12.23 * Bump yt-dlp to 2025.1.12 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 947abcce..33afd86b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ git+https://github.com/KurimuzonAkuma/pyrogram tgcrypto==1.2.5 -yt-dlp[default,curl-cffi]==2024.12.23 +yt-dlp[default,curl-cffi]==2025.1.12 APScheduler==3.10.4 beautifultable==1.1.0 ffmpeg-python==0.2.0 From d472cdc03c7e018111a719665d4eb30c52207d25 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 19:32:19 +0100 Subject: [PATCH 237/297] WIP: refactor (#460) * step1 * remove * add pdm * going * going * going * Improved `extract_code_from_instagram_url` function * Remove terabox download function Due to server-side restrictions, the Terabox download function is currently does not work as intended. * Remove unused `parse_cookie_file` function This commit removes the `parse_cookie_file` function as it is no longer required following the removal of the Terabox download function. * define abstract class * basic upload done? * db operation * db operation * db operation * db operation * db operation * fix name_pattern regex of `extract_url_and_name` function * pre-commit * pre-push * pre-push * Refactor: Change single quotes to double quotes for name_pattern regex * add more methods * rename * use self._bot_msg * download done? * update deps * add entrance * runnable * fixes * fix * record usage fix * add cookies * fix settings * hint * hint * reset * rename * formats fix * update README.md * update * rename * rename --------- Co-authored-by: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> --- .env.example | 61 +++ .gitignore | 2 + Dockerfile | 24 +- Makefile | 52 -- Procfile | 1 - README.md | 265 +--------- app.json | 43 -- assets/2.jpeg | Bin 274244 -> 0 bytes conf/YouTube Download Celery.json | 794 ---------------------------- conf/supervisor_main.conf | 34 -- conf/supervisor_worker.conf | 33 -- docker-compose.yml | 25 +- k8s.md | 200 ------- k8s/01.redis.yml | 53 -- k8s/02.mariadb.yml | 80 --- k8s/03.configmap.yml | 17 - k8s/04.ytdl-master.yml | 65 --- k8s/05.ytdl-worker.yml | 47 -- k8s/06.flower.yml | 101 ---- pdm.lock | 843 ++++++++++++++++++++++++++++++ pre-push.py | 22 + pyproject.toml | 15 + requirements.txt | 48 +- scripts/low_id.sh | 12 - scripts/migrate_to_mysql.py | 27 - scripts/start.sh | 13 - scripts/transfer.py | 29 - src/config/__init__.py | 19 + src/config/config.py | 40 ++ src/config/constant.py | 52 ++ src/database/__init__.py | 6 + src/database/cache.py | 32 ++ src/database/model.py | 174 ++++++ src/engine/__init__.py | 28 + src/engine/base.py | 241 +++++++++ src/engine/direct.py | 95 ++++ src/engine/generic.py | 113 ++++ src/engine/helper.py | 139 +++++ src/engine/instagram.py | 74 +++ src/engine/krakenfiles.py | 32 ++ src/engine/pixeldrain.py | 18 + src/main.py | 352 +++++++++++++ src/utils/__init__.py | 110 ++++ worker.yml | 15 - ytdlbot/channel.py | 181 ------- ytdlbot/client_init.py | 25 - ytdlbot/config.py | 81 --- ytdlbot/constant.py | 132 ----- ytdlbot/database.py | 418 --------------- ytdlbot/downloader.py | 297 ----------- ytdlbot/flower_tasks.py | 14 - ytdlbot/keep_alive.py | 11 - ytdlbot/limit.py | 260 --------- ytdlbot/premium.py | 106 ---- ytdlbot/sp_downloader.py | 244 --------- ytdlbot/split-video.sh | 60 --- ytdlbot/tasks.py | 735 -------------------------- ytdlbot/utils.py | 317 ----------- ytdlbot/ytdl_bot.py | 772 --------------------------- 59 files changed, 2524 insertions(+), 5575 deletions(-) create mode 100644 .env.example delete mode 100644 Makefile delete mode 100644 Procfile delete mode 100644 app.json delete mode 100644 assets/2.jpeg delete mode 100644 conf/YouTube Download Celery.json delete mode 100644 conf/supervisor_main.conf delete mode 100644 conf/supervisor_worker.conf delete mode 100644 k8s.md delete mode 100644 k8s/01.redis.yml delete mode 100644 k8s/02.mariadb.yml delete mode 100644 k8s/03.configmap.yml delete mode 100644 k8s/04.ytdl-master.yml delete mode 100644 k8s/05.ytdl-worker.yml delete mode 100644 k8s/06.flower.yml create mode 100644 pdm.lock create mode 100755 pre-push.py create mode 100644 pyproject.toml delete mode 100644 scripts/low_id.sh delete mode 100644 scripts/migrate_to_mysql.py delete mode 100644 scripts/start.sh delete mode 100644 scripts/transfer.py create mode 100644 src/config/__init__.py create mode 100644 src/config/config.py create mode 100644 src/config/constant.py create mode 100644 src/database/__init__.py create mode 100644 src/database/cache.py create mode 100644 src/database/model.py create mode 100644 src/engine/__init__.py create mode 100644 src/engine/base.py create mode 100644 src/engine/direct.py create mode 100644 src/engine/generic.py create mode 100644 src/engine/helper.py create mode 100644 src/engine/instagram.py create mode 100644 src/engine/krakenfiles.py create mode 100644 src/engine/pixeldrain.py create mode 100644 src/main.py create mode 100644 src/utils/__init__.py delete mode 100644 worker.yml delete mode 100644 ytdlbot/channel.py delete mode 100644 ytdlbot/client_init.py delete mode 100644 ytdlbot/config.py delete mode 100644 ytdlbot/constant.py delete mode 100644 ytdlbot/database.py delete mode 100644 ytdlbot/downloader.py delete mode 100644 ytdlbot/flower_tasks.py delete mode 100644 ytdlbot/keep_alive.py delete mode 100644 ytdlbot/limit.py delete mode 100644 ytdlbot/premium.py delete mode 100644 ytdlbot/sp_downloader.py delete mode 100755 ytdlbot/split-video.sh delete mode 100644 ytdlbot/tasks.py delete mode 100644 ytdlbot/utils.py delete mode 100644 ytdlbot/ytdl_bot.py diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..ff8d3cb8 --- /dev/null +++ b/.env.example @@ -0,0 +1,61 @@ +# Example .env file for ytdlbot configuration + +# Number of workers (default is 100) +WORKERS=100 + +# Telegram app ID +APP_ID= + +# Telegram app hash +APP_HASH= + +# Telegram bot token +BOT_TOKEN= + +# Owner ID, separate by , +OWNER= + +# List of authorized users (comma-separated) +AUTHORIZED_USER= + +# database Data Source Name +DB_DSN= + +# Redis host +REDIS_HOST= + +# Enable FFMPEG for video processing (True/False) +ENABLE_FFMPEG=False + +# Desired audio format (e.g., mp3, wav) +AUDIO_FORMAT= + +# Enable Aria2 for downloads (True/False) +ENABLE_ARIA2=False + +# Path to Rclone executable +RCLONE_PATH= + +# Enable VIP features (True/False) +ENABLE_VIP=False + +# Payment provider token +PROVIDER_TOKEN= + +# Free downloads allowed per user +FREE_DOWNLOAD=5 + +# Token price (default: 10 credits for 1 USD) +TOKEN_PRICE=10 + +# Rate limit for requests +RATE_LIMIT=120 + +# Path for temporary files (ensure the directory exists and is writable) +TMPFILE_PATH= + +# Maximum size for Telegram uploads in MB +TG_NORMAL_MAX_SIZE=2000 + +# Maximum URL length in captions +CAPTION_URL_LENGTH_LIMIT=150 diff --git a/.gitignore b/.gitignore index 778fa339..5f87873a 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,5 @@ reinforcement/* /dump.rdb /ytdlbot/premium.session-journal /ytdlbot/main.session-journal +/src/main.session +/src/main.session-journal diff --git a/Dockerfile b/Dockerfile index 737a0c4c..ca689d2d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,16 @@ -FROM python:3.11 as builder -ADD requirements.txt /tmp/ -RUN apt update && apt install -y git && pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt +FROM python:3.12-alpine AS pybuilder +ADD pyproject.toml pdm.lock /build/ +WORKDIR /build +RUN apk add alpine-sdk python3-dev musl-dev linux-headers +RUN pip install pdm +RUN pdm install +FROM python:3.12-alpine AS runner +WORKDIR /app -FROM python:3.11-slim -WORKDIR /ytdlbot/ytdlbot -ENV TZ=Europe/London +RUN apk update && apk add --no-cache ffmpeg aria2 +COPY --from=pybuilder /build/.venv/lib/ /usr/local/lib/ +COPY src /app +WORKDIR /app -RUN apt update && apt install -y --no-install-recommends --no-install-suggests ffmpeg vnstat git aria2 -COPY --from=builder /root/.local /usr/local -COPY . /ytdlbot - -CMD ["/usr/local/bin/supervisord", "-c" ,"/ytdlbot/conf/supervisor_main.conf"] +CMD ["python" ,"main.py"] diff --git a/Makefile b/Makefile deleted file mode 100644 index 299f31fa..00000000 --- a/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -define NOLOGGING - - logging: - driver: none -endef -export NOLOGGING - -default: - docker pull bennythink/ytdlbot - -bot: - make - docker-compose up -d - docker system prune -a --volumes -f - -worker: - make - docker-compose -f worker.yml up -d - docker system prune -a --volumes -f - sleep 5 - -weak-worker: - make - docker-compose --compatibility -f worker.yml up -d - docker system prune -a --volumes -f - sleep 5 - -upgrade-all-worker: - bash upgrade_worker.sh - -tag: - git tag -a v$(shell date "+%Y-%m-%d")_$(shell git rev-parse --short HEAD) -m v$(shell date "+%Y-%m-%d") - git push --tags - -nolog: - echo "$$NOLOGGING">> worker.yml - -flower: - echo 'import dbm;dbm.open("data/flower","n");exit()'| python3 - -up: - docker build -t bennythink/ytdlbot:latest . - docker-compose -f docker-compose.yml -f worker.yml up -d - -ps: - docker-compose -f docker-compose.yml -f worker.yml ps - -down: - docker-compose -f docker-compose.yml -f worker.yml down - -logs: - docker-compose -f docker-compose.yml -f worker.yml logs -f worker ytdl \ No newline at end of file diff --git a/Procfile b/Procfile deleted file mode 100644 index 8be22ed7..00000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -worker: python ytdlbot/ytdl_bot.py \ No newline at end of file diff --git a/README.md b/README.md index fea9daa5..0f5b7ae9 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,14 @@ [![docker image](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml/badge.svg)](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml) **YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ** +๐ŸงฐRefactor ongoing๐Ÿงฐ This Telegram bot allows you to download videos from YouTube and [other supported websites](#supported-websites). -**Celery mode won't work and I don't know why. So I may shutting down this bot soon.** - # Usage -* EU(recommended): [https://t.me/benny_2ytdlbot](https://t.me/benny_2ytdlbot) -* Asia:[https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) +* EU๐Ÿ‡ช๐Ÿ‡บ: [https://t.me/benny_2ytdlbot](https://t.me/benny_2ytdlbot) +* Singapore๐Ÿ‡ธ๐Ÿ‡ฌ:[https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) * Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. @@ -26,42 +25,22 @@ Just send a link directly to the bot. * Instagram (Videos, Photos, Reels, IGTV & carousel) * Pixeldrain * KrakenFiles - * Terabox (file/~~folders~~) (you need to add cookies txt in ytdlbot folder with name) - [terabox.txt](https://github.com/ytdl-org/youtube-dl#how-do-i-pass-cookies-to-youtube-dl). + * Terabox (file/~~folders~~) (you need to add cookies txt in ytdlbot folder with name) + [terabox.txt](https://github.com/ytdl-org/youtube-dl#how-do-i-pass-cookies-to-youtube-dl). # Features 1. fast download and upload. -2. ads free -3. support progress bar -4. audio conversion -5. playlist download -6. payment support: afdian, buy me a coffee, Telegram Payment and Tron(TRX) -7. different video resolutions -8. sending as file or streaming as video -9. celery worker distribution - faster than before. **NOT WORKING** -10. subscriptions to YouTube Channels -11. cache mechanism - download once for the same video. -12. instagram posts(only available for my bot) -13. 4 GiB file size support with Telegram Premium -14. History and inline mode support -15. Supports multiple download engines (yt-dlp, aria2, requests). - -> [!NOTE] -> **For users of [my official bot](https://t.me/benny_ytdlbot)**\ -> Files larger than 2 GiB will be automatically uploaded by me(My Premium Account). By utilizing our service for such downloads, you consent to this process. \ -> That means I know who you are and what you download. \ -> Rest assured that we handle your personal information with the utmost care. -> +2. No ads +3. download & upload progress bar +4. download quality selection +5. upload format: file, video, audio +6. cache mechanism - download once for the same video. +7. Supports multiple download engines (yt-dlp, aria2, requests). + > ## Limitations > Due to limitations on servers and bandwidth, there are some restrictions on this free service. -> * Each user is limited to 10 free downloads per 24-hour period -> * Maximum of three subscriptions allowed for YouTube channels. -> * Files bigger than 2 GiB will require at least 1 download token. -> -> If you need more downloads, you can buy download tokens. -> -> **Thank you for using the [official bot](https://t.me/benny_ytdlbot).** +> * Each user is limited to 3 free downloads every day. # Screenshots @@ -73,8 +52,6 @@ Just send a link directly to the bot. ![](assets/instagram.png) -## celery **NOT WORKING** - ![](assets/2.jpeg) # How to deploy? @@ -83,41 +60,10 @@ This bot can be deployed on any platform that supports Python. ## Run natively on your machine -To deploy this bot, follow these steps: - -1. Install bot dependencies - * Install Python 3.10 or a later version, FFmpeg. - * (optional)Aria2 and add it to the PATH. - -2. Clone the code from the repository and cd into it. - * ```Bash - git clone https://github.com/tgbot-collection/ytdlbot - ``` - * ```Bash - cd ytdlbot/ - ``` -3. Creating a virtual environment and installing required modules in Python. - * ```Python - python -m venv venv - ``` - * ```Bash - source venv/bin/activate # Linux - #or - .\venv\Scripts\activate # Windows - ``` - * ```Python - pip install --upgrade pip - ``` - * ```Python - pip install -r requirements.txt - ``` -4. Set the environment variables `TOKEN`, `APP_ID`, `APP_HASH`, and any others that you may need. - * Change values in ytdlbot/config.py or - * Use export APP_ID=111 APP_HASH=111 TOKEN=123 -5. Finally, run the bot with - * ```Python - python ytdlbot/ytdl_bot.py - ``` +* use pdm +* pdm install +* copy .env.example to .env +* python main.py ## Docker @@ -127,169 +73,8 @@ One line command to run the bot docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot ``` -## Heroku - -
Deploy to heroku - -Deploy to Heroku - -If you are having trouble deploying, you can fork the project to your personal account and deploy it from there. - -**Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no -longer be available.** -[Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) -
- # Complete deployment guide for docker-compose -* contains every functionality -* compatible with amd64 and arm64 - -## 1. get docker-compose.yml - -Download `docker-compose.yml` file to a directory - -## 2. create data directory - -```shell -mkdir data -mkdir env -``` - -## 3. configuration - -### 3.1. set environment variables - -```shell -vim env/ytdl.env -``` - -You can configure all the following environment variables: - -* WORKERS: workers count for celery **NOT WORKING** -* PYRO_WORKERS: number of workers for pyrogram, default is 100 -* APP_ID: **REQUIRED**, get it from https://core.telegram.org/ -* APP_HASH: **REQUIRED** -* TOKEN: **REQUIRED** -* REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ -* EXPIRE: token expire time, default: 1 day -* ENABLE_VIP: enable VIP mode -* OWNER: owner username -* AUTHORIZED_USER: only authorized users can use the bot -* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot -* ENABLE_CELERY: celery mode, default: disable **NOT WORKING** -* BROKER: celery broker, should be redis://redis:6379/0 **NOT WORKING** -* MYSQL_HOST:MySQL host -* MYSQL_USER: MySQL username -* MYSQL_PASS: MySQL password -* AUDIO_FORMAT: default audio format -* ARCHIVE_ID: forward all downloads to this group/channel -* IPv6 = os.getenv("IPv6", False) -* ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) -* PROVIDER_TOKEN: stripe token on Telegram payment -* PLAYLIST_SUPPORT: download playlist support -* M3U8_SUPPORT: download m3u8 files support -* ENABLE_ARIA2: enable aria2c download -* FREE_DOWNLOAD: free download count per day -* TOKEN_PRICE: token price per 1 USD -* GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. -* RCLONE_PATH: rclone path to upload files to cloud storage -* TMPFILE_PATH: tmpfile path(file download path) -* TRONGRID_KEY: TronGrid key, better use your own key to avoid rate limit -* TRON_MNEMONIC: Tron mnemonic, the default one is on nile testnet. -* PREMIUM_USER: premium user ID, it can help you to download files larger than 2 GiB - -## 3.2 Set up init data - -If you only need basic functionality, you can skip this step. - -### 3.2.1 Create MySQL db - -Required for VIP(Download token), settings, YouTube subscription. - -```shell -docker-compose up -d -docker-compose exec mysql bash - -mysql -u root -p - -> create database ytdl; -``` - -### 3.2.2 Setup flower db in `ytdlbot/ytdlbot/data` - -Required if you enable celery and want to monitor the workers. -**NOT WORKING** - -```shell -{} ~ python3 -Python 3.9.9 (main, Nov 21 2021, 03:22:47) -[Clang 12.0.0 (clang-1200.0.32.29)] on darwin -Type "help", "copyright", "credits" or "license" for more information. ->>> import dbm;dbm.open("flower","n");exit() -``` - -## 3.3 Tidy docker-compose.yml - -In `flower` service section, you may want to change your basic authentication username password and publish port. - -You can also limit CPU and RAM usage by adding a `deploy` key, use `--compatibility` when deploying. - -```docker - deploy: - resources: - limits: - cpus: '0.5' - memory: 1500M -``` - -## 4. run - -### 4.1. standalone mode - -If you only want to run the mode without any celery worker and VIP mode, you can just start `ytdl` service - -```shell -docker-compose up -d ytdl -``` - -### 4.2 VIP mode - -You'll have to start MySQL and redis to support VIP mode, subscription and settings. - -``` -docker-compose up -d mysql redis ytdl -``` - -### 4.3 Celery worker mode - -**NOT WORKING** -Firstly, set `ENABLE_CELERY` to true. And then, on one machine: - -```shell -docker-compose up -d -``` - -On the other machine: - -```shell -docker-compose -f worker.yml up -d -``` - -**โš ๏ธ You should not publish Redis directly on the internet. โš ๏ธ** - -### 4.4 4 GiB Support - -1. Subscribe to Telegram Premium -2. Setup user id `PREMIUM_USER` in `ytdl.env` -3. Create session file by running `python premium.py` -4. Copy the session file `premium.session` to `data` directory -5. `docker-compose up -d premium` - -## kubernetes - -refer guide here [kubernetes](k8s.md) - # Command ``` @@ -298,27 +83,19 @@ about - What's this bot? help - Help spdl - Use to download specific link downloader links ytdl - Download video in group -leech - Download file using aria2 -direct - Download file using requests +aria2 - Download file using aria2 settings - Set your preference -buy - Buy token -sub - Subscribe to YouTube Channel unsub - Unsubscribe from YouTube Channel -sub_count - Check subscription status, owner only. -uncache - Delete cache for this link, owner only. -purge - Delete all tasks, owner only. ping - Ping the Bot -stats - Bot running status -show_history - Show download history -clear_history - Clear download history ``` # Test data +
Tap to expand ## Test video -https://www.youtube.com/watch?v=BaW_jenozKc +https://www.youtube.com/watch?v=V3RtA-1b_2E ## Test Playlist @@ -350,7 +127,6 @@ https://krakenfiles.com/view/oqmSTF0T5t/file.html https://terabox.com/s/1mpgNshrZVl6KuH717Hs23Q
-
# Donation @@ -359,7 +135,6 @@ Found this bot useful? You can donate to support the development of this bot. ## Donation Platforms * [Buy me a coffee](https://www.buymeacoffee.com/bennythink) -* [Afdian](https://afdian.net/@BennyThink) * [GitHub Sponsor](https://github.com/sponsors/BennyThink) ## Stripe diff --git a/app.json b/app.json deleted file mode 100644 index f5f5fc37..00000000 --- a/app.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "YouTube-Downloader", - "description": "A Telegrambot to download youtube video", - "repository": "https://github.com/tgbot-collection/ytdlbot", - "logo": "https://avatars.githubusercontent.com/u/73354211?s=200&v=4", - "keywords": [ - "telegram", - "youtube-dl" - ], - "env": { - "TOKEN": { - "description": "Bot token", - "value": "token" - }, - "APP_ID": { - "description": "APP ID", - "value": "12345" - }, - "APP_HASH": { - "description": "APP HASH", - "value": "12345abc" - }, - "OWNER": { - "description": "Your telegram username", - "value": "username", - "required": false - } - }, - "formation": { - "worker": { - "quantity": 1, - "size": "eco" - } - }, - "buildpacks": [ - { - "url": "https://github.com/heroku/heroku-buildpack-python.git" - }, - { - "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git" - } - ] -} diff --git a/assets/2.jpeg b/assets/2.jpeg deleted file mode 100644 index 65c36cd44b10d9611ee0bedd798c1ee94b6381cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274244 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 z zPxw+o?HE-pl0qnIZch=NypyMG`erFYG9@1|09(tbXS4y{dx}!iEMK}&$6eP*QQ9@( zPTITM650_yKCRhDJU=kice%tS;**Jd#tWrFZ+HsnN-wEY;sAd(^zpS(`Cc6rIG#a! zuFITyQ@7u|UkK@j;9!8WqfU&GY`EI4EU>drT@!(5&$bx~h<^Sj>cBYauOSVktSh3I z#ZRkuF0|l~OFT5EoIXB%rm+oNFY8hdgg946o=4Of?Gt z`om9ibPE)*qCw+Woidg?->%s^(p|I89${LN-Mkt(qf3* zkoilX$dQ8KauXZ@yNSx(hBcdHkyh5}dCb=9lMZ-@V;u@zVaGJ6QELz81sxO@R{9(9 zUv!ykl5%G;oSe44RfAeHCJUBW*aDGK0M94CV+Z?(&L2}ji7~IHlst1>rFY;Do;m%c zyv4m3=o~Y1H7&foI_KScRLi(Sph14w_Cu@q52OPk>V*15VLIG})h}`_nuf}3z};iD zs0jqIz=2eT8zyQbZL=jB<@boFq?wVNe$1j#fU`{jjtd!6$ujj?lBPs=JCB9n8d0?n zN4lz6L0R5PmHz6Q(=(OEX;1I9g&A4RzF6D(tS+Pn;aHOekr=aLvY^uo-Lp$Pqzn(b zZiCgbHK@`s6F8SN-*(2d(W*KOeC*cMgwes?c0A8JP=66){zeE(PL-IXC$~IIYYNz- z0oE{*P&Iy3Znd7r;qB66+wa>9wYJM@myR9HV$04M)~e~8$z{;b7Bw*7Pj4Mxk4F_c zq_tbE7iluQ-QfL8V@Z3!v>V;$_DYz0eAR}zYFX_v_bcQwH^Wh+O|3*m7m$r!0G`ss zh4G_bRI|HKDEXd2(k$=JxVr|m9o767vWQHtAQ+ifw}7=yBdxRg?gtgv1(5^x&I$Fp zOCl3|OigJdb}75|OboP;8E70}c$SYhh}RfgYa!$yMo(|l*G#)N#J1Dt%S?osb&%YG z5&g^sPE8eVpDtr01PL9F>>XROu_H02%R*V?M@Sn)WjAfYGLaTgJ|3HHMOEn}sJkE4 zz{JA#-4rfhgk}~CA$eWNnwKc5;qhS>jP|s;tIKOj7wA6ir_XyqL>H;UkUCOtKO|Vt zjUN>#(*s~~+fcrN$9$Sqe!f8(?N=Y8x_~_TWVOE}ek?KHpo^_j!g-@PwbSDx%e*jW z?&Q@ZpNMpL;kh{>wQGf=mWy?1Cf}3z&ZdNs?;jr4f8yC1 z@Dkef&_REHr+B@j8%VaPST7@sGzb6~Q(clrF#FytfpEiW4B6Tm+92#7+e^R`84g!J zt=0K`gDL(er18fuG3;!BSh4Ne8*ycb&i*BMit7`In zkhpOm^E>O>PS;Kq{E%y|Pmr=V7JcevNX>s)@5ot{owvkpQqkmA0t!s@O?EYgm zcjY`{f1C}8Gprq<4DPhYjY8`juuiGx7DHVFMEz*d22$n1Qp{>DchiSo`+H*330*2FC8d#Oxn zzLpB9^a7|Aux(z!w9`vDsi;hFGxGBY%HE^FP|&=PQygss?z|^6&?PfyOz-}xOyo?yQXA+E?uAd+?m3>l|!q` zMdynG7KrN08yLjm?U?4uW?K5$8162Qw#^HT?Gc(3kRhFE4LiVKVSCoDzu!$mG-KuL zYMTaQ@6b5pQeAF^M^WEHTXRxdbMo<(5IelNqciPcCmGai9q=cOJ;+kmW^>6>L@$YA zyW-09d|UWfr(t-dToqDwA0uUa+T0=Q;aZf|$f^@o?EiVclkaGaJI6S*&-_{~9Y>^| zMTl;kqCULg&O~!?OZRM@T)tktIeRbZa${nNA+NH1CRm}-oEl?=OCcCi zM^D;RJQqgb%^O3bmTX5l7-PG(@%Gz!_@U!gqo}f}&=|i7D4ru)h`9bbXmNh7W9efy zl@5sSf$*u8o#~&W>*f_9qR{G_$fk|cx8gOL%GF<6(nIMdPfuHu(&PpcV*IW~X~m4H z)Kr>vH(d}?Y9si`30cGQLPikp@Um#QQFH_{C*ay#mnpn+a-kM>N^6(j5-#R|Cg~ge z;7;zm!p6BO{l)(F0eFa7QQ)NPNV(QLn_t zIp;Rl6|bI^q!L)3?F?gb=Vu`bIMpj)(m%T)cTB8cjH0EbI-pT6(_1s8{#x2pvvmGS zU%4m8gePYXq6(rC5s;cOu!>5MjB*5a8nhSU@M@i;bI_}T6r`|&*wd1XQ7copzp->t zglDx)-iyKXKFCB)j$p!~s!)7K_Op_NVcSBnWU}1Vv*@8#XDJx9R)@jdZ$zH^&HqI1 zi`M~1-QG1p=G<7isu`13s0JoE#7h2|R@&`Y}=M~4eC*D(g z;5v6LfEsXs*YB}>afgabt$Ib!zhp<}E%h z)tK@kmU2Z?wtb$@eHObeZ^@MgnA?A>@O5v)%*#4Pj#fmm;c79JBSyYY z?U*3huxeC%UXt^u!;PA9m54~gRZ_;l-f6+GprwEyuQymnvkHD4Q$N5bD0x)7W|G!Y zrNHJzH_Gna*yN>!)Y;c{mOLzDDh(Y2iIADIroC)(58%xc(|NHx-L%yg=FMa+0xNYD z7UW*%mUjd1FGWq`{Io-93GB>zwak*jOzpUx${E`*;|20L(KRct`Ky~D$ayh%aiT z3xFMFH=;;noHozxQ&b>t61WA5vLTwh#oLy$HRI0zH!huJP502B-Zgaf~^;XN?g zxDd?YAT|Qy&8eAK+b{4kpjCaq`4Q|%xk0k0PnFKNV3G{1A8_yGd~CIuB|0`94#4?< z&;R3vo$#>YVTIK=Xui?yq}$1%#@zv(=2_@Tj}49>tMT1V^Rwf#%VfEIGrkYREEDx`HqMot@Q!O~}5z5U`m0i%rDA0&trg05_fj4YK ztqs-e(!O!rHmkmIP83#vG{ZlSg)jRN@kpI9CQ zgHO#g{zSPh<6SbM2!7s}a_eDM?wnh$Gup%aD?xs*yQq!0+E}2u~Agti<}+2X4aBMyGT7 z?fs*AtL2*wp`vPtiHxbR2NzZAq-;D;8r-YwiKIGGi`34v6GY7I$ff8O?C?;AqqSz2 zuPEkvx8LV?_Fq>RZ*(@4mHS!^j5_pIR7uoZ?)oJog=yFRThd%k3W*SuX~!r1iRm*n zjgkeOl(`|JC9uF%q6@8RL=db#?Q|_6eALucN9FnX|59MP^*`nx`yDy$+|8n;y6f)t zF9Yde=Up&E1GMrVedK(H%FuM|nnk?yyVv89dFMD>e;jc)OzQ}a%!B`fV!T;?-1yly zF`WGJM69oN>9(5eZM7r*mzf%`nQup~v-?_A_{(SPO2p*8>r@POu~7NF8~Jg+yC@l@8fcH)etw2=R}9Bt6l3y|w_bHCyWz%t8XWLu zdiYsS<%?x4VG&6`t(Tf-PB7Di?*aQOc}Ru-?oVIf+Q_&4D{g?5{CgZ|1~BatKR$2R zZV<4O|3Id{1v4C_4qek??b!ao$L%_M3Z62}M|hsIjI5=4sW?em0FHMKaw;6RSbx%; zY*!w_p?vz1@4jHJGuC#E6AMJrfIHT{9lLx_#xTI=IEbjwkqo~W^ciH0hMynOZ#NAB zRiD6n0dTi~n+7737^io{6xowXp|y>ivy$&{2;>kLoc-a7};emwSV;`g_z*{yDk1(?{5#r{|%yR4_ar*d*K^y0{>*_ zV*Cj1SsHg(pwQ#!SBEH9>+|Q&Jzv^Bz#b0r&piX%d{mCdDJzao{@gGA zVZyv0pRm=uwj0+S=T2OCIe*jUdd_z>($)1LrGHcJJXY z2>n`gp`i9laqP_QX9V}^agjTk2;7Vg2ve3FyYjlxZvTdV%FEj+sdLOE3FL(dYqg3z z5qF8pZ{t&6`4;HbZ(R-{H7Rc^1O~5XWzm+O{OG56oCrU8&!J%5 zu>*ArBB}PyN(3w1yc~Cs&?EpO9>TNd z=w1&3FU)?J{t_Qwn8+$4wnP|qA=-8YVx+ePia~6Fyi7b~Kjd+4e$raI{A3H{%6^=s z%RZk6hL2KbRaC_edkWlV3#4IQ-tW1oKy~9(pz;CT*rqCv!KeL3HvNa5KTcFw&S~Hv zi@l%O>ZR%Pfq5`n!gs9TnxD1I1li2_)GrR3=^3prm9t*a4^*7iKk6-a{Q$*2{&FGN zhV^SPo3>{FCG_nQCpb4lyJkM`qp;>edsfCmPIJ<8iM`fldg!!eH5K(quNg*+4*qXH z;fw`MBQ5vf>+s%4ni(g(AU=VRU^d`jbO@7c3P}FV7nthSN(y-n$zF1c$K_)-qZ1=P zk@i85!+JN`n~{?Cas1bL7~Q*1F|p4fqV)Cinm6pRYsC>AB$(-v6;sve-}G*I4E6~1 zb)N5X$W$(OwZlJBcAM(7hnF*xulp&*$1*GzdQi_))siCpufH6u_gZp)?pXp`)qi#R z;P8bHj6;+Q{%W(eafTF6FJ{Yqbafc{5VI&;QEnC0lsdv-f^OuD+~Y@A<3jZ^t>ReN zNtSwV4s0**|GU4g*n7XeEA?9I-Bo!X#sha_sFQtZ>muhi-d}U0t3G(};DIACWs+XR zoaVXmuu^$>jLss=wL|HcNqTg@9|R!l-J73xBgij)Le9t+kE`* zEyvF#uo54ii(a7q)M1|<_pd~ME>9&TK%!w%@|KkU@!)Iuqwt^a>)#gtcS+#bXndS| zgkLmVMmaE(S6{!SH7?HM_r5IrKb1)PihK*SQ8Uq)v35d3_uv$sT{ve3!IEQP!?CVC zYvTWNNuq#!R$&alzxc5fTMpZ(Wd`g;8*0S=T%ehkzf0Dfvjx&wOWgwHZ-J&Y9iVIO z@;5~d%(5FiPjv9Lc^D6wZq^TOfuL{Bzg(TVg>R8_Ii=RNgCVOf?~*ek&(t$>7cL2) z@_PKOhN6v_Glo}N@qV+kSGk-$+~2b~1a#n$7>Cr%J{UbB;Y)gn-rLK$5bC}$R<8{Yy}}7%gMfLJndi#=w#`V zDSJaTBu#EFtY}@t^-fgf7U)BB%be!zQZsVKdm6IiA-pofXt~Wo-InjaxT~eH|Ab8T z$0KKpvLA+2Y=QbLyy@4-i}kw}Q#~fOKvSa%?x+)~NhkmXSXyJ#GZwy8N}HU_LYGv} zIMpq-Cg5v91Ys{+p_QOS#2b=F6*@j1h`ZspM(mW~N19MP+^2KV{eVxDbrlNeQp-{F zBLA{jPOCirrrs_!DA-|q1IkGc4weAH%6BtOjZ*|mnY(% zeBLzfU{$^45fK|!J5xEZkgy$J51Ee8yFTg?+~?MRrG1c}3{}u#bWXLAJE+|zw(EB8 zJ01Qcy=SzZFM341d#^P4)>42AR^!8M#ezPg^vojYtD$R4>MlN3EK zOqb;Z#f>V)8dkNlr-xhLe(XT{F7WEnoSR9_pOmLxm*^o4nxooe=2~fE@;*c%qLO9( z@4?&*FO~^txy|}4(rqNkjjl6QdLFtp;}=K3TXsmxb;|g(r3ReiLDKnWM=1xp&li=G zZjN1cC;=B8^oe7enCtoEahK*daHg~A^9!>{=OZb5C3FpHMUTHrLmAt8^6RgWa~v8; zUzgDBDCeEDFuf?0+ZYyjwnvN~mSdT4)|g*hIYxCrW31cb{m!4l$% zG8c#3^I7NkUzZ_Kl*P~i#-iLtjn8$)AChxxCuqErEiY~qa}f&_QlVgnmyDL{#2N?H0!}Nb4Tue>lfzkhB;EcAXxP+lG6h(>B9@I@Fsm+_N!mH z#=eJ&$n>-_liiC6ezKab)|AO-`D!YMAGLMb8-DQ!yf;h0zU*eea2RFm*U@_!xJ#fx zsmH>n+pjdu8z$;f4AdER7vxbrtRQ~7!&6DrNLAECc7WKRdYnH?wu`@kIdh3W_5Jmc zgj>wVLzGd$M{B~M&j?eZuLO#L^;-^~f4^b9tv<;!kPl3*Q_URsY%(!+Z+N#rtJjQX zX&OzZW?36w6Dynl_JXU{D8)D`qKREas!}(}+Qen|;%BbX~CYv(4&j=XZrqU$3Soyp#GZFOQ&*H)#lz&Tb*# z4Bh0LK`L^eO3+`ZGcJi&wOkatWM`aq`-{qAVq|(n>Ru2sz9}g+uV@Q&-6%^K;)@(V z+H5`}Lo|8M;@Wd#Hi_N0K(e_nG(JrS?~~GnEhGeZ$!A*Y@P8_<5|h`6kaJ;9xH_bobQ3~$ zI?rx8HNYri&$L)7b8KQNv(yBKAO535+`ZK0cH}3OAbcy&-Jsp4#@bRIlo22{#YXy@ zlC&=?v?7E1pWIhY_GVFuk?VnwM>HqqIhSWmaoSkFz9dnHQcrzCN{5IY@7MsJB2HH^ zw(wFCQ8Ri%-P~OCIn2kAcxv{hkM4a>c^bgEbxDN#=*Y>GH%cLj44WG;IBQ$m3rGLf zRM6QC<2^h&7B7lsHke6y5dJdnT@5xo!W84!3}0C^kw;$&#Sv?)DoW#y9WcrX9TjW- zw-(Hm=3TUmPFXlhb!*D;u0AKaZO;3!byoCaQ14T=@m`6>rH?%TId+jYJY^d;P%5FH@#h+6J^?9OdLJhkAGcU7u#zEEo|w6B z9uZ}?{Ic}rb)ItA!}{iT&(ElN+f5^zErLuvo9@58Qsy4&6Uy*mh?x6bb2JmFpPR6a zxvm=TVx$zZ@ zp{UCvsY><=*6_GreT66c1Zi*V8pt zL5O187KqFNnQ@!lPsq?h=W6$6Y1lfL!!fJm$UfbYlH$6y*d8%%$)yJ!?Dj0e2^^^2 zs`rIPp=BMn#Y$p43@n_4mGF>enGwjoYOrAqyGMy3d#bCnSe?-M7GPbX^G5Z}Eaj_%)v<;s?THU3&)v8)nh|BMX|h>>PP1N&w!c?91#H9#H`f337Hm9c!5Y@lkM3wuqjo(m zxGxgIa4op68C($CBbtESSSc6vV=I*>PPJT)b-Q)+9s4Ea7n$VxfAtc+^7rfCHICx_ zlRah+KTI-D)qB`f8s#Q^)Q_D_St;ZH(MuB*_g{)Sp1&#ZPmD%*zw%!mFp0v9zc^yI z3Vr@`jadG+KSTJp?S-Ky2q)*H@yK$0=|qwLVx(wd^fG)#r7ycciZM)ML~3y1ed!dw zTc_BuL{d(B;BJ**1h`u*T=`OX)mUuMrAx)7jw$qT;qRypPl(iqL_&B&8P!P4#)aF@jPWbz$5hH z8n4xHRJ}?=OxbDizVKH?0e`*|pSRZDv2j-pee{)Hr+MaKA1rKAifp>joP36{lBXt9 zrN$;$qDHt`kSE{?LiBE~*+2zPV9i-0%Qc4kMg(UXl?sb}bb7}QM)ycv%YWl}+D%uQ zbPIG=JL>AmMo&%TmoT?h2hQ>KS^v%c56p`j)LWpFV#lIMIdy5(E58)nH?>*Zk>i(q z*yY^n*VTz1R6`EiDsHR|4ZrWdPm`dcv1!wPtK4+Jw@v53KnM6G+ex*FeHV1ZH8pPb zMKm$_5il6I89N{G^MX7#%5|cZ4YHFy)4l}?U4G0KPKY#f4n~U@IIbf^Hv`kOIpAh0XuU6f>kSLTrO3JCVuAU-*u{@6XFY(W< zjSYUulTN&41x~1xQNOM~cSrGcV7^c5aC9;guhqG)6JxXQG!&7IY~IocbkMkp7KYc0 z#(m~qcK;uHXB`$r+xLA0L|Q<)r5i+A6p`*wsii}}WoZPYq@=q+KnVe1X?6(#0cip0 zSURPic>Bc#h+JkNpE?VRp_rXLja1zu)iYH+o7`vCHiV==;S|i3(0W z2#5(oVd!_HK>kvn(v)O7ju-jlzjHFd97az4U0K**Z0a-O;nQWi_N8J?-$p<5xob~~Pc+K17Ec8M!b~OdGL1g?V zr&g>T*Q*JMM0#zKwd}<4#1e&SW47H5vZHYdO}HXCx2MWUgl{qEHOdXGc76=&m@|&y z-gLqlbCWuGHG9N4gcS5BJOiiungUB!_J!_Ilx{n%yYu*T1!^eE{J6jDYesSf$3p=% zJub-5fF)---4ZJ5Yx9mug$OPVH?dKx8xwDZGvCl+z-N+m=a zz&baxkdQ&QONuw|vlH`M0o{FD>tuX=j?w&KfgMdjR+!Jqc?2Oy65W&hHoag@(RU=v zgCHfV!t7O+UIdOvtdoeD?>lHw+qxZ?J;z+sC&+~%!Rdyq<>1zZLCAdkkfHURv$;0b zuve{!O;x%Fi<=ilaKxJ_GE>Kr(I)KxO7RJAYF1BZv{IrsJUJqj_;g;5IMnha`2eZZ zJyVa|Wsu$b#C-N>h2N?G)^@5f`rezUc%v`>J5qUyj{4GoFr+c+y$5x6cP{>-EF}p> zJiTJMkeXQ@pwhfOsLoD3_~5e^tkX220(sS* zk_h6>QkJ$g-e3$~A1*BWjudeI(2hN2{GL-XgAI|rp#(UM^?i`?$t0vOehyU~wZPWI z<&&Hvg;C}?>Cos*05U?FJQiic+2RQ!%vl);7dHBuXOihV60~+-;tj$yS4|XxrQ!Hx zRgJE(TDTO&IZ~{ad>V5qXPloLac$Nk&4;p{+ZwQ&Z#1}iauaXXjlS@WTP*2dRkDn1 zk6Jr_we9Pabf7N60_DZvzze>YZPj{hUBK%dTfFw22}^kJIJ$scbB%aeDd%Y9JsW0k z*gC;TgD_0n2Y=0O;(3f|0uKR%D2n{)>n9!Li}-|sJh3HE?g-oK@un{Va+2FVm-R%7 zkx#LJ%rB;Z9_9-Qd6VLBF;3wbM9tB_L1CL2(9~v;;T@G(9qV9e87ZJKLDQ-+%sHC? z!OsjH)0C|QT{L`@3!>i#x_00>WkR!HY}l0cQo$4#d9`V2`#r7!+YOQ%omuxX{m3+x zw?rS*Q5BBbU%eG z9uuQyKFAI`^1U36Gp4OK~7wQ=5iO!s2^OgFwgfLtpCmk3GIaZP@B z_E#gLI<6>o7Jc65+hv+z$ser-=bjZ&@VkuWw{+lmC}k9@`WLF--UpdB}ywPY?IWj83o5qjZ5NT}Z~Rt1ktj%FipMa{9>9&lm0*;HTFPr2BS)AdA9w~`j@P~;J-BQ?g9cy5Ys5mS=)EUz zwR%8%ZPBA5M0I+lsHNd8Xj3%QbJkU#f<&aomC_l%uDZdcaUu)L*6Ys&T2djU1}3^@ z3K49Z#RK`i{ZU~Ayei+<84QwsLEY&TK|Y0F zhR)4TT%3<7yJlV+HcjGL|K&_S|JW;?CZzucN%?QaMQ44nIo+)3-;sJ2RCbSWi%7Blt=}0sQoWrc$)LxlUtEe|Lf(A|K+FIE0J5@ zU}UOu^KvlG?iDwD^76N{6SVB-N~St(6;WWiA*q58ZQ@eSf4(RjG5Vjb3Z}bDBfk3g zw#|I!lyq3Vz9aR*@h{sN_{HPK-)IwU$v*Wz-D7c+mHYp!kEBh${p~dZIXq9>D$nLV}gE!VR&joLmF$WBV zHEjY8;mp}oNqx$!Vkz!QzK(i)XZmESLv`!xwr2k6+MnQd2keUvual*HmMec1)UrXgDk6{n6x*Z*Sb1J5V6rRq|G)pm8*VO~dPugKD**q|8M0gCXnXmF5~r ze*u+oJ%tDyHsxvY0y`%RpDo;ulknNjHRg{EL(pz^n;|w`+}paQGKN!Dw4#1V#3^BK z?o3Okp$nwAZz-RdZk-e@UU)DE^oKP8TGaut+*|4K!@W5W+;!JC?&3JMsv6G)nG)1- zW|`ECx9o!EWQu}9Kp*jthw0*n>-Mxtp(@+zNlul;Lv2j9oG4~98R^yu)*-CJsH#c< z_94`!A#a-=AecBSTXOYWL#U;y)gUqKt_cJqg0H8Jeh6JBDmQZ|0syya4Oq^!J-wU@ z1EQPF;^F*XwD7RZq^*Ss&!(uYjkiLy(WK7e;AxM8R&TAJ%}V>sQGFY&5A2RcdG;+m z8Rq-YCs4LGEByRj_W20;HDib5fnomkZt!E>VU}nN()ycMT``?Sn%K&}{!U8$qWqV0 zVW~ESTD-&tnSMkdY>i$G8-LIStyLo|XXM7WmYK8X;$PE)%$je#KEThSZI!Mn%oyWZ zpHXx$kpGSpO)(%Bzk>_wVVhBPa-5tkP}r-GfJ{c!7MG*IJlnh z9ceJ8X?x&952ZR2wROxG0)JSTFDJ5A=A9|!HstxjUY7LRxv`AM;=()7bKf$8x%h{- zOc0D60c?DBeN-^VK=sVC`jq3nG@9_@??^>^13pcB2B-O(HVlD+wwx}!}V@w}%!>TOjuYpqqGy<~2AAWRTLBY#`z z+g6iQkllDYv#I%10U?BoT5q25-CU}Djd(><82Y2Kv5*)1CW6jKk6hLv{sU6?HAhX^ zpT<6;I*311sQUCBNyeqTYO&_Q@Joj}wjbu^RM)m6>W;1-C?YM?LbYj)JwP{J(+SI^q{7LO+Tyc|(H*nwXBW6&<$=ZeD?cx6E;xzxB%IBW0i zlEGAK!=;8m`SVVd&g1%YxtXKYnt>TE&%?K)jQR1=RRG_bByly5!<(kkXo*>CWpM zR7P5HrA?bBUT>_4UVu}rO5RXYe^ZmI&_$9LH*w<@cq?o7TA=rUxEJPAy9Kff(~4X# zN<%ooQu}7m2{w@^WL%ehLRz$Cc_+PUR;)jOQ^5&L+#zmhlZiEru-C)3c2u~t6uyNe zLAKwK@D$?cbUUqNzqVR9X)~WGP9DHMO8^yTbv36|*E1LnkV0nZq zHCAXbOww1@(K4Do@2+XvTXT8zvo{Omrhx-751zbkNoBfc+7)xVp8M)sJZoy$8Q#(x zdcBU6NvRZJmWY?}nnM+ng}xz(n&-r`ugNp?moN8X3H^{&TSEe+l|`#9tT7FGFrrH6 za>t}S!4xJhME?qZwSOo$w!|c!?=mM-|48s=gVoUNt%r`dUDif4q}(yQ2ux5DCQBwx zXaqGsmEFe-op-|9bTYxC@gw#VxRe%QQI_7cB}P)gTg^y{ixunH@d%kxhG$Y#$>Y4T z;bb&8;lv$`Y2%M1;SzxlrO~NZ-AObrX9NFo^@E}USoCpR_@{?`f9J;l=;3FBfL*T# zF-^-YGk~Fg%Io4`G5+8jh_ujjol+dv8BRQy=yP1APe+B{h#O`#k9wYZD6?x{DN1mA z{N|w-Md;ycNd>=%8ZN7j@i4n+xffOY0I~EnrfKEJRY6(sW(7sSXiU?p^vo|&0pQ!@ z8Z$1Nt|hqpDNW%c89{@?ve;2;TY-YFo|XH{afWtSeEWweubvM!UJFj)H@SJSS$RH) zBlrkh1NzMwE9p#T%GF@w8-{Qh;1r@PhA5RGZN!`qxOiypik&>m=hyzwrSZJ%X>!5f z|MG<=P={r?nY}sNQ?*KuB+&=JUE@;azB+nS1dy%yxOiLGhK8=Oia)V-p*T*5UY(iB}Moa}!!>ezO{CNy1F3 z7N&3{;XrxBlq_@McR6_Ixk&FwIrm`wqe08mQP2e_QSIVE1IXASV@f>np!K_zrvSKvG+|L2SSue*}}!&Qgg zWrW32|IKaNj+PXFz2X3+`Y+pPk!g10Z|v1OoVdTc@qcad5e3`*f7Vq}6fA#x4GRe~ znZMES-yW=h%Te{Z=$rb z##?xxIMZ3}RamJ|MlBHv&bmfC!MV@zVw_Rj=_|Cj4X2wyN9p=)>8eN3at;|%1#)%^ z{PGNWnr?Qc!An+E=yawdMGfaCACrRLD>jZe4=BC>AWA6G7wz3)mSIphyD z8(U&AtZ8M;fQMw7sMLhwofUj5Wga(b$bRj%!4o@LVkFRP5A7=5hz39CcVI|jo3p>i z4B|59nALkmZI(QDm~wojhy6Euu8#Vkq)wh-M>QeZ?_(_E{5x(&3|AI~_DA}^c~kZl zJ=N6Bg)`QZr-#`Wcf_0lGS+z;<+SIAlOo+0tk=k2?o28P--{iBe;z?08j@r1xKPV% z<$rnif_6e2Nji6~lSH8}j3XKZ#r#6XTqT&hzHjF}3Ucf2e*dlK=!j-v@H^6#hX3!W zyl*=a%`O8pg8KF-o8UBS24mAs0H1o;k?#9oRIk7tak_f$WF}otgU%h{w+zd}=wBz$ z+%3!FUIb8`e*{06jazucRReQ}(^I>2%$~$*0-J&gxWInqU`GWTn2Zfcm9HqF^;9gPqdk zDe{bU22sbMrb1eAeGs1F9ulRP)lw-@(-QIkGHzI{lRffI4!<5aE174HPe1M4U6k=5 z6->N~n=^S^g1^quayG#O-YRPOFowKV%IY$vc8+J~$q+A~bVjAaU#AvPApAB?8_rUK zfoA0zYT$CueZN*bp3K)RcL>b_1J${bn_=1bHR=1TSC|%9ypRqKnT8Z<6Zb&R-9jFn z&EPBPZ!RxDutUjX)Yr5ol)cJaIpIYf7g3pHRV@thd9+v?XV|u@=vbX2q;^yr^QD0? z`Mx6smqQ4+N>3RmP5162VV<*?Ag)pi|H|;~5_HUc)XIh$xhT_0>RqerLo>{4`xMsi z0<*pr-cSg4p4h09e^d)(w{P;mVXtS6P(DEI&2>%P#S&(hSwB&g^3zQ*QAJk!^l5a~ zHYawvkM!ichPRRgEN806Lw(pG!^DpQto%j=*w5an-`}iMj6VH%8#Ro@2oU);aCvkV ztHX1a0V^tZFsR9D2lh=^qY+Zm0c7@T(4v|2mJ)9y*b-JIARmobRhRTa5cy{O+*a7w z(Y-8XO{j#f(7Pf0k-)ij^ms3SQ{l|mbJO`^bN0qzaN#?Vmc^BudB8J2&J3vZ05R1X zYE_o$m7pyh^P)xrr{k#bP}IAgr#K zLQ8@{emi-a_de_e;&BL*E=vJtLloQdfoN^yb2IjvcA=o`#&~A3VbDOdK(s!)RBqQt z7_%8opC(oecg7}!4-bBGaNfXSn=}_A3!)$l9!yNkjam(wraDH@QF`Sm^fSL!YU#2gdP|&h%wC(ef@R8;hK~!T4O%Q{ zpf3VwgDREpNU+w>WfOm|5b{a!?x23S3ihgdjOtyH_;qSWgAj)IGL@e=pAeJ8()$GV8!5`lwh=g<~RPU`RO2 zK~1cdsTm&;|$7X^)zYZbZThLXG)sLr zU~~A3$=B`Gw=i!UdTm_Ldbwm`MphS|i70-&s5+_>skuN?ygkk_A$)-4S*Cm499x7y zTg2c36(55=JNP2Czy-MwtTs{R;I_!oApr$$5fVCcQ%5cj^nKSkH7h4)5sDOZd}4-U@Ev_V-5_ z5cHnV&OUB_-01xRRqJj5FXU}unb6Z`HFMXH84?RnGPT8Ka{@Ec?E{xSqp?ujMVGVd zz6a^!WVNl)*+7A}mJsJue+EiT40woBMsqIK$}n&;Fe~X6+A)5r3~LDC8rxM0BiyT3 zOrlomm=M9px7;j9?x};-6J6ySx-8}U2?5#pNIf>E*;4*YBerNM*bJEQ60yqDi{I@a zWirxWeZw_ozU9R|W3YP!Wi<$WEJm8s!WtL+@&@28G7XnF->Y!Ghf$I5DV6~R>zt`q zWP^2v%AuAzLlyZHn)QIg1=EQAu=f1NE1OkiViLcuUYC8*&S;>}$HZo(Jtm#Vk?yh!|1KS9GZv?2n;H*&|< zyj)1*lyUNYTY_Qy@&!|%%gv8)gQKq9>jm6!edvJ?TD%;dad5A3?z9%Gjz+35kWUf9 zThO18qImfMJ24@5f`9B@&g^gP1YJggyDwjg&#SKsNWRnx+!=!=+(uQ)@=@R}qsA8j z&yApCz0FWgJJcG*G{Sz}<|}0h_<@Up@ux>p!!?sTLe~eNX(8f>T{o2n%RV{g$Xr zQ1na}JXrGiShuq-bT2_`Xt+-|A3VW@u*uSFqaN9w<$D3#`sjQ@iDgiLp(d9qh}Rbu zx8rqp`(@G3!~Rdrj$cKw;KzDiIa$ATOkVc+jq#U*ErxobnILIc7=-+vFZKs^rTmAh z4)tb4#ZmvwZQGF*+ko@W@&=n;{AD{?$<)^WdRHtY&2IeDo&Orxar2*bSFdbG#NS>6 zpG^F3H2ilC*xA4Aj4Nf}O7Q*7JRYDx{*^!dLs|aF$zlxKml0hWH`GDDH`4Toj8r2h zQNrHRZ|Mwwjf#A+Hg5w=E?68U;f5hYs$}mrBI2ojDKmJIl!}_1G8>q#uBCd!d}tRr zRVNvXtQ+t@$YN&=-ua>QJQkzqrEWNnDJpe3P_Aq5HIp^4espS2ay9emU$`x%56YJ+ zSv47Ig+{2c>%g~a@Yk4jITt}+;7O{BpbmJP4cp{Xuv=QsNMQ>*qsQe3p)NZ1$p4Yc za8&h~58+oiuJR;J;dD;;gCe)|IV z%u2N0SB&D2X4noWdjF-Et(-aYx%1%N^QGpR)2TmhXN@vT2*ihUfwK5Z z99d?>Kq{Y_+Ma{bKrNH%(Zv+}L4&M5^_13LNlzdW0=?(MX+h;X%E;yzXKzZ!6>f@q zf~+|OHKr`v?0C+B3(GmlktULXvc)FyjNz+?qv&r_x$f89nY#eg!^;D8A=WSz7Lc%I zKIb`ihI6zxwc2%NZ8C6$!DC0*_+syzT^=gugcu7=kHl!FB!){j^zb+yv)u(=w zX{ay;2S1gS?aBL(F&hbB(ID@dyv=lhO)I_;_$QGW*Equjwo)&^Lj3P$9UzZ=Zl@3C z7Wz258`AwdKY}^O&483fJ=S+5n(57!=FX$L+jZL)XlE~N_7^XB>Jdl!u40&;0ucwE z&8`=_vlHi=KNPzyH@}*c%`GyN>}OGF)Lnc68}Zn}!`k4C`k5Z4a|z6%+-ifgWyf3w zZD@9=M#l>EQX;_w9z(ekGl1vBVgVTtuHrL5n*@&Pzck@(yL4Ie<4UsLX`OkIr!`w) zLjHB08cEFw>|6ESjCD7gOFfus=KF&?fdIl!^;j_^Uj=iH2TsJX7D6_BF9sQzuOVLk zHDmEc%#GPjo){zctQ83guE!|MKg4?^dHW-YSy7Z^xf>efj&zS6mdVpIHALYSua}wu z`5Hx|<_dCIDznI;CRo~I#<6*6N(J2@`V>eMQ9AeH8M*+zhbs4n>-!Jua!Cfm>k6Fq zO^WiVJhZ+U;SF7d4<7H(j!5mlsw*ZLLO%KQG=w^TMcP1}_arJ|Ak^=6$o|UMQ?T(+ zGc*JQ3@kmlDX8lzHHR@zW(;)MA8L4WBI}@*W84WdU$a3^Hz>Hi|0)@&qGN&AJl?9^ zyYAv+8coPr<3pHMp;A=?_D+4Sn2sRk8}haQ;!eBJYYlGelgJ=%yV7cjQR^DDl}?nF zrlX2aCz4w+WC9Zew%CQl3Gq&KeKX4U6T4EL!Fb2Ty00AN*;zSb)Xs`U(%X3cLC#fm z9D&Z-e3fad9A!`vkxn*^rh>X5{`_){Iv$28h@#5ZIVny8GolJqjf$eM&irZS(*-+(p=lAwoKg&coxmuIx zXO&}4;mct$$Si|gSotz5o*e9`2+CiBCFieoh_C3sp^0>EIvrAN#)LjOYz4=SC2&Op7PbB2qx5TyczPSGzq(2oPj#ZE0b?IZ|i&MtK4kW@*3_Q)}C*n z_i8>!@!&S{+imuU|HAH{njlBJER0%?b{{4)=KXIYG#;2`zYM#3c7n+Bn$M&

3t{ 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 z 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 YWRnr+6YcN$2fY1sIq-te_y2DK092aYd;kCd diff --git a/conf/YouTube Download Celery.json b/conf/YouTube Download Celery.json deleted file mode 100644 index 34393887..00000000 --- a/conf/YouTube Download Celery.json +++ /dev/null @@ -1,794 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_CELERY", - "label": "celery", - "description": "", - "type": "datasource", - "pluginId": "influxdb", - "pluginName": "InfluxDB" - } - ], - "__elements": [], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "8.3.1" - }, - { - "type": "datasource", - "id": "influxdb", - "name": "InfluxDB", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "timeseries", - "name": "Time series", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": null, - "iteration": 1644554238421, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "Active", - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "measurement": "active", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT mean(\"active\") FROM \"active\" WHERE $timeFilter GROUP BY time($__interval) ", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "active" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - }, - { - "alias": "$tag_hostname", - "hide": false, - "query": "\nSELECT \nmean(\"active\") AS active\nFROM \"tasks\" WHERE (\"hostname\" =~ /^$hostname$/) AND $timeFilter GROUP BY time($__interval) ,* ORDER BY asc ", - "rawQuery": true, - "refId": "B", - "resultFormat": "time_series" - } - ], - "title": "Active Jobs", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "$col", - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "measurement": "metrics", - "orderByTime": "ASC", - "policy": "default", - "query": "\nSELECT \nmean(\"today_audio_success\")/mean(\"today_audio_request\")*100 as audio_success,\nmean(\"today_video_success\")/mean(\"today_video_request\")*100 as video_success\n\nFROM \"metrics\" WHERE $timeFilter GROUP BY time($__interval), * ORDER BY asc ", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "today_audio_success" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "title": "Video & Audio Success Rate", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "$tag_hostname:$col", - "query": "SELECT mean(\"load1\") AS load1,mean(\"load5\") AS load5,mean(\"load15\") AS load15\nFROM \"tasks\" WHERE (\"hostname\" =~ /^$hostname$/) AND $timeFilter GROUP BY time($__interval) ,* ORDER BY asc \n\n", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series" - } - ], - "title": "Load Average", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "$tag_hostname:$col", - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "measurement": "tasks", - "orderByTime": "ASC", - "policy": "default", - "query": "\nSELECT mean(\"task-succeeded\")/mean(\"task-received\")*100 AS success_rate, mean(\"task-failed\")/mean(\"task-received\")*100 AS fail_rate\n\nFROM \"tasks\" WHERE (\"hostname\" =~ /^$hostname$/) AND $timeFilter GROUP BY time($__interval) ,* ORDER BY asc ", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "task-received" - ], - "type": "field" - } - ] - ], - "tags": [ - { - "key": "hostname", - "operator": "=~", - "value": "/^$hostname$/" - } - ] - } - ], - "title": "Task Rate", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "$tag_hostname:$col", - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "measurement": "tasks", - "orderByTime": "ASC", - "policy": "default", - "query": "\nSELECT mean(\"task-received\") AS received, mean(\"task-started\") AS started,mean(\"task-succeeded\") AS succeeded,mean(\"task-failed\") AS failed\n\nFROM \"tasks\" WHERE (\"hostname\" =~ /^$hostname$/) AND $timeFilter GROUP BY time($__interval) ,* ORDER BY asc ", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "task-received" - ], - "type": "field" - } - ] - ], - "tags": [ - { - "key": "hostname", - "operator": "=~", - "value": "/^$hostname$/" - } - ] - } - ], - "title": "Task Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 5, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "smooth", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 16 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "$col", - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "groupBy": [ - { - "params": [ - "$__interval" - ], - "type": "time" - }, - { - "params": [ - "null" - ], - "type": "fill" - } - ], - "measurement": "metrics", - "orderByTime": "ASC", - "policy": "default", - "query": "SELECT \nmean(\"today_audio_request\") as audio_request,\nmean(\"today_audio_success\") as audio_success,\n\nmean(\"today_bad_request\") as bad_request,\n\nmean(\"today_video_request\") as video_request,\nmean(\"today_video_success\") as video_success\nFROM \"metrics\" WHERE $timeFilter GROUP BY time($__interval), * ORDER BY asc ", - "rawQuery": true, - "refId": "A", - "resultFormat": "time_series", - "select": [ - [ - { - "params": [ - "today_audio_success" - ], - "type": "field" - }, - { - "params": [], - "type": "mean" - } - ] - ], - "tags": [] - } - ], - "title": "Video & Audio", - "type": "timeseries" - } - ], - "refresh": "", - "schemaVersion": 33, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": {}, - "datasource": { - "type": "influxdb", - "uid": "${DS_CELERY}" - }, - "definition": "show tag values with KEY=\"hostname\"", - "hide": 0, - "includeAll": true, - "label": "hostname", - "multi": true, - "name": "hostname", - "options": [], - "query": "show tag values with KEY=\"hostname\"", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "type": "query" - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "YouTube Download Celery", - "uid": "9yXGmc1nk", - "version": 14, - "weekStart": "" -} \ No newline at end of file diff --git a/conf/supervisor_main.conf b/conf/supervisor_main.conf deleted file mode 100644 index dbae1442..00000000 --- a/conf/supervisor_main.conf +++ /dev/null @@ -1,34 +0,0 @@ -[supervisord] -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -user=root - - -[program:vnstat] -command=vnstatd -n -autorestart=true - - -[program:ytdl] -directory=/ytdlbot/ytdlbot/ -command=python ytdl_bot.py -autorestart=true -priority=900 -stopasgroup=true -startsecs = 30 -startretries = 2 - -redirect_stderr=true -stdout_logfile_maxbytes = 50MB -stdout_logfile_backups = 2 -stdout_logfile = /var/log/ytdl.log - -[program:log] -command=tail -f /var/log/ytdl.log -autorestart=true -priority=999 - -redirect_stderr=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 diff --git a/conf/supervisor_worker.conf b/conf/supervisor_worker.conf deleted file mode 100644 index 5ca84301..00000000 --- a/conf/supervisor_worker.conf +++ /dev/null @@ -1,33 +0,0 @@ -[supervisord] -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -user=root - - -[program:vnstat] -command=vnstatd -n -autorestart=true - -[program:worker] -directory=/ytdlbot/ytdlbot/ -command=python tasks.py -autorestart=true -priority=900 -stopasgroup=true -startsecs = 5 -startretries = 100 - -redirect_stderr=true -stdout_logfile_maxbytes = 50MB -stdout_logfile_backups = 2 -stdout_logfile = /var/log/ytdl.log - -[program:log] -command=tail -f /var/log/ytdl.log -autorestart=true -priority=999 - -redirect_stderr=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 diff --git a/docker-compose.yml b/docker-compose.yml index e0b6ec13..47320511 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,6 @@ version: '3.1' services: - socat: - image: bennythink/socat - restart: always - volumes: - - /var/run/docker.sock:/var/run/docker.sock - entrypoint: [ "socat", "tcp-listen:2375,fork,reuseaddr","unix-connect:/var/run/docker.sock" ] - redis: image: redis:7-alpine restart: always @@ -28,19 +21,9 @@ services: ytdl: image: bennythink/ytdlbot env_file: - - env/ytdl.env - restart: always - depends_on: - - socat - - redis - volumes: - - ./data/vnstat/:/var/lib/vnstat/ - - premium: - image: bennythink/ytdlbot - env_file: - - env/ytdl.env + - .env restart: always volumes: - - ./data/premium.session:/ytdlbot/ytdlbot/premium.session - command: [ "/usr/local/bin/python", "/ytdlbot/ytdlbot/premium.py" ] + - ./youtube-cookies.txt:/app/youtube-cookies.txt + depends_on: + - redis \ No newline at end of file diff --git a/k8s.md b/k8s.md deleted file mode 100644 index 61b0146b..00000000 --- a/k8s.md +++ /dev/null @@ -1,200 +0,0 @@ -## Kubernetes - -Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of -containerized applications - -# Complete deployment guide for k8s deloyment - -* contains every functionality -* compatible with amd64, arm64 and armv7l - -## First. Get all file in k8s folder - -Download `k8s` file to a directory on your k8s server and go to this folder - -## 1. Create Redis deloyment - -```shell -kubectl apply -f 01.redis.yml -``` - -This command will create ytdl namespace, redis pod and redis service - -## 2. Creat MariaDB deloyment - -```shell -kubectl apply -f 02.mariadb.yml -``` - -This deloyment will claim 10GB storage from storageClassName: longhorn. Please replace longhorn with your -storageClassName before apply. - -## 3. Set environment variables - -Create configMap for env - -### 3.1 Edit configmap.yml - -```shell -vim 03.configmap.yml -``` - -you can configure all the following environment variables: - -* PYRO_WORKERS: number of workers for pyrogram, default is 100 -* WORKERS: workers count for celery -* APP_ID: **REQUIRED**, get it from https://core.telegram.org/ -* APP_HASH: **REQUIRED** -* TOKEN: **REQUIRED** -* REDIS: **REQUIRED if you need VIP mode and cache** โš ๏ธ Don't publish your redis server on the internet. โš ๏ธ - -* OWNER: owner username -* QUOTA: quota in bytes -* EX: quota expire time -* MULTIPLY: vip quota comparing to normal quota -* USD2CNY: exchange rate -* VIP: VIP mode, default: disable -* AFD_LINK -* COFFEE_LINK -* COFFEE_TOKEN -* AFD_TOKEN -* AFD_USER_ID - -* AUTHORIZED_USER: users that could use this bot, user_id, separated with `,` -* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot. Could be use with - above `AUTHORIZED_USER` - -* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations. -* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream -* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode -* MYSQL_USER -* MYSQL_PASS -* GOOGLE_API_KEY: YouTube API key, required for YouTube video subscription. -* AUDIO_FORMAT: audio format, default is m4a. You can set to any known and supported format for ffmpeg. For - example,`mp3`, `flac`, etc. โš ๏ธ m4a is the fastest. Other formats may affect performance. -* ARCHIVE_ID: group or channel id/username. All downloads will send to this group first and then forward to end user. - **Inline button will be lost during the forwarding.** - -### 3.2 Apply configMap for environment variables - -```shell -kubectl apply -f 03.configmap.yml -``` - -## 4. Run Master Celery - -```shell -kubectl apply -f 04.ytdl-master.yml -``` - -This deloyment will create ytdl-pvc PersistentVolumeClaim on storageClassName: longhorn. This clain will contain vnstat, -cookies folder and flower database. Please replace longhorn with your storageClassName before apply - -### 4.1 Setup instagram cookies - -Required if you want to support instagram. - -You can use this extension -[Get cookies.txt](https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid) -to get instagram cookies - -Get pod running ytdl master: - -```shell -kubectl get pods --namespace ytdl -``` - -Name should be ytdl-xxxxxxxx - -Access to pod - -```shell -kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh -``` - -(replace ytdl-xxx by your pod name) - -Go to ytdl-pvc mounted folder - -```shell -cd /ytdlbot/ytdlbot/data/ -vim instagram.com_cookies.txt -# paste your cookies -``` - -## 5. Run Worker Celery - -```shell -kubectl apply -f 05.ytdl-worker.yml -``` - -## 6. Run Flower image (OPTIONAL) - -### 6.1 Setup flower db - -Get pod running ytdl master: - -```shell -kubectl get pods --namespace ytdl -``` - -Name should be ytdl-xxxxxxxx - -Access to pod - -```shell -kubectl --namespace=ytdl exec --stdin --tty ytdl-xxx -- sh -``` - -(replace ytdl-xxx by your pod name) - -Go to ytdl-pvc mounted folder - -```shel -cd /var/lib/vnstat/ -``` - -Create flower database file - -```shell -{} ~ python3 -Python 3.9.9 (main, Nov 21 2021, 03:22:47) -[Clang 12.0.0 (clang-1200.0.32.29)] on darwin -Type "help", "copyright", "credits" or "license" for more information. ->>> import dbm;dbm.open("flower","n");exit() -``` - -### 6.2 Config Flower Ingress - -This step need config ingress from line 51 of file 06.flower.yml with your ingress service. Need for access from -internet. -YML file should be adjusted depending on your load balancing, ingress and network system - -For active SSL - -```yml -cert-manager.io/cluster-issuer: letsencrypt-prod -``` - -Replace nginx by your ingress service - -```yml -ingressClassName: nginx -``` - -Add your domain, example - -```yml -tls: - - hosts: - - flower.benny.com - secretName: flower-tls - rules: - - host: flower.benny.com -``` - -### 6.3 Apply Flower deloyment - -```shell -kubectl apply -f 06.flower.yml -``` diff --git a/k8s/01.redis.yml b/k8s/01.redis.yml deleted file mode 100644 index da52fc3c..00000000 --- a/k8s/01.redis.yml +++ /dev/null @@ -1,53 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: ytdl - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - labels: - ytdl: redis - name: redis - namespace: ytdl -spec: - replicas: 1 - selector: - matchLabels: - ytdl: redis - strategy: {} - template: - metadata: - creationTimestamp: null - labels: - ytdl: redis - spec: - containers: - - image: redis:7-alpine - name: redis - ports: - - containerPort: 6379 - resources: {} - restartPolicy: Always -status: {} - ---- -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - ytdl: redis - name: redis - namespace: ytdl -spec: - ports: - - name: "6379" - port: 6379 - targetPort: 6379 - selector: - ytdl: redis -status: - loadBalancer: {} \ No newline at end of file diff --git a/k8s/02.mariadb.yml b/k8s/02.mariadb.yml deleted file mode 100644 index c89dbc75..00000000 --- a/k8s/02.mariadb.yml +++ /dev/null @@ -1,80 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - ytdl: mariadb-pvc - name: mariadb-pvc - namespace: ytdl -spec: - accessModes: - - ReadWriteOnce - storageClassName: longhorn - resources: - requests: - storage: 10Gi -status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - creationTimestamp: null - labels: - ytdl: mariadb - name: mariadb - namespace: ytdl -spec: - replicas: 1 - selector: - matchLabels: - ytdl: mariadb - strategy: - type: Recreate - template: - metadata: - creationTimestamp: null - labels: - ytdl: mariadb - spec: - containers: - - env: - - name: MYSQL_ROOT_PASSWORD - value: ro0tP4sSworD - - name: MYSQL_DATABASE - value: ytdl - image: mariadb:latest - name: mariadb - ports: - - containerPort: 3306 - resources: {} - volumeMounts: - - mountPath: /var/lib/mysql - name: "mariadb-persistent-storage" - restartPolicy: Always - volumes: - - name: mariadb-persistent-storage - persistentVolumeClaim: - claimName: mariadb-pvc -status: {} - ---- -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - ytdl: mariadb - name: mariadb-svc - namespace: ytdl -spec: - ports: - - name: "3306" - port: 3306 - targetPort: 3306 - selector: - ytdl: mariadb -status: - loadBalancer: {} - diff --git a/k8s/03.configmap.yml b/k8s/03.configmap.yml deleted file mode 100644 index 90ec84a8..00000000 --- a/k8s/03.configmap.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: ytdlenv - namespace: ytdl - annotations: -data: - APP_HASH: - APP_ID: - TOKEN: - ARCHIVE_ID: - ENABLE_CELERY: 'True' - ENABLE_FFMPEG: 'True' - MYSQL_HOST: mariadb-svc - MYSQL_PASS: ro0tP4sSworD - MYSQL_USER: root - REDIS: redis \ No newline at end of file diff --git a/k8s/04.ytdl-master.yml b/k8s/04.ytdl-master.yml deleted file mode 100644 index f17579f6..00000000 --- a/k8s/04.ytdl-master.yml +++ /dev/null @@ -1,65 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ytdl-pvc - namespace: ytdl - creationTimestamp: null - labels: - ytdl: ytdl-pvc -spec: - accessModes: - - ReadWriteMany - storageClassName: longhorn - resources: - requests: - storage: 10Gi -status: {} - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ytdl - namespace: ytdl - creationTimestamp: null - labels: - ytdl: ytdl -spec: - replicas: 1 - selector: - matchLabels: - ytdl: ytdl - template: - metadata: - creationTimestamp: null - labels: - ytdl: ytdl - spec: - volumes: - - name: ytdl-pvc - persistentVolumeClaim: - claimName: ytdl-pvc - containers: - - name: ytdl - image: bennythink/ytdlbot - envFrom: - - configMapRef: - name: ytdlenv - resources: {} - volumeMounts: - - name: ytdl-pvc - mountPath: /var/lib/vnstat/ - subPath: vnstat/ - - name: ytdl-pvc - mountPath: /ytdlbot/ytdlbot/data/ - subPath: data/ - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - imagePullPolicy: Always - restartPolicy: Always - terminationGracePeriodSeconds: 30 - dnsPolicy: ClusterFirst - securityContext: {} - schedulerName: default-scheduler -status: {} diff --git a/k8s/05.ytdl-worker.yml b/k8s/05.ytdl-worker.yml deleted file mode 100644 index ca154655..00000000 --- a/k8s/05.ytdl-worker.yml +++ /dev/null @@ -1,47 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - labels: - ytdl: ytdl-worker - name: ytdl-worker - namespace: ytdl -spec: - replicas: 4 - selector: - matchLabels: - ytdl: ytdl-worker - template: - metadata: - creationTimestamp: null - labels: - ytdl: ytdl-worker - spec: - volumes: - - name: ytdl-pvc - persistentVolumeClaim: - claimName: ytdl-pvc - containers: - - name: ytdl-worker - image: bennythink/ytdlbot - args: - - /usr/local/bin/supervisord - - '-c' - - /ytdlbot/conf/supervisor_worker.conf - envFrom: - - configMapRef: - name: ytdlenv - resources: {} - volumeMounts: - - name: ytdl-pvc - mountPath: /ytdlbot/ytdlbot/data/ - subPath: data/ - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - imagePullPolicy: Always - restartPolicy: Always - terminationGracePeriodSeconds: 30 - dnsPolicy: ClusterFirst - securityContext: {} - schedulerName: default-scheduler -status: {} diff --git a/k8s/06.flower.yml b/k8s/06.flower.yml deleted file mode 100644 index e7c01c73..00000000 --- a/k8s/06.flower.yml +++ /dev/null @@ -1,101 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - creationTimestamp: null - labels: - ytdl: flower - name: flower - namespace: ytdl -spec: - replicas: 1 - selector: - matchLabels: - ytdl: flower - strategy: - type: Recreate - template: - metadata: - creationTimestamp: null - labels: - ytdl: flower - spec: - containers: - - envFrom: - - configMapRef: - name: ytdlenv - args: - - /usr/local/bin/celery - - -A - - flower_tasks - - flower - - --basic_auth=bennythink:123456 - - --address=0.0.0.0 - - --persistent - - --purge_offline_workers=3600 - image: bennythink/ytdlbot - name: flower - ports: - - containerPort: 5555 - resources: {} - volumeMounts: - - name: ytdl-pvc - mountPath: /ytdlbot/ytdlbot/flower - subPath: vnstat/flower - restartPolicy: Always - volumes: - - name: ytdl-pvc - persistentVolumeClaim: - claimName: ytdl-pvc -status: {} - -# THIS IS OPTION IF YOU WANT PUBLIC FLOWER PAGE TO INTERNET. -# should be adjusted depending on your load balancing system machine ---- -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - ytdl: flower - name: flower-svc - namespace: ytdl -spec: - type: NodePort - ports: - - name: "5555" - protocol: TCP - port: 5555 - targetPort: 5555 - selector: - ytdl: flower -status: - loadBalancer: {} - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: nginx-flower-ingress - namespace: ytdl - annotations: - # cert-manager.io/cluster-issuer: letsencrypt-prod - nginx.ingress.kubernetes.io/rewrite-target: / - # nginx.ingress.kubernetes.io/whitelist-source-range: 14.161.27.151 limit by ipaddresss - -spec: - ingressClassName: nginx - tls: - - hosts: - - your-domain - secretName: flower-tls - rules: - - host: your-domain - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: flower-svc - port: - number: 5555 \ No newline at end of file diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 00000000..46692ece --- /dev/null +++ b/pdm.lock @@ -0,0 +1,843 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:a7c051a17c96e18dbeb071ab2c3ae06ece5e873dcda49778dce65ae94a1a82d5" + +[[metadata.targets]] +requires_python = ">3.9" + +[[package]] +name = "apscheduler" +version = "3.11.0" +requires_python = ">=3.8" +summary = "In-process task scheduler with Cron-like capabilities" +groups = ["default"] +dependencies = [ + "backports-zoneinfo; python_version < \"3.9\"", + "tzlocal>=3.0", +] +files = [ + {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, + {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +requires_python = ">=3.8" +summary = "Timeout context manager for asyncio programs" +groups = ["default"] +marker = "python_full_version < \"3.11.3\"" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +requires_python = ">=3.6.0" +summary = "Screen-scraping library" +groups = ["default"] +dependencies = [ + "soupsieve>1.2", +] +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[[package]] +name = "black" +version = "24.10.0" +requires_python = ">=3.9" +summary = "The uncompromising code formatter." +groups = ["default"] +dependencies = [ + "click>=8.0.0", + "mypy-extensions>=0.4.3", + "packaging>=22.0", + "pathspec>=0.9.0", + "platformdirs>=2", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.0.1; python_version < \"3.11\"", +] +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["default"] +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +requires_python = ">=3.8" +summary = "Foreign Function Interface for Python calling C code." +groups = ["default"] +marker = "platform_python_implementation != \"PyPy\"" +dependencies = [ + "pycparser", +] +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +groups = ["default"] +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +groups = ["default"] +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["default"] +marker = "platform_system == \"Windows\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cryptography" +version = "43.0.3" +requires_python = ">=3.7" +summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +groups = ["default"] +dependencies = [ + "cffi>=1.12; platform_python_implementation != \"PyPy\"", +] +files = [ + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, +] + +[[package]] +name = "fakeredis" +version = "2.26.2" +requires_python = "<4.0,>=3.7" +summary = "Python implementation of redis API, can be used for testing purposes." +groups = ["default"] +dependencies = [ + "redis>=4.3; python_full_version > \"3.8.0\"", + "redis>=4; python_version < \"3.8\"", + "sortedcontainers<3,>=2", + "typing-extensions<5.0,>=4.7; python_version < \"3.11\"", +] +files = [ + {file = "fakeredis-2.26.2-py3-none-any.whl", hash = "sha256:86d4129df001efc25793cb334008160fccc98425d9f94de47884a92b63988c14"}, + {file = "fakeredis-2.26.2.tar.gz", hash = "sha256:3ee5003a314954032b96b1365290541346c9cc24aab071b52cc983bb99ecafbf"}, +] + +[[package]] +name = "ffmpeg-python" +version = "0.2.0" +summary = "Python bindings for FFmpeg - with complex filtering support" +groups = ["default"] +dependencies = [ + "future", +] +files = [ + {file = "ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127"}, + {file = "ffmpeg_python-0.2.0-py3-none-any.whl", hash = "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5"}, +] + +[[package]] +name = "ffpb" +version = "0.4.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "A progress bar for ffmpeg. Yay !" +groups = ["default"] +dependencies = [ + "tqdm~=4.25", +] +files = [ + {file = "ffpb-0.4.1-py2.py3-none-any.whl", hash = "sha256:0e3e2962f4812e39f29649f09785e7cd877ea7f0e14e84d17918c33618647321"}, + {file = "ffpb-0.4.1.tar.gz", hash = "sha256:ede56a6cba4c1d2d6c070daf612e1c4edc957679e49c6b4423cd7dd159577e59"}, +] + +[[package]] +name = "filetype" +version = "1.2.0" +summary = "Infer file type and MIME type of any file/buffer. No external dependencies." +groups = ["default"] +files = [ + {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, + {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, +] + +[[package]] +name = "future" +version = "1.0.0" +requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Clean single-source support for Python 3 and 2" +groups = ["default"] +files = [ + {file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"}, + {file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"}, +] + +[[package]] +name = "greenlet" +version = "3.1.1" +requires_python = ">=3.7" +summary = "Lightweight in-process concurrent programming" +groups = ["default"] +marker = "(platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"" +files = [ + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +] + +[[package]] +name = "idna" +version = "3.10" +requires_python = ">=3.6" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["default"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[[package]] +name = "kurigram" +version = "2.1.36" +requires_python = "~=3.8" +summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" +groups = ["default"] +dependencies = [ + "pyaes==1.6.1", + "pysocks==1.7.1", +] +files = [ + {file = "Kurigram-2.1.36-py3-none-any.whl", hash = "sha256:04bacdecfdf9cb91c3698f66b2ab9c5cfcf6b7b7cf30c5b84066b54612e40a75"}, + {file = "kurigram-2.1.36.tar.gz", hash = "sha256:341815a38b71c75c8fe76e3d55b079fea6477548aa58459dd4d984512f467ba2"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["default"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.2" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +groups = ["default"] +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +requires_python = ">=3.8" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["default"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["default"] +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[[package]] +name = "psutil" +version = "6.1.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +summary = "Cross-platform lib for process and system monitoring in Python." +groups = ["default"] +files = [ + {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, + {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, + {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, + {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, + {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, + {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, +] + +[[package]] +name = "pyaes" +version = "1.6.1" +summary = "Pure-Python Implementation of the AES block-cipher and common modes of operation" +groups = ["default"] +files = [ + {file = "pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +requires_python = ">=3.8" +summary = "C parser in Python" +groups = ["default"] +marker = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pymysql" +version = "1.1.1" +requires_python = ">=3.7" +summary = "Pure Python MySQL Driver" +groups = ["default"] +files = [ + {file = "PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c"}, + {file = "pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0"}, +] + +[[package]] +name = "pysocks" +version = "1.7.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +groups = ["default"] +files = [ + {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, + {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, +] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +requires_python = ">=3.8" +summary = "Read key-value pairs from a .env file and set them as environment variables" +groups = ["default"] +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[[package]] +name = "redis" +version = "5.2.1" +requires_python = ">=3.8" +summary = "Python client for Redis database and key-value store" +groups = ["default"] +dependencies = [ + "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", +] +files = [ + {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, + {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +requires_python = ">=3.8" +summary = "Python HTTP for Humans." +groups = ["default"] +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +summary = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +groups = ["default"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "soupsieve" +version = "2.6" +requires_python = ">=3.8" +summary = "A modern CSS selector implementation for Beautiful Soup." +groups = ["default"] +files = [ + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.37" +requires_python = ">=3.7" +summary = "Database Abstraction Library" +groups = ["default"] +dependencies = [ + "greenlet!=0.4.17; (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") and python_version < \"3.14\"", + "importlib-metadata; python_version < \"3.8\"", + "typing-extensions>=4.6.0", +] +files = [ + {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72"}, + {file = "SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989"}, + {file = "SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761"}, + {file = "SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2"}, + {file = "SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278"}, + {file = "SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b"}, + {file = "SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1"}, + {file = "sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb"}, +] + +[[package]] +name = "tgcrypto" +version = "1.2.5" +requires_python = "~=3.7" +summary = "Fast and Portable Cryptography Extension Library for Pyrogram" +groups = ["default"] +files = [ + {file = "TgCrypto-1.2.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4507102377002966f35f2481830b7529e00c9bbff8c7d1e09634f984af801675"}, + {file = "TgCrypto-1.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:38fe25c0d79b41d7a89caba2a78dea0358e17ca73b033cefd16abed680685829"}, + {file = "TgCrypto-1.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c035bf8ef89846f67e77e82ea85c089b6ea30631b32e8ac1a6511b9be52ab065"}, + {file = "TgCrypto-1.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f594e2680daf20dbac6bf56862f567ddc3cc8d6a19757ed07faa8320ff7acee4"}, + {file = "TgCrypto-1.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8723a16076e229ffdf537fdb5e638227d10f44ca43e6939db1eab524de6eaed7"}, + {file = "TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1c8d974b8b2d7132364b6f0f6712b92bfe47ab9c5dcee25c70327ff68d22d95"}, + {file = "TgCrypto-1.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89d9c143a1fcdb2562a4aa887152abbe9253e1979d7bebef2b489148e0bbe086"}, + {file = "TgCrypto-1.2.5-cp310-cp310-win32.whl", hash = "sha256:aa4bc1d11d4a90811c162abd45a5981f171679d1b5bd0322cd7ccd16447366a2"}, + {file = "TgCrypto-1.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:39145103614c5e38fe938549742d355920f4a0778fa8259eb69c0c85ba4b1d28"}, + {file = "TgCrypto-1.2.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59597cdb1c87eb1184088563d20b42a8f2e431e9334fed64926079044ad2a4af"}, + {file = "TgCrypto-1.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1283337ae75b02406dd700377b8b783e70033b548492517df6e6c4156b0ed69c"}, + {file = "TgCrypto-1.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1735437df0023a40e5fdd95e6b09ce806ec8f2cd2f8879023818840dfae60cab"}, + {file = "TgCrypto-1.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfa17a20206532c6d2442c9d7a7f6434120bd75896ad9a3e9b9277477afa084f"}, + {file = "TgCrypto-1.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48da3674474839e5619e7430ff1f98aed9f55369f3cfaef7f65511852869572e"}, + {file = "TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b49e982e5b156be821a5235bd9102c00dc506a58607e2c8bd50ac872724a951f"}, + {file = "TgCrypto-1.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9d9f13586065a6d86d05c16409054033a84be208acee29b49f6f194e27b08642"}, + {file = "TgCrypto-1.2.5-cp311-cp311-win32.whl", hash = "sha256:10dd3870aecb1a783c6eafd3b164b2149dbc93a9ee13feb7e6f5c58f87c24cd0"}, + {file = "TgCrypto-1.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:a1beec47d6af8b509af7cf266e30f7703208076076594714005b42d2c25225b3"}, + {file = "TgCrypto-1.2.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7885a75db09ce8bdba42d2c332085bfe314f232541a729808c7507ffa261ff9a"}, + {file = "TgCrypto-1.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0d28aa317364a5c27317fe97a48267aa1c65c9aaf589909e97489ebe82a714e3"}, + {file = "TgCrypto-1.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:940974e19044dc65bcf7b9c5255173b896dff010142f3833047dc55d59cde21c"}, + {file = "TgCrypto-1.2.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457c657dd10ffb4bbbb007132a0f6a7bee5080176a98c51f285fedf636b624cb"}, + {file = "TgCrypto-1.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:539bdc6b9239fb6a6b134591a998dc7f50d4dcc4fed861f80540682acc0c3802"}, + {file = "TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d70d5517d64ca952896b726d22c8a66594e6f6259ee2cb4fa134c02d0e8c3e0"}, + {file = "TgCrypto-1.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90b6337d3ae4348ed14f89dd2ebf7011fa63d67a48c8a98d955a1e392176c60a"}, + {file = "TgCrypto-1.2.5-cp39-cp39-win32.whl", hash = "sha256:37c4b9be82716fbc6d2b123caef448eee28683888803db075d842327766f7624"}, + {file = "TgCrypto-1.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:6e96b3a478fae977228c5750194c20a18cde402bbbea6593de424f84d4a8893b"}, + {file = "TgCrypto-1.2.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0a088ff2e05b6bbe891da936f62b99bd85202b2b9f4f57f71a408490dd518c"}, + {file = "TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f245895c7d518342089d15b5dca3cee9ffa5a0f3534db9d5a930f6a27dff4adf"}, + {file = "TgCrypto-1.2.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7dbf607d645c39a577a0f8571039d11ddd2dcdf9656465be75f9e0f540472444"}, + {file = "TgCrypto-1.2.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6b0c2dc84e632ce7b3d0b767cfe20967e557ad7d71ea5dbd7df2dd544323181"}, + {file = "TgCrypto-1.2.5.tar.gz", hash = "sha256:9bc2cac6fb9a12ef5b08f3dd500174fe374d89b660cce981f57e3138559cb682"}, +] + +[[package]] +name = "token-bucket" +version = "0.3.0" +requires_python = ">=3.5" +summary = "Very fast implementation of the token bucket algorithm." +groups = ["default"] +files = [ + {file = "token_bucket-0.3.0-py2.py3-none-any.whl", hash = "sha256:6df24309e3cf5b808ae5ef714a3191ec5b54f48c34ef959e4882eef140703369"}, + {file = "token_bucket-0.3.0.tar.gz", hash = "sha256:979571c99db2ff9e651f2b2146a62b2ebadf7de6c217a8781698282976cb675f"}, +] + +[[package]] +name = "tomli" +version = "2.1.0" +requires_python = ">=3.8" +summary = "A lil' TOML parser" +groups = ["default"] +marker = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +requires_python = ">=3.7" +summary = "Fast, Extensible Progress Meter" +groups = ["default"] +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["default"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +groups = ["default"] +marker = "platform_system == \"Windows\"" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[[package]] +name = "tzlocal" +version = "5.2" +requires_python = ">=3.8" +summary = "tzinfo object for the local timezone" +groups = ["default"] +dependencies = [ + "backports-zoneinfo; python_version < \"3.9\"", + "tzdata; platform_system == \"Windows\"", +] +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["default"] +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[[package]] +name = "yt-dlp" +version = "2025.1.12" +requires_python = ">=3.9" +summary = "A feature-rich command-line audio/video downloader" +groups = ["default"] +files = [ + {file = "yt_dlp-2025.1.12-py3-none-any.whl", hash = "sha256:f7ea19afb64f8e457a1b9598ddb67f8deaa313bf1d57abd5612db9272ab10795"}, + {file = "yt_dlp-2025.1.12.tar.gz", hash = "sha256:8e7e246e2a5a2cff0a9c13db46844a37a547680702012058c94ec18fce0ca25a"}, +] diff --git a/pre-push.py b/pre-push.py new file mode 100755 index 00000000..17f6e49b --- /dev/null +++ b/pre-push.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - pre-commit.py +# for dependabot + +import tomllib +import subprocess + + +with open("pyproject.toml", "rb") as file: + config = tomllib.load(file) + +with open("requirements.txt", "w") as file: + for item in config["project"]["dependencies"]: + if " " in item: + item = item.split()[-1] + file.write(f"{item}\n") + +# commit with amend +subprocess.run(["git", "add", "requirements.txt"]) +subprocess.run(["git", "commit", "--amend", "--no-edit"]) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6cfb9c82 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "ytdlbot" +version = "1.0.0" +description = "Default template for PDM package" +authors = [ + {name = "Benny", email = "benny.think@gmail.com"}, +] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.12", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.1", "redis>=5.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] +requires-python = ">3.9" +readme = "README.md" +license = {text = "Apache2.0"} + + +[tool.pdm] +distribution = false diff --git a/requirements.txt b/requirements.txt index 33afd86b..ae5ffdeb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,29 +1,19 @@ -git+https://github.com/KurimuzonAkuma/pyrogram -tgcrypto==1.2.5 -yt-dlp[default,curl-cffi]==2025.1.12 -APScheduler==3.10.4 -beautifultable==1.1.0 -ffmpeg-python==0.2.0 -PyMySQL==1.1.1 -celery==5.4.0 -filetype==1.2.0 -flower==2.0.1 -psutil==6.0.0 -influxdb==5.3.2 -beautifulsoup4==4.12.3 -fakeredis==2.24.1 -supervisor==4.2.5 -tgbot-ping==1.0.7 -redis==5.0.8 -requests==2.32.3 -tqdm==4.66.5 -requests-toolbelt==1.0.0 -ffpb==0.4.1 -youtube-search-python==1.6.6 -token-bucket==0.3.0 -coloredlogs==15.0.1 -tronpy==0.5.0 -mnemonic==0.21 -qrcode==7.4.2 -blinker==1.8.2 -flask===3.0.3 +tgcrypto>=1.2.5 +yt-dlp==2025.1.12 +APScheduler>=3.11.0 +ffmpeg-python>=0.2.0 +PyMySQL>=1.1.1 +filetype>=1.2.0 +beautifulsoup4>=4.12.3 +fakeredis>=2.26.1 +redis>=5.2.0 +requests>=2.32.3 +tqdm>=4.67.1 +token-bucket>=0.3.0 +python-dotenv>=1.0.1 +black>=24.10.0 +sqlalchemy>=2.0.36 +psutil>=6.1.0 +ffpb>=0.4.1 +kurigram>=2.1.36 +cryptography>=43.0.3 diff --git a/scripts/low_id.sh b/scripts/low_id.sh deleted file mode 100644 index 16fc2cef..00000000 --- a/scripts/low_id.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/opt/bin - -# Check the logs for the given string -if docker-compose logs --tail=100 ytdl | grep -q "The msg_id is too low"; then - # If the string is found, stop the ytdl service - echo "ytdl service stopped due to 'The msg_id is too low' found in logs." - docker-compose stop ytdl && docker-compose rm ytdl && docker-compose up -d - -else - echo "String not found in logs." -fi diff --git a/scripts/migrate_to_mysql.py b/scripts/migrate_to_mysql.py deleted file mode 100644 index 436fb78a..00000000 --- a/scripts/migrate_to_mysql.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - migrate_to_mysql.py -# 12/29/21 15:28 -# - -__author__ = "Benny " - -import sqlite3 - -import pymysql - -mysql_con = pymysql.connect(host='localhost', user='root', passwd='root', db='vip', charset='utf8mb4') -sqlite_con = sqlite3.connect('vip.sqlite') - -vips = sqlite_con.execute('SELECT * FROM VIP').fetchall() - -for vip in vips: - mysql_con.cursor().execute('INSERT INTO vip VALUES (%s, %s, %s, %s, %s, %s)', vip) - -settings = sqlite_con.execute('SELECT * FROM settings').fetchall() - -for setting in settings: - mysql_con.cursor().execute("INSERT INTO settings VALUES (%s,%s,%s)", setting) - -mysql_con.commit() diff --git a/scripts/start.sh b/scripts/start.sh deleted file mode 100644 index 242f207f..00000000 --- a/scripts/start.sh +++ /dev/null @@ -1,13 +0,0 @@ -docker run -d --restart unless-stopped --name ytdl \ - --net host \ - -e TOKEN=12345 \ - -e APP_ID=123123 \ - -e APP_HASH=4990 \ - -e ENABLE_CELERY=True \ - -e REDIS=192.168.6.1 \ - -e MYSQL_HOST=192.168.6.1 \ - -e WORKERS=4 \ - -e VIP=True \ - -e CUSTOM_TEXT=#StandWithUkraine \ - bennythink/ytdlbot \ - /usr/local/bin/supervisord -c "/ytdlbot/conf/supervisor_worker.conf" diff --git a/scripts/transfer.py b/scripts/transfer.py deleted file mode 100644 index 2ace122f..00000000 --- a/scripts/transfer.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 - -# ytdlbot - transfer.py -# 2023-12-07 18:21 -from tronpy import Tron -from tronpy.hdwallet import seed_from_mnemonic, key_from_seed -from tronpy.keys import PrivateKey - -mnemonic = "web horse smile ramp olive slush blue property world physical donkey pumpkin" - -client = Tron(network="nile") - -from_ = client.generate_address_from_mnemonic(mnemonic, account_path="m/44'/195'/0'/0/0")["base58check_address"] -balance = client.get_account_balance(from_) -print("my addr: ", from_, "balance: ", balance) -to = input("to: ") -amount = int(input("amount in TRX: ")) - - -def mnemonic_to_private_key(): - seed = seed_from_mnemonic(mnemonic, passphrase="") - private_key = key_from_seed(seed, account_path="m/44'/195'/0'/0/0") - return PrivateKey(private_key) - - -t = client.trx.transfer(from_, to, amount * 1_000_000).build().sign(mnemonic_to_private_key()).broadcast() - -print(t.wait()) diff --git a/src/config/__init__.py b/src/config/__init__.py new file mode 100644 index 00000000..5dcf0ac9 --- /dev/null +++ b/src/config/__init__.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - __init__.py.py + +import logging + +from dotenv import load_dotenv + +load_dotenv() + +from config.config import * +from config.constant import * + +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s %(filename)s:%(lineno)d %(levelname).1s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) diff --git a/src/config/config.py b/src/config/config.py new file mode 100644 index 00000000..fb996a4e --- /dev/null +++ b/src/config/config.py @@ -0,0 +1,40 @@ +#!/usr/local/bin/python3 +# coding: utf-8 + +# ytdlbot - config.py +# 8/28/21 15:01 +# + +__author__ = "Benny " + +import os + +# general settings +WORKERS: int = int(os.getenv("WORKERS", 100)) +APP_ID: int = int(os.getenv("APP_ID")) +APP_HASH = os.getenv("APP_HASH") +BOT_TOKEN = os.getenv("BOT_TOKEN") +OWNER = [int(i) for i in os.getenv("OWNER").split(",")] +# db settings +AUTHORIZED_USER: str = os.getenv("AUTHORIZED_USER", "") +DB_DSN = os.getenv("DB_DSN") +REDIS_HOST = os.getenv("REDIS_HOST") +ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) +AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") +ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) +RCLONE_PATH = os.getenv("RCLONE") + +# payment settings +ENABLE_VIP = os.getenv("ENABLE_VIP", False) +PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") +FREE_DOWNLOAD = int(os.getenv("FREE_DOWNLOAD", 3)) +TOKEN_PRICE = os.getenv("TOKEN_PRICE", 10) # 1 USD=10 downloads + +# For advance users +# Please do not change, if you don't know what these are. +TG_NORMAL_MAX_SIZE = 2000 * 1024 * 1024 +CAPTION_URL_LENGTH_LIMIT = 150 + +# This will set the value for the tmpfile path(engine path). If not, will return None and use systemโ€™s default path. +# Please ensure that the directory exists and you have necessary permissions to write to it. +TMPFILE_PATH = os.getenv("TMPFILE_PATH") diff --git a/src/config/constant.py b/src/config/constant.py new file mode 100644 index 00000000..1bf5d787 --- /dev/null +++ b/src/config/constant.py @@ -0,0 +1,52 @@ +#!/usr/local/bin/python3 +# coding: utf-8 + +# ytdlbot - constant.py +# 8/16/21 16:59 +# + +__author__ = "Benny " + +import typing + +from pyrogram import Client, types + + +class BotText: + + start = """ + Welcome to YouTube Download bot. Type /help for more information. + EU๐Ÿ‡ช๐Ÿ‡บ: @benny_2ytdlbot + SG๐Ÿ‡ธ๐Ÿ‡ฌ๏ผš@benny_ytdlbot + + Join https://t.me/+OGRC8tp9-U9mZDZl for updates.\n\n""" + + help = """ +1. For YouTube and any websites supported by yt-dlp, just send the link and we will engine and send it to you. + +2. For specific links use `/spdl {URL}`. More info at https://github.com/SanujaNS/ytdlbot-telegram#supported-websites + +3. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. + +4. Want to deploy it yourself?\nHere's the source code: https://github.com/tgbot-collection/ytdlbot + """ + + about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" + + settings = """ +Please choose the preferred format and video quality for your video. These settings only **apply to YouTube videos**. +High: 1080P +Medium: 720P +Low: 480P + +If you choose to send the video as a document, Telegram client will not be able stream it. + +Your current settings: +Video quality: {} +Sending type: {} +""" + + +class Types: + Message = typing.Union[types.Message, typing.Coroutine] + Client = Client diff --git a/src/database/__init__.py b/src/database/__init__.py new file mode 100644 index 00000000..77daf4c9 --- /dev/null +++ b/src/database/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - __init__.py.py + +from database.cache import Redis diff --git a/src/database/cache.py b/src/database/cache.py new file mode 100644 index 00000000..5fca7a0c --- /dev/null +++ b/src/database/cache.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - cache.py + + +import logging + +import fakeredis +import redis + +from config import REDIS_HOST + + +class Redis: + def __init__(self): + try: + self.r = redis.StrictRedis(host=REDIS_HOST, db=1, decode_responses=True) + self.r.ping() + except Exception: + logging.warning("Redis connection failed, using fake redis instead.") + self.r = fakeredis.FakeStrictRedis(host=REDIS_HOST, db=1, decode_responses=True) + + def __del__(self): + self.r.close() + + def add_send_cache(self, link: str, file_id: str, _type: str): + # one link might have multiple files, so we use hset + self.r.hset(link, mapping={"file_id": file_id, "type": _type}) + + def get_send_cache(self, link: str): + return self.r.hgetall(link) diff --git a/src/database/model.py b/src/database/model.py new file mode 100644 index 00000000..7db10ff1 --- /dev/null +++ b/src/database/model.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# coding: utf-8 +import math +import os +from contextlib import contextmanager +from typing import Literal + +from sqlalchemy import Column, Enum, Float, ForeignKey, Integer, String, create_engine +from sqlalchemy.dialects.mysql import JSON +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship, sessionmaker + +from config import FREE_DOWNLOAD + +# ytdlbot - model.py + + +Base = declarative_base() + + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, autoincrement=True) + user_id = Column(Integer, unique=True, nullable=False) # telegram user id + free = Column(Integer, default=FREE_DOWNLOAD) + paid = Column(Integer, default=0) + config = Column(JSON) + + settings = relationship("Setting", back_populates="user", cascade="all, delete-orphan", uselist=False) + payments = relationship("Payment", back_populates="user", cascade="all, delete-orphan") + + +class Setting(Base): + __tablename__ = "settings" + + id = Column(Integer, primary_key=True, autoincrement=True) + quality = Column(Enum("high", "medium", "low", "audio", "custom"), nullable=False, default="high") + format = Column(Enum("video", "audio", "document"), nullable=False, default="video") + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + + user = relationship("User", back_populates="settings") + + +class Payment(Base): + __tablename__ = "payments" + + id = Column(Integer, primary_key=True, autoincrement=True) + method = Column(String(50), nullable=False) + amount = Column(Float, nullable=False) + status = Column(Enum("pending", "completed", "failed", "refunded"), nullable=False) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + + user = relationship("User", back_populates="payments") + + +def create_session(): + engine = create_engine( + "mysql+pymysql://root:root@localhost/ytdlbot", + pool_size=50, + max_overflow=100, + pool_timeout=30, + pool_recycle=1800, + ) + Base.metadata.create_all(engine) + return sessionmaker(bind=engine) + + +SessionFactory = create_session() + + +@contextmanager +def session_manager(): + s = SessionFactory() + try: + yield s + s.commit() + except Exception as e: + s.rollback() + raise + finally: + s.close() + + +def get_quality_settings(tgid) -> Literal["high", "medium", "low", "audio", "custom"]: + with session_manager() as session: + user = session.query(User).filter(User.user_id == tgid).first() + if user and user.settings: + return user.settings.quality + + return "high" + + +def get_format_settings(tgid) -> Literal["video", "audio", "document"]: + with session_manager() as session: + user = session.query(User).filter(User.user_id == tgid).first() + if user and user.settings: + return user.settings.format + return "video" + + +def set_user_settings(tgid: int, key: str, value: str): + # set quality or format settings + with session_manager() as session: + # find user first + user = session.query(User).filter(User.user_id == tgid).first() + # upsert + setting = session.query(Setting).filter(Setting.user_id == user.id).first() + if setting: + setattr(setting, key, value) + else: + session.add(Setting(user_id=user.id, **{key: value})) + + +def get_free_quota(uid: int): + with session_manager() as session: + data = session.query(User).filter(User.user_id == uid).first() + if data: + return data.free + return FREE_DOWNLOAD + + +def get_paid_quota(uid: int): + if os.getenv("ENABLE_VIP"): + with session_manager() as session: + data = session.query(User).filter(User.user_id == uid).first() + if data: + return data.paid + + return 0 + + return math.inf + + +def reset_free_quota(uid: int): + with session_manager() as session: + data = session.query(User).filter(User.user_id == uid).first() + if data: + data.free = 5 + + +def add_paid_quota(uid: int, amount: int): + with session_manager() as session: + data = session.query(User).filter(User.user_id == uid).first() + if data: + data.paid += amount + + +def use_quota(uid: int): + # use free first, then paid + with session_manager() as session: + user = session.query(User).filter(User.user_id == uid).first() + if user: + if user.free > 0: + user.free -= 1 + elif user.paid > 0: + user.paid -= 1 + else: + raise Exception("Quota exhausted") + + +def init_user(uid: int): + with session_manager() as session: + user = session.query(User).filter(User.user_id == uid).first() + if not user: + session.add(User(user_id=uid)) + + +def reset_free(): + with session_manager() as session: + users = session.query(User).all() + for user in users: + user.free = FREE_DOWNLOAD + session.commit() diff --git a/src/engine/__init__.py b/src/engine/__init__.py new file mode 100644 index 00000000..bda81ef7 --- /dev/null +++ b/src/engine/__init__.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - __init__.py.py + +from urllib.parse import urlparse + +from engine.generic import YoutubeDownload + + +def youtube_entrance(client, bot_message, url): + youtube = YoutubeDownload(client, bot_message, url) + youtube.start() + + +def special_download_entrance(url: str, tempdir: str, bm, **kwargs) -> list: + """Specific link downloader""" + domain = urlparse(url).hostname + if "youtube.com" in domain or "youtu.be" in domain: + raise ValueError("ERROR: This is ytdl bot for Youtube links just send the link.") + elif "www.instagram.com" in domain: + return instagram(url, tempdir, bm, **kwargs) + elif "pixeldrain.com" in domain: + return pixeldrain(url, tempdir, bm, **kwargs) + elif "krakenfiles.com" in domain: + return krakenfiles(url, tempdir, bm, **kwargs) + else: + raise ValueError(f"Invalid URL: No specific link function found for {url}") diff --git a/src/engine/base.py b/src/engine/base.py new file mode 100644 index 00000000..aa5b5d06 --- /dev/null +++ b/src/engine/base.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - types.py + +import logging +import re +import tempfile +import uuid +from abc import ABC, abstractmethod +from io import StringIO +from pathlib import Path +from types import SimpleNamespace + +import ffmpeg +import filetype +from pyrogram import enums, types +from tqdm import tqdm + +from config import TG_NORMAL_MAX_SIZE, Types +from database import Redis +from database.model import ( + get_format_settings, + get_free_quota, + get_paid_quota, + get_quality_settings, + use_quota, +) +from engine.helper import debounce, sizeof_fmt + + +def generate_input_media(file_paths: list, cap: str) -> list: + input_media = [] + for path in file_paths: + mime = filetype.guess_mime(path) + if "video" in mime: + input_media.append(types.InputMediaVideo(media=path)) + elif "image" in mime: + input_media.append(types.InputMediaPhoto(media=path)) + elif "audio" in mime: + input_media.append(types.InputMediaAudio(media=path)) + else: + input_media.append(types.InputMediaDocument(media=path)) + + input_media[0].caption = cap + return input_media + + +class BaseDownloader(ABC): + def __init__(self, client: Types.Client, bot_msg: Types.Message, url: str): + self._client = client + self._url = url + self._user_id = getattr(bot_msg.from_user, "id", None) or bot_msg.chat.id + self._id = bot_msg.id + self._tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") + self._bot_msg: Types.Message = bot_msg + self._redis = Redis() + self._record_usage() + + def __del__(self): + self._tempdir.cleanup() + + def _record_usage(self): + free, paid = get_free_quota(self._user_id), get_paid_quota(self._user_id) + logging.info("User %s has %s free and %s paid quota", self._user_id, free, paid) + if free + paid < 0: + raise Exception("Usage limit exceeded") + + use_quota(self._user_id) + + @staticmethod + def __remove_bash_color(text): + return re.sub(r"\u001b|\[0;94m|\u001b\[0m|\[0;32m|\[0m|\[0;33m", "", text) + + @staticmethod + def __tqdm_progress(desc, total, finished, speed="", eta=""): + def more(title, initial): + if initial: + return f"{title} {initial}" + else: + return "" + + f = StringIO() + tqdm( + total=total, + initial=finished, + file=f, + ascii=False, + unit_scale=True, + ncols=30, + bar_format="{l_bar}{bar} |{n_fmt}/{total_fmt} ", + ) + raw_output = f.getvalue() + tqdm_output = raw_output.split("|") + progress = f"`[{tqdm_output[1]}]`" + detail = tqdm_output[2].replace("[A", "") + text = f""" + {desc} + + {progress} + {detail} + {more("Speed:", speed)} + {more("ETA:", eta)} + """ + f.close() + return text + + def download_hook(self, d: dict): + if d["status"] == "downloading": + downloaded = d.get("downloaded_bytes", 0) + total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) + + if total > TG_NORMAL_MAX_SIZE: + msg = f"Your download file size {sizeof_fmt(total)} is too large for Telegram." + raise Exception(msg) + + # percent = remove_bash_color(d.get("_percent_str", "N/A")) + speed = self.__remove_bash_color(d.get("_speed_str", "N/A")) + eta = self.__remove_bash_color(d.get("_eta_str", d.get("eta"))) + text = self.__tqdm_progress("Downloading...", total, downloaded, speed, eta) + self.edit_text(text) + + def upload_hook(self, current, total): + text = self.__tqdm_progress("Uploading...", total, current) + self.edit_text(text) + + @debounce(5) + def edit_text(self, text: str): + self._bot_msg.edit_text(text) + + def get_cache_fileid(self): + unique = self._url + get_quality_settings(self._url) + return self._redis.get_send_cache(unique) + + @abstractmethod + def _setup_formats(self) -> list | None: + pass + + @abstractmethod + def _download(self, formats) -> list: + # responsible for get format and download it + pass + + @property + def _methods(self): + return { + "document": self._client.send_document, + "audio": self._client.send_audio, + "video": self._client.send_video, + "animation": self._client.send_animation, + "photo": self._client.send_photo, + } + + def send_something(self, *, chat_id, files, _type, caption=None, thumb=None, **kwargs): + self._client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) + if len(files) > 1: + inputs = generate_input_media(files, caption) + return self._client.send_media_group(chat_id, inputs)[0] + else: + return self._methods[_type]( + chat_id, + files[0], + caption=caption, + thumb=thumb, + progress=self.upload_hook, + # progress_args=(self._bot_msg,), + **kwargs, + ) + + def get_metadata(self): + video_path = list(Path(self._tempdir.name).glob("*"))[0] + filename = Path(video_path).name + width = height = duration = 0 + try: + video_streams = ffmpeg.probe(video_path, select_streams="v") + for item in video_streams.get("streams", []): + height = item["height"] + width = item["width"] + duration = int(float(video_streams["format"]["duration"])) + except Exception as e: + logging.error(e) + try: + thumb = Path(video_path).parent.joinpath(f"{uuid.uuid4().hex}-thunmnail.png").as_posix() + # A thumbnail's width and height should not exceed 320 pixels. + ffmpeg.input(video_path, ss=duration / 2).filter( + "scale", + "if(gt(iw,ih),300,-1)", # If width > height, scale width to 320 and height auto + "if(gt(iw,ih),-1,300)", + ).output(thumb, vframes=1).run() + except ffmpeg._run.Error: + thumb = None + + caption = f"{self._url}\n{filename}\n\nResolution: {width}x{height}\nDuration: {duration} seconds" + return dict(height=height, width=width, duration=duration, thumb=thumb, caption=caption) + + def _upload(self): + upload = get_format_settings(self._user_id) + # we only support single file upload + files = list(Path(self._tempdir.name).glob("*")) + meta = self.get_metadata() + + success = SimpleNamespace(document=None, video=None, audio=None, animation=None, photo=None) + if upload == "document": + success = self.send_something( + chat_id=self._user_id, + files=files, + _type="document", + thumb=meta["thumb"], + force_document=True, + caption=meta["caption"], + ) + elif upload == "audio": + success = self.send_something( + chat_id=self._user_id, + files=files, + _type="audio", + caption=meta["caption"], + ) + elif upload == "video": + methods = {"video": meta, "animation": {}, "photo": {}} + for method, thumb in methods.items(): + try: + success = self.send_something(chat_id=self._user_id, files=files, _type=method, **meta) + break + except Exception as e: + logging.error("Retry to send as %s, error:", method, e) + else: + logging.error("Unknown upload settings") + return + + # unique link is link+download_format + unique = self._url + get_quality_settings(self._url) + upload + obj = success.document or success.video or success.audio or success.animation or success.photo + self._redis.add_send_cache(unique, getattr(obj, "file_id", None), upload) + # change progress bar to done + self._bot_msg.edit_text("โœ… Success") + return success + + @abstractmethod + def start(self): + pass diff --git a/src/engine/direct.py b/src/engine/direct.py new file mode 100644 index 00000000..187fb08f --- /dev/null +++ b/src/engine/direct.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - leech.py + +import logging +import os +import pathlib +import subprocess +import tempfile +from pathlib import Path +from uuid import uuid4 + +import filetype +import requests +from base import BaseDownloader +from pyrogram import enums + +from config import ENABLE_ARIA2, TMPFILE_PATH + + +class DirectDownloader(BaseDownloader): + + def _setup_formats(self) -> list | None: + # direct download doesn't need to setup formats + pass + + def _requests_download(self): + response = requests.get(self._url, stream=True) + response.raise_for_status() + file = Path(self._tempdir).joinpath(uuid4().hex) + ext = filetype.guess_extension(file) + if ext is not None: + file = file.with_suffix(ext) + + with open(file, "wb") as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + return [file.as_posix()] + + def _aria2_download(self): + ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + response = None + video_paths = None + # Download process using aria2c + try: + self._bot_msg.edit_text(f"Aria2 download starting...") + # Command to engine the link using aria2c + command = [ + "aria2c", + "-U", + ua, + "--max-tries=5", + "--console-log-level=warn", + "-d", + self._tempdir, + self._url, + ] + # Run the command using subprocess.Popen + process = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + max_iterations = 100 # Set a reasonable maximum number of iterations + iteration = 0 + while process.poll() is None and iteration < max_iterations: + line: str = process.stdout.readline().decode("utf-8") + if line.startswith("[#"): + line = line.strip() + self.edit_text(f"Aria2 downloading... \n\n`{line}`", disable_web_page_preview=True) + break + iteration += 1 + + if iteration >= max_iterations: + self.edit_text("Download exceed max iteration. Please try again later.", disable_web_page_preview=True) + except Exception as e: + self.edit_text(f"Download failed!โŒ\n\n`{e}`", disable_web_page_preview=True) + return + # Get filename and extension correctly after engine + file: Path = next(Path(self._tempdir).glob("*")) + filename = file.name + logging.info("Downloaded file %s", filename) + self.edit_text(f"Download Complete", disable_web_page_preview=True) + ext = filetype.guess_extension(file) + # Rename file if it doesn't have extension + if ext is not None and not filename.endswith(ext): + file.rename(f"{self._tempdir}/{filename}.{ext}") + # Get file path of the downloaded file to upload + return [file.as_posix()] + + def _download(self, formats=None) -> list: + if ENABLE_ARIA2: + return self._aria2_download() + return self._requests_download() + + def start(self): + self._download() + self._upload() diff --git a/src/engine/generic.py b/src/engine/generic.py new file mode 100644 index 00000000..6dba3060 --- /dev/null +++ b/src/engine/generic.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - generic.py + +import os +from pathlib import Path + +import yt_dlp +from pyrogram import types + +from database.model import get_format_settings, get_quality_settings +from engine.base import BaseDownloader + + +class YoutubeDownload(BaseDownloader): + @staticmethod + def get_format(m): + return [ + f"bestvideo[ext=mp4][height={m}]+bestaudio[ext=m4a]", + f"bestvideo[vcodec^=avc][height={m}]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", + ] + + def _setup_formats(self) -> list | None: + quality, format_ = get_quality_settings(self._user_id), get_format_settings(self._user_id) + # quality: high, medium, low, custom + # format: audio, video, document + formats = [] + defaults = [ + # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 + "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", + "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", + None, + ] + maps = { + "high-audio": ["bestaudio[ext=m4a]"], + "high-video": defaults, + "high-document": defaults, + "medium-audio": ["bestaudio[ext=m4a]"], # no mediumaudio :-( + "medium-video": self.get_format(720), + "medium-document": self.get_format(720), + "low-audio": ["bestaudio[ext=m4a]"], + "low-video": self.get_format(480), + "low-document": self.get_format(480), + "custom-audio": "", + "custom-video": "", + "custom-document": "", + } + + if quality == "custom": + pass + # TODO not supported yet + # get format from ytdlp, send inlinekeyboard button to user so they can choose + # another callback will be triggered to download the video + # available_options = { + # "480P": "best[height<=480]", + # "720P": "best[height<=720]", + # "1080P": "best[height<=1080]", + # } + # markup, temp_row = [], [] + # for quality, data in available_options.items(): + # temp_row.append(types.InlineKeyboardButton(quality, callback_data=data)) + # if len(temp_row) == 3: # Add a row every 3 buttons + # markup.append(temp_row) + # temp_row = [] + # # Add any remaining buttons as the last row + # if temp_row: + # markup.append(temp_row) + # self._bot_msg.edit_text("Choose the format", reply_markup=types.InlineKeyboardMarkup(markup)) + # return None + + formats.extend(maps[f"{quality}-{format_}"]) + # extend default formats if not high* + if quality != "high": + formats.extend(defaults) + return formats + + def _download(self, formats) -> list: + output = Path(self._tempdir.name, "%(title).70s.%(ext)s").as_posix() + ydl_opts = { + "progress_hooks": [lambda d: self.download_hook(d)], + "outtmpl": output, + "restrictfilenames": False, + "quiet": True, + } + # setup cookies for youtube only + if self._url.startswith("https://www.youtube.com/") or self._url.startswith("https://youtu.be/"): + if os.path.isfile("youtube-cookies.txt"): + # src/cookies.txt + ydl_opts["cookiefile"] = "youtube-cookies.txt" + + if self._url.startswith("https://drive.google.com"): + # Always use the `source` format for Google Drive URLs. + formats = ["source"] + formats + + files = None + for f in formats: + ydl_opts["format"] = f + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + ydl.download([self._url]) + files = list(Path(self._tempdir.name).glob("*")) + break + + return files + + def start(self, formats=None): + # user can choose format by clicking on the button(custom config) + default_formats = self._setup_formats() + if formats is not None: + # formats according to user choice + default_formats = formats + self._setup_formats() + self._download(default_formats) + self._upload() diff --git a/src/engine/helper.py b/src/engine/helper.py new file mode 100644 index 00000000..a2ca94cd --- /dev/null +++ b/src/engine/helper.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - helper.py + +import functools +import logging +import os +import pathlib +import re +import subprocess +import threading +import time +from http import HTTPStatus +from io import StringIO + +import ffmpeg +import ffpb +import filetype +import pyrogram +import requests +import yt_dlp +from bs4 import BeautifulSoup +from pyrogram import types +from tqdm import tqdm + +from config import ( + AUDIO_FORMAT, + CAPTION_URL_LENGTH_LIMIT, + ENABLE_ARIA2, + TG_NORMAL_MAX_SIZE, +) +from utils import shorten_url, sizeof_fmt + + +def debounce(wait_seconds): + """ + Thread-safe debounce decorator for functions that take a message with chat.id and msg.id attributes. + The function will only be called if it hasn't been called with the same chat.id and msg.id in the last 'wait_seconds'. + """ + + def decorator(func): + last_called = {} + lock = threading.Lock() + + @functools.wraps(func) + def wrapper(*args, **kwargs): + nonlocal last_called + now = time.time() + + # Assuming the first argument is the message object with chat.id and msg.id + bot_msg = args[0]._bot_msg + key = (bot_msg.chat.id, bot_msg.id) + + with lock: + if key not in last_called or now - last_called[key] >= wait_seconds: + last_called[key] = now + return func(*args, **kwargs) + + return wrapper + + return decorator + + +def get_caption(url, video_path): + if isinstance(video_path, pathlib.Path): + meta = get_metadata(video_path) + file_name = video_path.name + file_size = sizeof_fmt(os.stat(video_path).st_size) + else: + file_name = getattr(video_path, "file_name", "") + file_size = sizeof_fmt(getattr(video_path, "file_size", (2 << 2) + ((2 << 2) + 1) + (2 << 5))) + meta = dict( + width=getattr(video_path, "width", 0), + height=getattr(video_path, "height", 0), + duration=getattr(video_path, "duration", 0), + thumb=getattr(video_path, "thumb", None), + ) + + # Shorten the URL if necessary + try: + if len(url) > CAPTION_URL_LENGTH_LIMIT: + url_for_cap = shorten_url(url, CAPTION_URL_LENGTH_LIMIT) + else: + url_for_cap = url + except Exception as e: + logging.warning(f"Error shortening URL: {e}") + url_for_cap = url + + cap = ( + f"{file_name}\n\n{url_for_cap}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" f"{meta['duration']}s\n" + ) + return cap + + +def convert_audio_format(video_paths: list, bm): + # 1. file is audio, default format + # 2. file is video, default format + # 3. non default format + + for path in video_paths: + streams = ffmpeg.probe(path)["streams"] + if AUDIO_FORMAT is None and len(streams) == 1 and streams[0]["codec_type"] == "audio": + logging.info("%s is audio, default format, no need to convert", path) + elif AUDIO_FORMAT is None and len(streams) >= 2: + logging.info("%s is video, default format, need to extract audio", path) + audio_stream = {"codec_name": "m4a"} + for stream in streams: + if stream["codec_type"] == "audio": + audio_stream = stream + break + ext = audio_stream["codec_name"] + new_path = path.with_suffix(f".{ext}") + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) + path.unlink() + index = video_paths.index(path) + video_paths[index] = new_path + else: + logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) + new_path = path.with_suffix(f".{AUDIO_FORMAT}") + run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_path], bm) + path.unlink() + index = video_paths.index(path) + video_paths[index] = new_path + + +def split_large_video(video_paths: list): + original_video = None + split = False + for original_video in video_paths: + size = os.stat(original_video).st_size + if size > TG_NORMAL_MAX_SIZE: + split = True + logging.warning("file is too large %s, splitting...", size) + subprocess.check_output(f"sh split-video.sh {original_video} {TG_NORMAL_MAX_SIZE * 0.95} ".split()) + os.remove(original_video) + + if split and original_video: + return [i for i in pathlib.Path(original_video).parent.glob("*")] diff --git a/src/engine/instagram.py b/src/engine/instagram.py new file mode 100644 index 00000000..3921a7f2 --- /dev/null +++ b/src/engine/instagram.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - instagram.py + + +import pathlib +import re + +import filetype +import requests +from base import BaseDownloader + + +class InstagramDownload(BaseDownloader): + def extract_code(self): + # Regular expression patterns + patterns = [ + # Instagram stories highlights + r"/stories/highlights/([a-zA-Z0-9_-]+)/", + # Posts + r"/p/([a-zA-Z0-9_-]+)/", + # Reels + r"/reel/([a-zA-Z0-9_-]+)/", + # TV + r"/tv/([a-zA-Z0-9_-]+)/", + # Threads post (both with @username and without) + r"(?:https?://)?(?:www\.)?(?:threads\.net)(?:/[@\w.]+)?(?:/post)?/([\w-]+)(?:/?\?.*)?$", + ] + + for pattern in patterns: + match = re.search(pattern, self._url) + if match: + if pattern == patterns[0]: # Check if it's the stories highlights pattern + # Return the URL as it is + return self._url + else: + # Return the code part (first group) + return match.group(1) + + return None + + def _setup_formats(self) -> list | None: + pass + + def _download(self, formats): + # TODO: Implement download method + resp = requests.get(f"http://instagram:15000/?url={self._url}").json() + code = self.extract_code() + counter = 1 + video_paths = [] + if url_results := resp.get("data"): + for link in url_results: + req = requests.get(link, stream=True) + length = int(req.headers.get("content-length")) + content = req.content + ext = filetype.guess_extension(content) + filename = f"{code}_{counter}.{ext}" + save_path = pathlib.Path(self._tempdir, filename) + chunk_size = 4096 + downloaded = 0 + for chunk in req.iter_content(chunk_size): + text = tqdm_progress(f"Downloading: {filename}", length, downloaded) + edit_text(bm, text) + with open(save_path, "ab") as fp: + fp.write(chunk) + downloaded += len(chunk) + video_paths.append(save_path) + counter += 1 + + return video_paths + + def start(self): + pass diff --git a/src/engine/krakenfiles.py b/src/engine/krakenfiles.py new file mode 100644 index 00000000..1ac4dfcb --- /dev/null +++ b/src/engine/krakenfiles.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - krakenfiles.py + +__author__ = "SanujaNS " + +import requests +from bs4 import BeautifulSoup + + +def krakenfiles(url: str, tempdir: str, bm, **kwargs): + resp = requests.get(url) + html = resp.content + soup = BeautifulSoup(html, "html.parser") + link_parts = [] + token_parts = [] + for form_tag in soup.find_all("form"): + action = form_tag.get("action") + if action and "krakenfiles.com" in action: + link_parts.append(action) + input_tag = form_tag.find("input", {"name": "token"}) + if input_tag: + value = input_tag.get("value") + token_parts.append(value) + for link_part, token_part in zip(link_parts, token_parts): + link = f"https:{link_part}" + data = {"token": token_part} + response = requests.post(link, data=data) + json_data = response.json() + url = json_data["url"] + return sp_ytdl_download(url, tempdir, bm, **kwargs) diff --git a/src/engine/pixeldrain.py b/src/engine/pixeldrain.py new file mode 100644 index 00000000..14210341 --- /dev/null +++ b/src/engine/pixeldrain.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - pixeldrain.py + +__author__ = "SanujaNS " + +import re + + +def pixeldrain(url: str, tempdir: str, bm, **kwargs): + user_page_url_regex = r"https://pixeldrain.com/u/(\w+)" + match = re.match(user_page_url_regex, url) + if match: + url = "https://pixeldrain.com/api/file/{}?engine".format(match.group(1)) + return sp_ytdl_download(url, tempdir, bm, **kwargs) + else: + return url diff --git a/src/main.py b/src/main.py new file mode 100644 index 00000000..40ebe353 --- /dev/null +++ b/src/main.py @@ -0,0 +1,352 @@ +#!/usr/local/bin/python3 +# coding: utf-8 + +# ytdlbot - new.py +# 8/14/21 14:37 +# + +__author__ = "Benny " + +import contextlib +import logging +import re +import threading +import time +import typing +from io import BytesIO +from typing import Any + +import psutil +import pyrogram.errors +import yt_dlp +from apscheduler.schedulers.background import BackgroundScheduler +from pyrogram import Client, enums, filters, types + +from config import ( + APP_HASH, + APP_ID, + AUTHORIZED_USER, + BOT_TOKEN, + ENABLE_ARIA2, + ENABLE_FFMPEG, + ENABLE_VIP, + OWNER, + PROVIDER_TOKEN, + TOKEN_PRICE, + BotText, +) +from database.model import ( + get_format_settings, + get_free_quota, + get_paid_quota, + get_quality_settings, + init_user, + reset_free, + set_user_settings, +) +from engine import youtube_entrance +from utils import extract_url_and_name, sizeof_fmt, timeof_fmt + +logging.info("Authorized users are %s", AUTHORIZED_USER) +logging.getLogger("apscheduler.executors.default").propagate = False + + +def create_app(name: str, workers: int = 64) -> Client: + return Client( + name, + APP_ID, + APP_HASH, + bot_token=BOT_TOKEN, + workers=workers, + # max_concurrent_transmissions=max(1, WORKERS // 2), + # https://github.com/pyrogram/pyrogram/issues/1225#issuecomment-1446595489 + ) + + +app = create_app("main") + + +def private_use(func): + def wrapper(client: Client, message: types.Message): + chat_id = getattr(message.from_user, "id", None) + + # message type check + if message.chat.type != enums.ChatType.PRIVATE and not getattr(message, "text", "").lower().startswith("/ytdl"): + logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) + return + + # authorized users check + if AUTHORIZED_USER: + users = [int(i) for i in AUTHORIZED_USER.split(",")] + else: + users = [] + + if users and chat_id and chat_id not in users: + message.reply_text("BotText.private", quote=True) + return + + return func(client, message) + + return wrapper + + +@app.on_message(filters.command(["start"])) +def start_handler(client: Client, message: types.Message): + from_id = message.chat.id + init_user(from_id) + logging.info("%s welcome to youtube-dl bot!", message.from_user.id) + client.send_chat_action(from_id, enums.ChatAction.TYPING) + free, paid = get_free_quota(from_id), get_paid_quota(from_id) + client.send_message( + from_id, + BotText.start + f"You have {free} free and {paid} paid quota.", + disable_web_page_preview=True, + ) + + +@app.on_message(filters.command(["help"])) +def help_handler(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_message(chat_id, BotText.help, disable_web_page_preview=True) + + +@app.on_message(filters.command(["about"])) +def about_handler(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + client.send_message(chat_id, BotText.about) + + +@app.on_message(filters.command(["ping"])) +def ping_handler(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + + def send_message_and_measure_ping(): + start_time = int(round(time.time() * 1000)) + reply: types.Message | typing.Any = client.send_message(chat_id, "Starting Ping...") + + end_time = int(round(time.time() * 1000)) + ping_time = int(round(end_time - start_time)) + message_sent = True + if message_sent: + message.reply_text(f"Ping: {ping_time:.2f} ms", quote=True) + time.sleep(0.5) + client.edit_message_text(chat_id=reply.chat.id, message_id=reply.id, text="Ping Calculation Complete.") + time.sleep(1) + client.delete_messages(chat_id=reply.chat.id, message_ids=reply.id) + + thread = threading.Thread(target=send_message_and_measure_ping) + thread.start() + + +@app.on_message(filters.command(["stats"])) +def stats_handler(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + cpu_usage = psutil.cpu_percent() + total, used, free, disk = psutil.disk_usage("/") + swap = psutil.swap_memory() + memory = psutil.virtual_memory() + boot_time = psutil.boot_time() + + owner_stats = ( + "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" + f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" + f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" + f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" + f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" + f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" + f"Memory Total: {sizeof_fmt(memory.total)}\n" + f"Memory Free: {sizeof_fmt(memory.available)}\n" + f"Memory Used: {sizeof_fmt(memory.used)}\n" + f"SWAP Total: {sizeof_fmt(swap.total)} | SWAP Usage: {swap.percent}%\n\n" + f"Total Disk Space: {sizeof_fmt(total)}\n" + f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" + f"Physical Cores: {psutil.cpu_count(logical=False)}\n" + f"Total Cores: {psutil.cpu_count(logical=True)}\n\n" + f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" + f"โฒ๏ธOS Uptime: {timeof_fmt(time.time() - boot_time)}\n" + ) + + user_stats = ( + "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" + f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" + f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" + f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" + f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" + f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" + f"Memory Total: {sizeof_fmt(memory.total)}\n" + f"Memory Free: {sizeof_fmt(memory.available)}\n" + f"Memory Used: {sizeof_fmt(memory.used)}\n" + f"Total Disk Space: {sizeof_fmt(total)}\n" + f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" + f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" + ) + + if message.from_user.id in OWNER: + message.reply_text(owner_stats, quote=True) + else: + message.reply_text(user_stats, quote=True) + + +@app.on_message(filters.command(["settings"])) +def settings_handler(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + markup = types.InlineKeyboardMarkup( + [ + [ # First row + types.InlineKeyboardButton("send as document", callback_data="document"), + types.InlineKeyboardButton("send as video", callback_data="video"), + types.InlineKeyboardButton("send as audio", callback_data="audio"), + ], + [ # second row + types.InlineKeyboardButton("High Quality", callback_data="high"), + types.InlineKeyboardButton("Medium Quality", callback_data="medium"), + types.InlineKeyboardButton("Low Quality", callback_data="low"), + ], + ] + ) + + quality = get_quality_settings(chat_id) + send_type = get_format_settings(chat_id) + client.send_message(chat_id, BotText.settings.format(quality, send_type), reply_markup=markup) + + +@app.on_message(filters.command(["spdl"])) +def spdl_handler(client: Client, message: types.Message): + chat_id = message.from_user.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("spdl start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + spdl_download_entrance(client, bot_msg, url) + + +@app.on_message(filters.command(["leech"])) +def leech_handler(client: Client, message: types.Message): + if not ENABLE_ARIA2: + message.reply_text("Aria2 Not Enabled.", quote=True) + return + + chat_id = message.from_user.id + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("leech using aria2 start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + message.reply_text("Send me a correct LINK.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + leech_download_entrance(client, bot_msg, url) + + +@app.on_message(filters.command(["ytdl"])) +def ytdl_handler(client: Client, message: types.Message): + # for group usage + chat_id = message.from_user.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + message_text = message.text + url, new_name = extract_url_and_name(message_text) + logging.info("ytdl start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + return + + bot_msg = message.reply_text("Request received.", quote=True) + youtube_entrance(client, bot_msg, url) + + +def check_link(url: str): + ytdl = yt_dlp.YoutubeDL() + if re.findall(r"^https://www\.youtube\.com/channel/", url) or "list" in url: + # TODO maybe using ytdl.extract_info + raise ValueError("Playlist or channel download are not supported at this moment.") + + if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): + raise ValueError("m3u8 links are not supported.") + + with contextlib.suppress(yt_dlp.utils.DownloadError): + # TODO remove it or try 'match_filter': '!is_live', + if ytdl.extract_info(url, download=False).get("live_status") == "is_live": + raise ValueError("Live stream links are disabled. Please engine it after the stream ends.") + + +@app.on_message(filters.incoming & filters.text) +@private_use +def download_handler(client: Client, message: types.Message): + chat_id = message.from_user.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + url = message.text + logging.info("start %s", url) + + try: + check_link(url) + # raise pyrogram.errors.exceptions.FloodWait(10) + bot_msg: types.Message | Any = message.reply_text("Task received.", quote=True) + client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) + youtube_entrance(client, bot_msg, url) + except pyrogram.errors.Flood as e: + f = BytesIO() + f.write(str(e).encode()) + f.write(b"Your job will be done soon. Just wait!") + f.name = "Please wait.txt" + message.reply_document(f, caption=f"Flood wait! Please wait {e} seconds...", quote=True) + f.close() + client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e} seconds....") + time.sleep(e.value) + except ValueError as e: + message.reply_text(e.__str__(), quote=True) + except Exception as e: + message.reply_text(f"โŒ Download failed: {e}", quote=True) + + +@app.on_callback_query(filters.regex(r"document|video|audio")) +def format_callback(client: Client, callback_query: types.CallbackQuery): + chat_id = callback_query.message.chat.id + data = callback_query.data + logging.info("Setting %s file type to %s", chat_id, data) + callback_query.answer(f"Your send type was set to {callback_query.data}") + set_user_settings(chat_id, "format", data) + + +@app.on_callback_query(filters.regex(r"high|medium|low")) +def quality_callback(client: Client, callback_query: types.CallbackQuery): + chat_id = callback_query.message.chat.id + data = callback_query.data + logging.info("Setting %s download quality to %s", chat_id, data) + callback_query.answer(f"Your default engine quality was set to {callback_query.data}") + set_user_settings(chat_id, "quality", data) + + +if __name__ == "__main__": + botStartTime = time.time() + scheduler = BackgroundScheduler() + scheduler.add_job(reset_free, "cron", hour=0, minute=0) + scheduler.start() + banner = f""" +โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ +โ–โ–ž โ–žโ–€โ–– โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–›โ–€โ–– โ–žโ–€โ–– โ–Œ โ–Œ โ–žโ–€โ–– โ–Œ โ–Œ โ–›โ–€โ–– โ– โ–žโ–€โ–– โ–โ–€โ–– โ–žโ–€โ–Œ + โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–›โ–€ โ–Œ โ–Œ โ–Œ โ–Œ โ–โ–โ– โ–Œ โ–Œ โ– โ–Œ โ–Œ โ–žโ–€โ–Œ โ–Œ โ–Œ + โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–˜ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€ โ–˜โ–˜ โ–˜ โ–˜ โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–โ–€โ–˜ + +By @BennyThink, VIP Mode: {ENABLE_VIP} + """ + print(banner) + app.run() diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 00000000..105d5d2e --- /dev/null +++ b/src/utils/__init__.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - __init__.py.py + + +import logging +import pathlib +import re +import shutil +import tempfile +import time +import uuid +from http.cookiejar import MozillaCookieJar +from urllib.parse import quote_plus + +import ffmpeg + + +def sizeof_fmt(num: int, suffix="B"): + for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, "Yi", suffix) + + +def timeof_fmt(seconds: int | float): + periods = [("d", 86400), ("h", 3600), ("m", 60), ("s", 1)] + result = "" + for period_name, period_seconds in periods: + if seconds >= period_seconds: + period_value, seconds = divmod(seconds, period_seconds) + result += f"{int(period_value)}{period_name}" + return result + + +def is_youtube(url: str): + if url.startswith("https://www.youtube.com/") or url.startswith("https://youtu.be/"): + return True + + +def adjust_formats(formats): + # high: best quality 1080P, 2K, 4K, 8K + # medium: 720P + # low: 480P + + mapping = {"high": [], "medium": [720], "low": [480]} + # formats.insert(0, f"bestvideo[ext=mp4][height={m}]+bestaudio[ext=m4a]") + # formats.insert(1, f"bestvideo[vcodec^=avc][height={m}]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best") + # + # if settings[2] == "audio": + # formats.insert(0, "bestaudio[ext=m4a]") + # + # if settings[2] == "document": + # formats.insert(0, None) + + +def current_time(ts=None): + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts)) + + +def clean_tempfile(): + patterns = ["ytdl*", "spdl*", "leech*", "direct*"] + temp_path = pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()) + + for pattern in patterns: + for item in temp_path.glob(pattern): + if time.time() - item.stat().st_ctime > 3600: + shutil.rmtree(item, ignore_errors=True) + + +def shorten_url(url, CAPTION_URL_LENGTH_LIMIT): + # Shortens a URL by cutting it to a specified length. + shortened_url = url[: CAPTION_URL_LENGTH_LIMIT - 3] + "..." + + return shortened_url + + +def extract_filename(response): + try: + content_disposition = response.headers.get("content-disposition") + if content_disposition: + filename = re.findall("filename=(.+)", content_disposition)[0] + return filename + except (TypeError, IndexError): + pass # Handle potential exceptions during extraction + + # Fallback if Content-Disposition header is missing + filename = response.url.rsplit("/")[-1] + if not filename: + filename = quote_plus(response.url) + return filename + + +def extract_url_and_name(message_text): + # Regular expression to match the URL + url_pattern = r"(https?://[^\s]+)" + # Regular expression to match the new name after '-n' + name_pattern = r"-n\s+(.+)$" + + # Find the URL in the message_text + url_match = re.search(url_pattern, message_text) + url = url_match.group(0) if url_match else None + + # Find the new name in the message_text + name_match = re.search(name_pattern, message_text) + new_name = name_match.group(1) if name_match else None + + return url, new_name diff --git a/worker.yml b/worker.yml deleted file mode 100644 index 990e2453..00000000 --- a/worker.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3.1' - -services: - worker: - image: bennythink/ytdlbot - env_file: - - env/ytdl.env - restart: always - command: [ "/usr/local/bin/supervisord", "-c" ,"/ytdlbot/conf/supervisor_worker.conf" ] -# network_mode: "host" -# deploy: -# resources: -# limits: -# cpus: '0.3' -# memory: 1500M diff --git a/ytdlbot/channel.py b/ytdlbot/channel.py deleted file mode 100644 index fd70a1ec..00000000 --- a/ytdlbot/channel.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 -import http -import logging -import os -import re - -import requests -from bs4 import BeautifulSoup - -from config import ENABLE_VIP -from limit import Payment - - -class Channel(Payment): - def subscribe_channel(self, user_id: int, share_link: str) -> str: - if not re.findall(r"youtube\.com|youtu\.be", share_link): - raise ValueError("Is this a valid YouTube Channel link?") - if ENABLE_VIP: - self.cur.execute("select count(user_id) from subscribe where user_id=%s", (user_id,)) - usage = int(self.cur.fetchone()[0]) - if usage >= 10: - logging.warning("User %s has subscribed %s channels", user_id, usage) - return "You have subscribed too many channels. Maximum 5 channels." - - data = self.get_channel_info(share_link) - channel_id = data["channel_id"] - - self.cur.execute("select user_id from subscribe where user_id=%s and channel_id=%s", (user_id, channel_id)) - if self.cur.fetchall(): - raise ValueError("You have already subscribed this channel.") - - self.cur.execute( - "INSERT IGNORE INTO channel values" - "(%(link)s,%(title)s,%(description)s,%(channel_id)s,%(playlist)s,%(last_video)s)", - data, - ) - self.cur.execute("INSERT INTO subscribe values(%s,%s, NULL)", (user_id, channel_id)) - self.con.commit() - logging.info("User %s subscribed channel %s", user_id, data["title"]) - return "Subscribed to {}".format(data["title"]) - - def unsubscribe_channel(self, user_id: int, channel_id: str) -> int: - affected_rows = self.cur.execute( - "DELETE FROM subscribe WHERE user_id=%s AND channel_id=%s", (user_id, channel_id) - ) - self.con.commit() - logging.info("User %s tried to unsubscribe channel %s", user_id, channel_id) - return affected_rows - - @staticmethod - def extract_canonical_link(url: str) -> str: - # canonic link works for many websites. It will strip out unnecessary stuff - props = ["canonical", "alternate", "shortlinkUrl"] - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36" - } - cookie = {"CONSENT": "PENDING+197"} - # send head request first - r = requests.head(url, headers=headers, allow_redirects=True, cookies=cookie) - if r.status_code != http.HTTPStatus.METHOD_NOT_ALLOWED and "text/html" not in r.headers.get("content-type", ""): - # get content-type, if it's not text/html, there's no need to issue a GET request - logging.warning("%s Content-type is not text/html, no need to GET for extract_canonical_link", url) - return url - - html_doc = requests.get(url, headers=headers, cookies=cookie, timeout=5).text - soup = BeautifulSoup(html_doc, "html.parser") - for prop in props: - element = soup.find(lambda tag: tag.name == "link" and tag.get("rel") == ["prop"]) - try: - href = element["href"] - if href not in ["null", "", None, "https://consent.youtube.com/m"]: - return href - except Exception as e: - logging.debug("Canonical exception %s %s e", url, e) - - return url - - def get_channel_info(self, url: str) -> dict: - api_key = os.getenv("GOOGLE_API_KEY") - canonical_link = self.extract_canonical_link(url) - try: - channel_id = canonical_link.split("youtube.com/channel/")[1] - except IndexError: - channel_id = canonical_link.split("/")[-1] - channel_api = ( - f"https://www.googleapis.com/youtube/v3/channels?part=snippet,contentDetails&id={channel_id}&key={api_key}" - ) - - data = requests.get(channel_api).json() - snippet = data["items"][0]["snippet"] - title = snippet["title"] - description = snippet["description"] - playlist = data["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"] - - return { - "link": url, - "title": title, - "description": description, - "channel_id": channel_id, - "playlist": playlist, - "last_video": self.get_latest_video(playlist), - } - - @staticmethod - def get_latest_video(playlist_id: str) -> str: - api_key = os.getenv("GOOGLE_API_KEY") - video_api = ( - f"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=1&" - f"playlistId={playlist_id}&key={api_key}" - ) - data = requests.get(video_api).json() - video_id = data["items"][0]["snippet"]["resourceId"]["videoId"] - logging.info(f"Latest video %s from %s", video_id, data["items"][0]["snippet"]["channelTitle"]) - return f"https://www.youtube.com/watch?v={video_id}" - - def has_newer_update(self, channel_id: str) -> str: - self.cur.execute("SELECT playlist,latest_video FROM channel WHERE channel_id=%s", (channel_id,)) - data = self.cur.fetchone() - playlist_id = data[0] - old_video = data[1] - newest_video = self.get_latest_video(playlist_id) - if old_video != newest_video: - logging.info("Newer update found for %s %s", channel_id, newest_video) - self.cur.execute("UPDATE channel SET latest_video=%s WHERE channel_id=%s", (newest_video, channel_id)) - self.con.commit() - return newest_video - - def get_user_subscription(self, user_id: int) -> str: - self.cur.execute( - """ - select title, link, channel.channel_id from channel, subscribe - where subscribe.user_id = %s and channel.channel_id = subscribe.channel_id - """, - (user_id,), - ) - data = self.cur.fetchall() - text = "" - for item in data: - text += "[{}]({}) `{}\n`".format(*item) - return text - - def group_subscriber(self) -> dict: - # {"channel_id": [user_id, user_id, ...]} - self.cur.execute("select * from subscribe where is_valid=1") - data = self.cur.fetchall() - group = {} - for item in data: - group.setdefault(item[1], []).append(item[0]) - logging.info("Checking periodic subscriber...") - return group - - def deactivate_user_subscription(self, user_id: int): - self.cur.execute("UPDATE subscribe set is_valid=0 WHERE user_id=%s", (user_id,)) - self.con.commit() - - def sub_count(self) -> str: - sql = """ - select user_id, channel.title, channel.link - from subscribe, channel where subscribe.channel_id = channel.channel_id - """ - self.cur.execute(sql) - data = self.cur.fetchall() - text = f"Total {len(data)} subscriptions found.\n\n" - for item in data: - text += "{} ==> [{}]({})\n".format(*item) - return text - - def del_cache(self, user_link: str) -> int: - unique = self.extract_canonical_link(user_link) - caches = self.r.hgetall("cache") - count = 0 - for key in caches: - if key.startswith(unique): - count += self.del_send_cache(key) - return count - - -if __name__ == "__main__": - s = Channel.extract_canonical_link("https://www.youtube.com/shorts/KkbYbknjPBM") - print(s) diff --git a/ytdlbot/client_init.py b/ytdlbot/client_init.py deleted file mode 100644 index 7702cd51..00000000 --- a/ytdlbot/client_init.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - client_init.py -# 12/29/21 16:20 -# - -__author__ = "Benny " - -from pyrogram import Client - -from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6 - - -def create_app(name: str, workers: int = PYRO_WORKERS) -> Client: - return Client( - name, - APP_ID, - APP_HASH, - bot_token=TOKEN, - workers=workers, - ipv6=IPv6, - # max_concurrent_transmissions=max(1, WORKERS // 2), - # https://github.com/pyrogram/pyrogram/issues/1225#issuecomment-1446595489 - ) diff --git a/ytdlbot/config.py b/ytdlbot/config.py deleted file mode 100644 index 6bec4bb2..00000000 --- a/ytdlbot/config.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - config.py -# 8/28/21 15:01 -# - -__author__ = "Benny " - -import os - -from blinker import signal - -# general settings -WORKERS: int = int(os.getenv("WORKERS", 10)) -PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", 100)) -APP_ID: int = int(os.getenv("APP_ID", 198214)) -APP_HASH = os.getenv("APP_HASH", "1234b90") -TOKEN = os.getenv("TOKEN", "1234") - -REDIS = os.getenv("REDIS", "redis") - -ENABLE_VIP = os.getenv("VIP", False) -OWNER = os.getenv("OWNER", "BennyThink") - -# limitation settings -AUTHORIZED_USER: str = os.getenv("AUTHORIZED_USER", "") -# membership requires: the format could be username(without @ sign)/chat_id of channel or group. -# You need to add the bot to this group/channel as admin -REQUIRED_MEMBERSHIP: str = os.getenv("REQUIRED_MEMBERSHIP", "") - -# celery related -ENABLE_CELERY = os.getenv("ENABLE_CELERY", False) -BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/1") - -MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql") -MYSQL_USER = os.getenv("MYSQL_USER", "root") -MYSQL_PASS = os.getenv("MYSQL_PASS", "root") - -ARCHIVE_ID = os.getenv("ARCHIVE_ID") - -ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) -AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") - -PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False) -M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False) -ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) - -RCLONE_PATH = os.getenv("RCLONE") - -# payment settings -AFD_LINK = os.getenv("AFD_LINK", "https://afdian.net/@BennyThink") -COFFEE_LINK = os.getenv("COFFEE_LINK", "https://www.buymeacoffee.com/bennythink") -COFFEE_TOKEN = os.getenv("COFFEE_TOKEN") -AFD_TOKEN = os.getenv("AFD_TOKEN") -AFD_USER_ID = os.getenv("AFD_USER_ID") -PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234" -FREE_DOWNLOAD = os.getenv("FREE_DOWNLOAD", 10) -EXPIRE = 24 * 3600 -TOKEN_PRICE = os.getenv("BUY_UNIT", 20) # one USD=20 credits -TRONGRID_KEY = os.getenv("TRONGRID_KEY", "").split(",") -# the default mnemonic is for nile testnet -TRON_MNEMONIC = os.getenv("TRON_MNEMONIC", "cram floor today legend service drill pitch leaf car govern harvest soda") -TRX_SIGNAL = signal("trx_received") - -PREMIUM_USER = int(os.getenv("PREMIUM_USER", "0")) - -# For advance users -# Please do not change, if you don't know what these are. -TG_PREMIUM_MAX_SIZE = 4000 * 1024 * 1024 -TG_NORMAL_MAX_SIZE = 2000 * 1024 * 1024 -CAPTION_URL_LENGTH_LIMIT = 150 -IPv6 = os.getenv("IPv6", False) -RATE_LIMIT = os.getenv("RATE_LIMIT", 120) -# This will set the value for the tmpfile path(download path). If not, will return None and use systemโ€™s default path. -# Please ensure that the directory exists and you have necessary permissions to write to it. -TMPFILE_PATH = os.getenv("TMPFILE_PATH") - - -class FileTooBig(Exception): - pass diff --git a/ytdlbot/constant.py b/ytdlbot/constant.py deleted file mode 100644 index d412653d..00000000 --- a/ytdlbot/constant.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - constant.py -# 8/16/21 16:59 -# - -__author__ = "Benny " - -import os - -from config import ( - AFD_LINK, - COFFEE_LINK, - ENABLE_CELERY, - FREE_DOWNLOAD, - REQUIRED_MEMBERSHIP, - TOKEN_PRICE, -) -from database import InfluxDB -from utils import get_func_queue - - -class BotText: - start = """ - Welcome to YouTube Download bot. Type /help for more information. Recommend to use EU Bot - EU๐Ÿ‡ช๐Ÿ‡บ: @benny_2ytdlbot - SG๐Ÿ‡ธ๐Ÿ‡ฌ๏ผš@benny_ytdlbot - - Join https://t.me/+OGRC8tp9-U9mZDZl for updates.""" - - help = """ -1. For YouTube and any websites supported by yt-dlp, just send the link and we will download and send it to you. - -2. For specific links use `/spdl {URL}`. More info at https://github.com/SanujaNS/ytdlbot-telegram#supported-websites - -3. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. - -4. Wanna deploy it yourself?\nHere's the source code: https://github.com/tgbot-collection/ytdlbot - """ - - about = "YouTube Downloader by @BennyThink.\n\nOpen source on GitHub: https://github.com/tgbot-collection/ytdlbot" - - buy = f""" -**Terms:** -1. You can use this bot to download video for {FREE_DOWNLOAD} times within a 24-hour period. - -2. You can buy additional download tokens, valid permanently. - -3. Refunds are possible, contact me if you need that @BennyThink - -4. Download for paid user will be automatically changed to Local mode to avoid queuing. - -5. Paid user can download files larger than 2GB. - -**Price:** -valid permanently -1. 1 USD == {TOKEN_PRICE} tokens -2. 7 CNY == {TOKEN_PRICE} tokens -3. 10 TRX == {TOKEN_PRICE} tokens - -**Payment options:** -Pay any amount you want. For example you can send 20 TRX for {TOKEN_PRICE * 2} tokens. -1. AFDIAN(AliPay, WeChat Pay and PayPal): {AFD_LINK} -2. Buy me a coffee: {COFFEE_LINK} -3. Telegram Bot Payment(Stripe), please click Bot Payment button. -4. TRON(TRX), please click TRON(TRX) button. - -**After payment:** -1. Afdian: attach order number with /redeem command (e.g., `/redeem 123456`). -2. Buy Me a Coffee: attach email with /redeem command (e.g., `/redeem 123@x.com`). **Use different email each time.** -3. Telegram Payment & Tron(TRX): automatically activated within 60s. Check /start to see your balance. - -Want to buy more token with Telegram payment? Let's say 100? Here you go! `/buy 123` - """ - - private = "This bot is for private use" - - membership_require = f"You need to join this group or channel to use this bot\n\nhttps://t.me/{REQUIRED_MEMBERSHIP}" - - settings = """ -Please choose the preferred format and video quality for your video. These settings only **apply to YouTube videos**. - -High quality is recommended. Medium quality aims to 720P, while low quality is 480P. - -If you choose to send the video as a document, it will not be possible to stream it. - -Your current settings: -Video quality: **{0}** -Sending format: **{1}** -""" - custom_text = os.getenv("CUSTOM_TEXT", "") - - premium_warning = """ - Your file is too big, do you want me to try to send it as premium user? - This is an experimental feature so you can only use it once per day. - Also, the premium user will know who you are and what you are downloading. - You may be banned if you abuse this feature. - """ - - @staticmethod - def get_receive_link_text() -> str: - reserved = get_func_queue("reserved") - if ENABLE_CELERY and reserved: - text = f"Your tasks was added to the reserved queue {reserved}. Processing...\n\n" - else: - text = "Your task was added to active queue.\nProcessing...\n\n" - - return text - - @staticmethod - def ping_worker() -> str: - from tasks import app as celery_app - - workers = InfluxDB().extract_dashboard_data() - # [{'celery@BennyใฎMBP': 'abc'}, {'celery@BennyใฎMBP': 'abc'}] - response = celery_app.control.broadcast("ping_revision", reply=True) - revision = {} - for item in response: - revision.update(item) - - text = "" - for worker in workers: - fields = worker["fields"] - hostname = worker["tags"]["hostname"] - status = {True: "โœ…"}.get(fields["status"], "โŒ") - active = fields["active"] - load = "{},{},{}".format(fields["load1"], fields["load5"], fields["load15"]) - rev = revision.get(hostname, "") - text += f"{status}{hostname} **{active}** {load} {rev}\n" - - return text diff --git a/ytdlbot/database.py b/ytdlbot/database.py deleted file mode 100644 index 6e3356d8..00000000 --- a/ytdlbot/database.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - database.py -# 12/7/21 16:57 -# - -__author__ = "Benny " - -import base64 -import contextlib -import datetime -import logging -import os -import re -import sqlite3 -import subprocess -import time -from io import BytesIO - -import fakeredis -import pymysql -import redis -import requests -from beautifultable import BeautifulTable -from influxdb import InfluxDBClient - -from config import MYSQL_HOST, MYSQL_PASS, MYSQL_USER, REDIS - -init_con = sqlite3.connect(":memory:", check_same_thread=False) - - -class FakeMySQL: - @staticmethod - def cursor() -> "Cursor": - return Cursor() - - def commit(self): - pass - - def close(self): - pass - - def ping(self, reconnect): - pass - - -class Cursor: - def __init__(self): - self.con = init_con - self.cur = self.con.cursor() - - def execute(self, *args, **kwargs): - sql = self.sub(args[0]) - new_args = (sql,) + args[1:] - with contextlib.suppress(sqlite3.OperationalError): - return self.cur.execute(*new_args, **kwargs) - - def fetchall(self): - return self.cur.fetchall() - - def fetchone(self): - return self.cur.fetchone() - - @staticmethod - def sub(sql): - sql = re.sub(r"CHARSET.*|charset.*", "", sql, re.IGNORECASE) - sql = sql.replace("%s", "?") - return sql - - -class Redis: - def __init__(self): - db = 1 - try: - self.r = redis.StrictRedis(host=REDIS, db=db, decode_responses=True) - self.r.ping() - except Exception: - logging.warning("Redis connection failed, using fake redis instead.") - self.r = fakeredis.FakeStrictRedis(host=REDIS, db=db, decode_responses=True) - - db_banner = "=" * 20 + "DB data" + "=" * 20 - quota_banner = "=" * 20 + "Celery" + "=" * 20 - metrics_banner = "=" * 20 + "Metrics" + "=" * 20 - usage_banner = "=" * 20 + "Usage" + "=" * 20 - vnstat_banner = "=" * 20 + "vnstat" + "=" * 20 - self.final_text = f""" -{db_banner} -%s - - -{vnstat_banner} -%s - - -{quota_banner} -%s - - -{metrics_banner} -%s - - -{usage_banner} -%s - """ - super().__init__() - - def __del__(self): - self.r.close() - - def update_metrics(self, metrics: str): - logging.info(f"Setting metrics: {metrics}") - all_ = f"all_{metrics}" - today = f"today_{metrics}" - self.r.hincrby("metrics", all_) - self.r.hincrby("metrics", today) - - @staticmethod - def generate_table(header, all_data: list): - table = BeautifulTable() - for data in all_data: - table.rows.append(data) - table.columns.header = header - table.rows.header = [str(i) for i in range(1, len(all_data) + 1)] - return table - - def show_usage(self): - db = MySQL() - db.cur.execute("select user_id,payment_amount,old_user,token from payment") - data = db.cur.fetchall() - fd = [] - for item in data: - fd.append([item[0], item[1], item[2], item[3]]) - db_text = self.generate_table(["ID", "pay amount", "old user", "token"], fd) - - fd = [] - hash_keys = self.r.hgetall("metrics") - for key, value in hash_keys.items(): - if re.findall(r"^today|all", key): - fd.append([key, value]) - fd.sort(key=lambda x: x[0]) - metrics_text = self.generate_table(["name", "count"], fd) - - fd = [] - for key, value in hash_keys.items(): - if re.findall(r"\d+", key): - fd.append([key, value]) - fd.sort(key=lambda x: int(x[-1]), reverse=True) - usage_text = self.generate_table(["UserID", "count"], fd) - - worker_data = InfluxDB.get_worker_data() - fd = [] - for item in worker_data["data"]: - fd.append( - [ - item.get("hostname", 0), - item.get("status", 0), - item.get("active", 0), - item.get("processed", 0), - item.get("task-failed", 0), - item.get("task-succeeded", 0), - ",".join(str(i) for i in item.get("loadavg", [])), - ] - ) - - worker_text = self.generate_table( - ["worker name", "status", "active", "processed", "failed", "succeeded", "Load Average"], fd - ) - - # vnstat - if os.uname().sysname == "Darwin": - cmd = "/opt/homebrew/bin/vnstat -i en0".split() - else: - cmd = "/usr/bin/vnstat -i eth0".split() - vnstat_text = subprocess.check_output(cmd).decode("u8") - return self.final_text % (db_text, vnstat_text, worker_text, metrics_text, usage_text) - - def reset_today(self): - pairs = self.r.hgetall("metrics") - for k in pairs: - if k.startswith("today"): - self.r.hdel("metrics", k) - - self.r.delete("premium") - - def user_count(self, user_id): - self.r.hincrby("metrics", user_id) - - def generate_file(self): - text = self.show_usage() - file = BytesIO() - file.write(text.encode("u8")) - date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) - file.name = f"{date}.txt" - return file - - def add_send_cache(self, unique: str, file_id: str): - self.r.hset("cache", unique, file_id) - - def get_send_cache(self, unique) -> str: - return self.r.hget("cache", unique) - - def del_send_cache(self, unique): - return self.r.hdel("cache", unique) - - -class MySQL: - vip_sql = """ - CREATE TABLE if not exists `payment` - ( - `user_id` bigint NOT NULL, - `payment_amount` float DEFAULT NULL, - `payment_id` varchar(256) DEFAULT NULL, - `old_user` tinyint(1) DEFAULT NULL, - `token` int DEFAULT NULL, - UNIQUE KEY `payment_id` (`payment_id`) - ) CHARSET = utf8mb4 - """ - - settings_sql = """ - create table if not exists settings - ( - user_id bigint not null, - resolution varchar(128) null, - method varchar(64) null, - mode varchar(32) default 'Celery' null, - history varchar(10) default 'OFF' null, - constraint settings_pk - primary key (user_id) - ); - """ - - channel_sql = """ - create table if not exists channel - ( - link varchar(256) null, - title varchar(256) null, - description text null, - channel_id varchar(256), - playlist varchar(256) null, - latest_video varchar(256) null, - constraint channel_pk - primary key (channel_id) - ) CHARSET=utf8mb4; - """ - - subscribe_sql = """ - create table if not exists subscribe - ( - user_id bigint null, - channel_id varchar(256) null, - is_valid boolean default 1 null - ) CHARSET=utf8mb4; - """ - history_sql = """ - create table if not exists history - ( - user_id bigint null, - link varchar(256) null, - title varchar(512) null - ) CHARSET=utf8mb4; - """ - - def __init__(self): - try: - self.con = pymysql.connect( - host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4" - ) - self.con.ping(reconnect=True) - except Exception: - logging.warning("MySQL connection failed, using fake mysql instead.") - self.con = FakeMySQL() - - self.con.ping(reconnect=True) - self.cur = self.con.cursor() - self.init_db() - super().__init__() - - def init_db(self): - self.cur.execute(self.vip_sql) - self.cur.execute(self.settings_sql) - self.cur.execute(self.channel_sql) - self.cur.execute(self.subscribe_sql) - self.cur.execute(self.history_sql) - self.con.commit() - - def __del__(self): - self.con.close() - - def get_user_settings(self, user_id: int) -> tuple: - self.cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) - data = self.cur.fetchone() - if data is None: - return 100, "high", "video", "Celery", "OFF" - return data - - def set_user_settings(self, user_id: int, field: str, value: str): - cur = self.con.cursor() - cur.execute("SELECT * FROM settings WHERE user_id = %s", (user_id,)) - data = cur.fetchone() - if data is None: - resolution = method = "" - if field == "resolution": - method = "video" - resolution = value - if field == "method": - method = value - resolution = "high" - cur.execute("INSERT INTO settings VALUES (%s,%s,%s,%s,%s)", (user_id, resolution, method, "Celery", "OFF")) - else: - cur.execute(f"UPDATE settings SET {field} =%s WHERE user_id = %s", (value, user_id)) - self.con.commit() - - def show_history(self, user_id: int): - self.cur.execute("SELECT link,title FROM history WHERE user_id = %s", (user_id,)) - data = self.cur.fetchall() - return "\n".join([f"{i[0]} {i[1]}" for i in data]) - - def clear_history(self, user_id: int): - self.cur.execute("DELETE FROM history WHERE user_id = %s", (user_id,)) - self.con.commit() - - def add_history(self, user_id: int, link: str, title: str): - self.cur.execute("INSERT INTO history VALUES (%s,%s,%s)", (user_id, link, title)) - self.con.commit() - - def search_history(self, user_id: int, kw: str): - self.cur.execute("SELECT * FROM history WHERE user_id = %s AND title like %s", (user_id, f"%{kw}%")) - data = self.cur.fetchall() - if data: - return data - return None - - -class InfluxDB: - def __init__(self): - self.client = InfluxDBClient( - host=os.getenv("INFLUX_HOST"), - path=os.getenv("INFLUX_PATH"), - port=443, - username="nova", - password=os.getenv("INFLUX_PASS"), - database="celery", - ssl=True, - verify_ssl=True, - ) - self.data = None - - def __del__(self): - self.client.close() - - @staticmethod - def get_worker_data() -> dict: - username = os.getenv("FLOWER_USERNAME", "benny") - password = os.getenv("FLOWER_PASSWORD", "123456abc") - token = base64.b64encode(f"{username}:{password}".encode()).decode() - headers = {"Authorization": f"Basic {token}"} - r = requests.get("https://celery.dmesg.app/workers?json=1", headers=headers) - if r.status_code != 200: - return dict(data=[]) - return r.json() - - def extract_dashboard_data(self): - self.data = self.get_worker_data() - json_body = [] - for worker in self.data["data"]: - load1, load5, load15 = worker["loadavg"] - t = { - "measurement": "tasks", - "tags": { - "hostname": worker["hostname"], - }, - "time": datetime.datetime.utcnow(), - "fields": { - "task-received": worker.get("task-received", 0), - "task-started": worker.get("task-started", 0), - "task-succeeded": worker.get("task-succeeded", 0), - "task-failed": worker.get("task-failed", 0), - "active": worker.get("active", 0), - "status": worker.get("status", False), - "load1": load1, - "load5": load5, - "load15": load15, - }, - } - json_body.append(t) - return json_body - - def __fill_worker_data(self): - json_body = self.extract_dashboard_data() - self.client.write_points(json_body) - - def __fill_overall_data(self): - active = sum([i["active"] for i in self.data["data"]]) - json_body = [{"measurement": "active", "time": datetime.datetime.utcnow(), "fields": {"active": active}}] - self.client.write_points(json_body) - - def __fill_redis_metrics(self): - json_body = [{"measurement": "metrics", "time": datetime.datetime.utcnow(), "fields": {}}] - r = Redis().r - hash_keys = r.hgetall("metrics") - for key, value in hash_keys.items(): - if re.findall(r"^today", key): - json_body[0]["fields"][key] = int(value) - - self.client.write_points(json_body) - - def collect_data(self): - if os.getenv("INFLUX_HOST") is None: - return - - with contextlib.suppress(Exception): - self.data = self.get_worker_data() - self.__fill_worker_data() - self.__fill_overall_data() - self.__fill_redis_metrics() - logging.debug("InfluxDB data was collected.") diff --git a/ytdlbot/downloader.py b/ytdlbot/downloader.py deleted file mode 100644 index 6844d5d6..00000000 --- a/ytdlbot/downloader.py +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - downloader.py -# 8/14/21 16:53 -# - -__author__ = "Benny " - -import functools -import logging -import os -import pathlib -import re -import subprocess -import threading -import time -import traceback -from io import StringIO -from unittest.mock import MagicMock - -import ffmpeg -import ffpb -import filetype -import yt_dlp as ytdl -from pyrogram import types -from tqdm import tqdm - -from config import ( - AUDIO_FORMAT, - ENABLE_ARIA2, - ENABLE_FFMPEG, - PREMIUM_USER, - TG_NORMAL_MAX_SIZE, - TG_PREMIUM_MAX_SIZE, - FileTooBig, - IPv6, -) -from limit import Payment -from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt - -apply_log_formatter() - - -def debounce(wait_seconds): - """ - Thread-safe debounce decorator for functions that take a message with chat.id and msg.id attributes. - The function will only be called if it hasn't been called with the same chat.id and msg.id in the last 'wait_seconds'. - """ - - def decorator(func): - last_called = {} - lock = threading.Lock() - - @functools.wraps(func) - def wrapper(*args, **kwargs): - nonlocal last_called - now = time.time() - - # Assuming the first argument is the message object with chat.id and msg.id - bot_msg = args[0] - key = (bot_msg.chat.id, bot_msg.id) - - with lock: - if key not in last_called or now - last_called[key] >= wait_seconds: - last_called[key] = now - return func(*args, **kwargs) - - return wrapper - - return decorator - - -@debounce(5) -def edit_text(bot_msg: types.Message, text: str): - bot_msg.edit_text(text) - - -def tqdm_progress(desc, total, finished, speed="", eta=""): - def more(title, initial): - if initial: - return f"{title} {initial}" - else: - return "" - - f = StringIO() - tqdm( - total=total, - initial=finished, - file=f, - ascii=False, - unit_scale=True, - ncols=30, - bar_format="{l_bar}{bar} |{n_fmt}/{total_fmt} ", - ) - raw_output = f.getvalue() - tqdm_output = raw_output.split("|") - progress = f"`[{tqdm_output[1]}]`" - detail = tqdm_output[2].replace("[A", "") - text = f""" -{desc} - -{progress} -{detail} -{more("Speed:", speed)} -{more("ETA:", eta)} - """ - f.close() - return text - - -def remove_bash_color(text): - return re.sub(r"\u001b|\[0;94m|\u001b\[0m|\[0;32m|\[0m|\[0;33m", "", text) - - -def download_hook(d: dict, bot_msg): - if d["status"] == "downloading": - downloaded = d.get("downloaded_bytes", 0) - total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) - if total > TG_PREMIUM_MAX_SIZE: - raise Exception(f"There's no way to handle a file of {sizeof_fmt(total)}.") - if total > TG_NORMAL_MAX_SIZE: - msg = f"Your download file size {sizeof_fmt(total)} is too large for Telegram." - if PREMIUM_USER: - raise FileTooBig(msg) - else: - raise Exception(msg) - - # percent = remove_bash_color(d.get("_percent_str", "N/A")) - speed = remove_bash_color(d.get("_speed_str", "N/A")) - eta = remove_bash_color(d.get("_eta_str", d.get("eta"))) - text = tqdm_progress("Downloading...", total, downloaded, speed, eta) - # debounce in here - edit_text(bot_msg, text) - - -def upload_hook(current, total, bot_msg): - text = tqdm_progress("Uploading...", total, current) - edit_text(bot_msg, text) - - -def convert_to_mp4(video_paths: list, bot_msg): - default_type = ["video/x-flv", "video/webm"] - # all_converted = [] - for path in video_paths: - # if we can't guess file type, we assume it's video/mp4 - mime = getattr(filetype.guess(path), "mime", "video/mp4") - if mime in default_type: - if not can_convert_mp4(path, bot_msg.chat.id): - logging.warning("Conversion abort for %s", bot_msg.chat.id) - bot_msg._client.send_message(bot_msg.chat.id, "Can't convert your video. ffmpeg has been disabled.") - break - edit_text(bot_msg, f"{current_time()}: Converting {path.name} to mp4. Please wait.") - new_file_path = path.with_suffix(".mp4") - logging.info("Detected %s, converting to mp4...", mime) - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_file_path], bot_msg) - index = video_paths.index(path) - video_paths[index] = new_file_path - - -class ProgressBar(tqdm): - b = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.bot_msg = self.b - - def update(self, n=1): - super().update(n) - t = tqdm_progress("Converting...", self.total, self.n) - edit_text(self.bot_msg, t) - - -def run_ffmpeg_progressbar(cmd_list: list, bm): - cmd_list = cmd_list.copy()[1:] - ProgressBar.b = bm - ffpb.main(cmd_list, tqdm=ProgressBar) - - -def can_convert_mp4(video_path, uid): - if not ENABLE_FFMPEG: - return False - return True - - -def ytdl_download(url: str, tempdir: str, bm, **kwargs) -> list: - payment = Payment() - chat_id = bm.chat.id - hijack = kwargs.get("hijack") - output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() - ydl_opts = { - "progress_hooks": [lambda d: download_hook(d, bm)], - "outtmpl": output, - "restrictfilenames": False, - "quiet": True, - } - if ENABLE_ARIA2: - ydl_opts["external_downloader"] = "aria2c" - ydl_opts["external_downloader_args"] = [ - "--min-split-size=1M", - "--max-connection-per-server=16", - "--max-concurrent-downloads=16", - "--split=16", - ] - if url.startswith("https://drive.google.com"): - # Always use the `source` format for Google Drive URLs. - formats = ["source"] - else: - # Use the default formats for other URLs. - formats = [ - # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 - "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", - "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", - None, - ] - # This method will alter formats if necessary - adjust_formats(chat_id, url, formats, hijack) - address = ["::", "0.0.0.0"] if IPv6 else [None] - error = None - video_paths = None - for format_ in formats: - ydl_opts["format"] = format_ - for addr in address: - # IPv6 goes first in each format - ydl_opts["source_address"] = addr - try: - logging.info("Downloading for %s with format %s", url, format_) - with ytdl.YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - video_paths = list(pathlib.Path(tempdir).glob("*")) - break - except FileTooBig as e: - raise e - except Exception: - error = traceback.format_exc() - logging.error("Download failed for %s - %s, try another way", format_, url) - if error is None: - break - - if not video_paths: - raise Exception(error) - - # convert format if necessary - settings = payment.get_user_settings(chat_id) - if settings[2] == "video" or isinstance(settings[2], MagicMock): - # only convert if send type is video - convert_to_mp4(video_paths, bm) - if settings[2] == "audio" or hijack == "bestaudio[ext=m4a]": - convert_audio_format(video_paths, bm) - # split_large_video(video_paths) - return video_paths - - -def convert_audio_format(video_paths: list, bm): - # 1. file is audio, default format - # 2. file is video, default format - # 3. non default format - - for path in video_paths: - streams = ffmpeg.probe(path)["streams"] - if AUDIO_FORMAT is None and len(streams) == 1 and streams[0]["codec_type"] == "audio": - logging.info("%s is audio, default format, no need to convert", path) - elif AUDIO_FORMAT is None and len(streams) >= 2: - logging.info("%s is video, default format, need to extract audio", path) - audio_stream = {"codec_name": "m4a"} - for stream in streams: - if stream["codec_type"] == "audio": - audio_stream = stream - break - ext = audio_stream["codec_name"] - new_path = path.with_suffix(f".{ext}") - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, "-vn", "-acodec", "copy", new_path], bm) - path.unlink() - index = video_paths.index(path) - video_paths[index] = new_path - else: - logging.info("Not default format, converting %s to %s", path, AUDIO_FORMAT) - new_path = path.with_suffix(f".{AUDIO_FORMAT}") - run_ffmpeg_progressbar(["ffmpeg", "-y", "-i", path, new_path], bm) - path.unlink() - index = video_paths.index(path) - video_paths[index] = new_path - - -def split_large_video(video_paths: list): - original_video = None - split = False - for original_video in video_paths: - size = os.stat(original_video).st_size - if size > TG_NORMAL_MAX_SIZE: - split = True - logging.warning("file is too large %s, splitting...", size) - subprocess.check_output(f"sh split-video.sh {original_video} {TG_NORMAL_MAX_SIZE * 0.95} ".split()) - os.remove(original_video) - - if split and original_video: - return [i for i in pathlib.Path(original_video).parent.glob("*")] diff --git a/ytdlbot/flower_tasks.py b/ytdlbot/flower_tasks.py deleted file mode 100644 index c791421a..00000000 --- a/ytdlbot/flower_tasks.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - flower_tasks.py -# 1/2/22 10:17 -# - -__author__ = "Benny " - -from celery import Celery - -from config import BROKER - -app = Celery("tasks", broker=BROKER, timezone="Europe/London") diff --git a/ytdlbot/keep_alive.py b/ytdlbot/keep_alive.py deleted file mode 100644 index 9cf90068..00000000 --- a/ytdlbot/keep_alive.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 - -# ytdlbot - keep_alive.py -# 2024-01-22 17:59 - -import time - -while True: - print("I'm still alive") - time.sleep(10) diff --git a/ytdlbot/limit.py b/ytdlbot/limit.py deleted file mode 100644 index 819978db..00000000 --- a/ytdlbot/limit.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - limit.py -# 8/15/21 18:23 -# - -__author__ = "Benny " - -import hashlib -import logging -import time - -import requests -from tronpy import Tron -from tronpy.exceptions import TransactionError, ValidationError -from tronpy.hdwallet import key_from_seed, seed_from_mnemonic -from tronpy.keys import PrivateKey -from tronpy.providers import HTTPProvider - -from config import ( - AFD_TOKEN, - AFD_USER_ID, - COFFEE_TOKEN, - EXPIRE, - FREE_DOWNLOAD, - OWNER, - TOKEN_PRICE, - TRON_MNEMONIC, - TRONGRID_KEY, - TRX_SIGNAL, -) -from database import MySQL, Redis -from utils import apply_log_formatter, current_time - -apply_log_formatter() - - -class BuyMeACoffee: - def __init__(self): - self._token = COFFEE_TOKEN - self._url = "https://developers.buymeacoffee.com/api/v1/supporters" - self._data = [] - - def _get_data(self, url): - d = requests.get(url, headers={"Authorization": f"Bearer {self._token}"}).json() - self._data.extend(d["data"]) - next_page = d["next_page_url"] - if next_page: - self._get_data(next_page) - - def _get_bmac_status(self, email: str) -> dict: - self._get_data(self._url) - for user in self._data: - if user["payer_email"] == email or user["support_email"] == email: - return user - return {} - - def get_user_payment(self, email: str) -> (int, "float", str): - order = self._get_bmac_status(email) - price = float(order.get("support_coffee_price", 0)) - cups = float(order.get("support_coffees", 1)) - amount = price * cups - return amount, email - - -class Afdian: - def __init__(self): - self._token = AFD_TOKEN - self._user_id = AFD_USER_ID - self._url = "https://afdian.net/api/open/query-order" - - def _generate_signature(self): - data = { - "user_id": self._user_id, - "params": '{"x":0}', - "ts": int(time.time()), - } - sign_text = "{token}params{params}ts{ts}user_id{user_id}".format( - token=self._token, params=data["params"], ts=data["ts"], user_id=data["user_id"] - ) - - md5 = hashlib.md5(sign_text.encode("u8")) - md5 = md5.hexdigest() - data["sign"] = md5 - - return data - - def _get_afdian_status(self, trade_no: str) -> dict: - req_data = self._generate_signature() - data = requests.post(self._url, json=req_data).json() - # latest 50 - for order in data["data"]["list"]: - if order["out_trade_no"] == trade_no: - return order - - return {} - - def get_user_payment(self, trade_no: str) -> (int, float, str): - order = self._get_afdian_status(trade_no) - amount = float(order.get("show_amount", 0)) - # convert to USD - return amount / 7, trade_no - - -class TronTrx: - def __init__(self): - if TRON_MNEMONIC == "cram floor today legend service drill pitch leaf car govern harvest soda": - logging.warning("Using nile testnet") - provider = HTTPProvider(endpoint_uri="https://nile.trongrid.io") - network = "nile" - else: - provider = HTTPProvider(api_key=TRONGRID_KEY) - network = "mainnet" - self.client = Tron(provider, network=network) - - def central_transfer(self, from_, index, amount: int): - logging.info("Generated key with index %s", index) - seed = seed_from_mnemonic(TRON_MNEMONIC, passphrase="") - key = PrivateKey(key_from_seed(seed, account_path=f"m/44'/195'/1'/0/{index}")) - central = self.central_wallet() - logging.info("Transfer %s TRX from %s to %s", amount, from_, central) - try: - self.client.trx.transfer(from_, central, amount).build().sign(key).broadcast() - except (TransactionError, ValidationError): - logging.error("Failed to transfer %s TRX to %s. Lower and try again.", amount, from_) - if amount > 1_100_000: - # 1.1 trx transfer fee - self.client.trx.transfer(from_, central, amount - 1_100_000).build().sign(key).broadcast() - - def central_wallet(self): - wallet = self.client.generate_address_from_mnemonic(TRON_MNEMONIC, account_path="m/44'/195'/0'/0/0") - return wallet["base58check_address"] - - def get_payment_address(self, user_id): - # payment_id is like tron,0,TN8Mn9KKv3cSrKyrt6Xx5L18nmezbpiW31,index where 0 means unpaid - db = MySQL() - con = db.con - cur = db.cur - cur.execute("select user_id from payment where payment_id like 'tron,%'") - data = cur.fetchall() - index = len(data) - path = f"m/44'/195'/1'/0/{index}" - logging.info("Generating address for user %s with path %s", user_id, path) - addr = self.client.generate_address_from_mnemonic(TRON_MNEMONIC, account_path=path)["base58check_address"] - # add row in db, unpaid - cur.execute("insert into payment values (%s,%s,%s,%s,%s)", (user_id, 0, f"tron,0,{addr},{index}", 0, 0)) - con.commit() - return addr - - def check_payment(self): - db = MySQL() - con = db.con - cur = db.cur - - cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'") - data = cur.fetchall() - for row in data: - logging.info("Checking user payment %s", row) - user_id = row[0] - addr, index = row[1].split(",")[2:] - try: - balance = self.client.get_account_balance(addr) - except: - balance = 0 - if balance: - logging.info("User %s has %s TRX", user_id, balance) - # paid, calc token count - token_count = int(balance / 10 * TOKEN_PRICE) - cur.execute( - "update payment set token=%s,payment_id=%s where user_id=%s and payment_id like %s", - (token_count, f"tron,1,{addr},{index}", user_id, f"tron,%{addr}%"), - ) - cur.execute("UPDATE settings SET mode='Local' WHERE user_id=%s", (user_id,)) - con.commit() - self.central_transfer(addr, index, int(balance * 1_000_000)) - logging.debug("Dispatch signal now....") - TRX_SIGNAL.send("cron", user_id=user_id, text=f"{balance} TRX received, {token_count} tokens added.") - - -class Payment(Redis, MySQL): - def check_old_user(self, user_id: int) -> tuple: - self.cur.execute("SELECT * FROM payment WHERE user_id=%s AND old_user=1", (user_id,)) - data = self.cur.fetchone() - return data - - def get_pay_token(self, user_id: int) -> int: - self.cur.execute("SELECT token, old_user FROM payment WHERE user_id=%s", (user_id,)) - data = self.cur.fetchall() or [(0, False)] - number = sum([i[0] for i in data if i[0]]) - if number == 0 and data[0][1] != 1: - # not old user, no token - logging.warning("User %s has no token, set download mode to Celery", user_id) - # change download mode to Celery - self.set_user_settings(user_id, "mode", "Celery") - return number - - def get_free_token(self, user_id: int) -> int: - if self.r.exists(user_id): - return int(self.r.get(user_id)) - else: - # set and return - self.r.set(user_id, FREE_DOWNLOAD, ex=EXPIRE) - return FREE_DOWNLOAD - - def get_token(self, user_id: int): - ttl = self.r.ttl(user_id) - return self.get_free_token(user_id), self.get_pay_token(user_id), current_time(time.time() + ttl) - - def use_free_token(self, user_id: int): - if self.r.exists(user_id): - self.r.decr(user_id, 1) - else: - # first time download - self.r.set(user_id, 5 - 1, ex=EXPIRE) - - def use_pay_token(self, user_id: int): - # a user may pay multiple times, so we'll need to filter the first payment with valid token - self.cur.execute("SELECT payment_id FROM payment WHERE user_id=%s AND token>0", (user_id,)) - data = self.cur.fetchone() - payment_id = data[0] - logging.info("User %s use pay token with payment_id %s", user_id, payment_id) - self.cur.execute("UPDATE payment SET token=token-1 WHERE payment_id=%s", (payment_id,)) - self.con.commit() - - def use_token(self, user_id: int): - free = self.get_free_token(user_id) - if free > 0: - self.use_free_token(user_id) - else: - self.use_pay_token(user_id) - - def add_pay_user(self, pay_data: list): - self.cur.execute("INSERT INTO payment VALUES (%s,%s,%s,%s,%s)", pay_data) - self.set_user_settings(pay_data[0], "mode", "Local") - self.con.commit() - - def verify_payment(self, user_id: int, unique: str) -> str: - pay = BuyMeACoffee() if "@" in unique else Afdian() - self.cur.execute("SELECT * FROM payment WHERE payment_id=%s ", (unique,)) - data = self.cur.fetchone() - if data: - # TODO what if a user pay twice with the same email address? - return ( - f"Failed. Payment has been verified by other users. Please contact @{OWNER} if you have any questions." - ) - - amount, pay_id = pay.get_user_payment(unique) - logging.info("User %s paid %s, identifier is %s", user_id, amount, unique) - # amount is already in USD - if amount == 0: - return "Payment not found. Please check your payment ID or email address" - self.add_pay_user([user_id, amount, pay_id, 0, amount * TOKEN_PRICE]) - return "Thanks! Your payment has been verified. /start to get your token details" - - -if __name__ == "__main__": - a = TronTrx() - # a.central_wallet() - a.check_payment() diff --git a/ytdlbot/premium.py b/ytdlbot/premium.py deleted file mode 100644 index b6365e80..00000000 --- a/ytdlbot/premium.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 - -# ytdlbot - premium.py -# 2023-12-20 17:53 - -import json -import logging -import pathlib -import tempfile - -import yt_dlp -from pyrogram import Client, filters, types - -from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN -from limit import Payment, Redis -from utils import apply_log_formatter, sizeof_fmt - -apply_log_formatter() -app = Client("premium", APP_ID, APP_HASH, workers=PYRO_WORKERS) - -BOT_ID = int(TOKEN.split(":")[0]) - - -def download_hook(d: dict): - downloaded = d.get("downloaded_bytes", 0) - total = d.get("total_bytes") or d.get("total_bytes_estimate", 0) - logging.info("Downloaded %s/%s, %.2f%% complete", downloaded, total, downloaded / total * 100) - - -async def upload_hook(current, total): - logging.info("Uploaded %s/%s, %.2f%% complete", current, total, current / total * 100) - - -@app.on_message(filters.user(BOT_ID) & filters.incoming) -async def hello(client: Client, message: types.Message): - text = message.text - try: - data = json.loads(text) - except json.decoder.JSONDecodeError: - return - url = data["url"] - user_id = data["user_id"] - - redis = Redis() - redis.r.hset("premium", user_id, 1) - tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") - output = pathlib.Path(tempdir.name, "%(title).70s.%(ext)s").as_posix() - ydl_opts = {"restrictfilenames": False, "quiet": True, "outtmpl": output, "progress_hooks": [download_hook]} - formats = [ - # webm , vp9 and av01 are not streamable on telegram, so we'll extract only mp4 - "bestvideo[ext=mp4][vcodec!*=av01][vcodec!*=vp09]+bestaudio[ext=m4a]/bestvideo+bestaudio", - "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", - None, - ] - - for f in formats: - ydl_opts["format"] = f - logging.info("Downloading BIG FILE for %s with format %s", url, f) - try: - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - break - except Exception as e: - logging.error("Download failed for %s: %s", url, e) - - payment = Payment() - settings = payment.get_user_settings(user_id) - video_path = next(pathlib.Path(tempdir.name).glob("*")) - logging.info("Final filesize is %s", sizeof_fmt(video_path.stat().st_size)) - caption = "Powered by @benny_ytdlbot " - if settings[2] == "audio": - logging.info("Sending as audio") - await client.send_audio( - BOT_ID, - video_path.as_posix(), - caption=caption, - file_name=f"{user_id}.mp3", - progress=upload_hook, - ) - elif settings[2] == "document": - logging.info("Sending as document") - await client.send_document( - BOT_ID, - video_path.as_posix(), - caption=caption, - file_name=f"{user_id}.mp4", - progress=upload_hook, - ) - else: - logging.info("Sending as video") - await client.send_video( - BOT_ID, - video_path.as_posix(), - caption=caption, - supports_streaming=True, - file_name=f"{user_id}.mp4", - progress=upload_hook, - ) - - tempdir.cleanup() - logging.info("Finished sending %s", url) - - -if __name__ == "__main__": - app.run() diff --git a/ytdlbot/sp_downloader.py b/ytdlbot/sp_downloader.py deleted file mode 100644 index 18355549..00000000 --- a/ytdlbot/sp_downloader.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - sp_downloader.py -# 3/16/24 16:32 -# - -__author__ = "SanujaNS " - -import functools -import os -import json -import logging -import pathlib -import re -import traceback -from urllib.parse import urlparse, parse_qs - -from pyrogram import types -from tqdm import tqdm -import filetype -import requests -from bs4 import BeautifulSoup -import yt_dlp as ytdl - -from config import ( - PREMIUM_USER, - TG_NORMAL_MAX_SIZE, - TG_PREMIUM_MAX_SIZE, - FileTooBig, - IPv6, -) -from downloader import ( - edit_text, - remove_bash_color, - ProgressBar, - tqdm_progress, - download_hook, - upload_hook, -) -from limit import Payment -from utils import sizeof_fmt, parse_cookie_file, extract_code_from_instagram_url - - -def sp_dl(url: str, tempdir: str, bm, **kwargs) -> list: - """Specific link downloader""" - domain = urlparse(url).hostname - if "youtube.com" in domain or "youtu.be" in domain: - raise ValueError("ERROR: This is ytdl bot for Youtube links just send the link.") - elif "www.instagram.com" in domain: - return instagram(url, tempdir, bm, **kwargs) - elif "pixeldrain.com" in domain: - return pixeldrain(url, tempdir, bm, **kwargs) - elif "krakenfiles.com" in domain: - return krakenfiles(url, tempdir, bm, **kwargs) - elif any( - x in domain - for x in [ - "terabox.com", - "nephobox.com", - "4funbox.com", - "mirrobox.com", - "momerybox.com", - "teraboxapp.com", - "1024tera.com", - "terabox.app", - "gibibox.com", - "goaibox.com", - ] - ): - return terabox(url, tempdir, bm, **kwargs) - else: - raise ValueError(f"Invalid URL: No specific link function found for {url}") - - return [] - - -def sp_ytdl_download(url: str, tempdir: str, bm, filename=None, **kwargs) -> list: - payment = Payment() - chat_id = bm.chat.id - if filename: - output = pathlib.Path(tempdir, filename).as_posix() - else: - output = pathlib.Path(tempdir, "%(title).70s.%(ext)s").as_posix() - ydl_opts = { - "progress_hooks": [lambda d: download_hook(d, bm)], - "outtmpl": output, - "restrictfilenames": False, - "quiet": True, - "format": None, - } - - address = ["::", "0.0.0.0"] if IPv6 else [None] - error = None - video_paths = None - for addr in address: - ydl_opts["source_address"] = addr - try: - logging.info("Downloading %s", url) - with ytdl.YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - video_paths = list(pathlib.Path(tempdir).glob("*")) - break - except FileTooBig as e: - raise e - except Exception: - error = traceback.format_exc() - logging.error("Download failed for %s - %s", url) - - if not video_paths: - raise Exception(error) - - return video_paths - - -def instagram(url: str, tempdir: str, bm, **kwargs): - resp = requests.get(f"http://192.168.6.1:15000/?url={url}").json() - code = extract_code_from_instagram_url(url) - counter = 1 - video_paths = [] - if url_results := resp.get("data"): - for link in url_results: - req = requests.get(link, stream=True) - length = int(req.headers.get("content-length")) - content = req.content - ext = filetype.guess_extension(content) - filename = f"{code}_{counter}.{ext}" - save_path = pathlib.Path(tempdir, filename) - chunk_size = 4096 - downloaded = 0 - for chunk in req.iter_content(chunk_size): - text = tqdm_progress(f"Downloading: {filename}", length, downloaded) - edit_text(bm, text) - with open(save_path, "ab") as fp: - fp.write(chunk) - downloaded += len(chunk) - video_paths.append(save_path) - counter += 1 - - return video_paths - - -def pixeldrain(url: str, tempdir: str, bm, **kwargs): - user_page_url_regex = r"https://pixeldrain.com/u/(\w+)" - match = re.match(user_page_url_regex, url) - if match: - url = "https://pixeldrain.com/api/file/{}?download".format(match.group(1)) - return sp_ytdl_download(url, tempdir, bm, **kwargs) - else: - return url - - -def krakenfiles(url: str, tempdir: str, bm, **kwargs): - resp = requests.get(url) - html = resp.content - soup = BeautifulSoup(html, "html.parser") - link_parts = [] - token_parts = [] - for form_tag in soup.find_all("form"): - action = form_tag.get("action") - if action and "krakenfiles.com" in action: - link_parts.append(action) - input_tag = form_tag.find("input", {"name": "token"}) - if input_tag: - value = input_tag.get("value") - token_parts.append(value) - for link_part, token_part in zip(link_parts, token_parts): - link = f"https:{link_part}" - data = { - "token": token_part - } - response = requests.post(link, data=data) - json_data = response.json() - url = json_data["url"] - return sp_ytdl_download(url, tempdir, bm, **kwargs) - - -def find_between(s, start, end): - return (s.split(start))[1].split(end)[0] - -def terabox(url: str, tempdir: str, bm, **kwargs): - cookies_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "terabox.txt") - cookies = parse_cookie_file(cookies_file) - - headers = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "en-US,en;q=0.9,hi;q=0.8", - "Connection": "keep-alive", - "DNT": "1", - "Host": "www.terabox.app", - "Sec-Fetch-Dest": "document", - "Sec-Fetch-Mode": "navigate", - "Sec-Fetch-Site": "none", - "Sec-Fetch-User": "?1", - "Upgrade-Insecure-Requests": "1", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", - "sec-ch-ua": "'Not A(Brand';v='99', 'Google Chrome';v='121', 'Chromium';v='121'", - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": "'Windows'", - } - - session = requests.Session() - session.headers.update(headers) - session.cookies.update(cookies) - temp_req = session.get(url) - request_url = urlparse(temp_req.url) - surl = parse_qs(request_url.query).get("surl") - req = session.get(temp_req.url) - respo = req.text - js_token = find_between(respo, "fn%28%22", "%22%29") - logid = find_between(respo, "dp-logid=", "&") - bdstoken = find_between(respo, 'bdstoken":"', '"') - - params = { - "app_id": "250528", - "web": "1", - "channel": "dubox", - "clienttype": "0", - "jsToken": js_token, - "dp-logid": logid, - "page": "1", - "num": "20", - "by": "name", - "order": "asc", - "site_referer": temp_req.url, - "shorturl": surl, - "root": "1,", - } - - req2 = session.get("https://www.terabox.app/share/list", params=params) - response_data2 = req2.json() - file_name = response_data2["list"][0]["server_filename"] - sizebytes = int(response_data2["list"][0]["size"]) - if sizebytes > 48 * 1024 * 1024: - direct_link = response_data2["list"][0]["dlink"] - url = direct_link.replace("d.terabox.app", "d3.terabox.app") - else: - direct_link_response = session.head(response_data2["list"][0]["dlink"]) - direct_link_response_headers = direct_link_response.headers - direct_link = direct_link_response_headers["Location"] - url = direct_link - - return sp_ytdl_download(url, tempdir, bm, filename=file_name, **kwargs) \ No newline at end of file diff --git a/ytdlbot/split-video.sh b/ytdlbot/split-video.sh deleted file mode 100755 index 5265ea19..00000000 --- a/ytdlbot/split-video.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# Short script to split videos by filesize using ffmpeg by LukeLR - -if [ $# -ne 2 ]; then - echo 'Illegal number of parameters. Needs 2 parameters:' - echo 'Usage:' - echo './split-video.sh FILE SIZELIMIT "FFMPEG_ARGS' - echo - echo 'Parameters:' - echo ' - FILE: Name of the video file to split' - echo ' - SIZELIMIT: Maximum file size of each part (in bytes)' - echo ' - FFMPEG_ARGS: Additional arguments to pass to each ffmpeg-call' - echo ' (video format and quality options etc.)' - exit 1 -fi - -FILE="$1" -SIZELIMIT="$2" -FFMPEG_ARGS="$3" - -# Duration of the source video -DURATION=$(ffprobe -i "$FILE" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f1) - -# Duration that has been encoded so far -CUR_DURATION=0 - -# Filename of the source video (without extension) -BASENAME="${FILE%.*}" - -# Extension for the video parts -#EXTENSION="${FILE##*.}" -EXTENSION="mp4" - -# Number of the current video part -i=1 - -# Filename of the next video part -NEXTFILENAME="$BASENAME-$i.$EXTENSION" - -echo "Duration of source video: $DURATION" - -# Until the duration of all partial videos has reached the duration of the source video -while [[ $CUR_DURATION -lt $DURATION ]]; do - # Encode next part - echo ffmpeg -i "$FILE" -ss "$CUR_DURATION" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME" - ffmpeg -ss "$CUR_DURATION" -i "$FILE" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME" - - # Duration of the new part - NEW_DURATION=$(ffprobe -i "$NEXTFILENAME" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f1) - - # Total duration encoded so far - CUR_DURATION=$((CUR_DURATION + NEW_DURATION)) - - i=$((i + 1)) - - echo "Duration of $NEXTFILENAME: $NEW_DURATION" - echo "Part No. $i starts at $CUR_DURATION" - - NEXTFILENAME="$BASENAME-$i.$EXTENSION" -done \ No newline at end of file diff --git a/ytdlbot/tasks.py b/ytdlbot/tasks.py deleted file mode 100644 index 4178e870..00000000 --- a/ytdlbot/tasks.py +++ /dev/null @@ -1,735 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - tasks.py -# 12/29/21 14:57 -# - -__author__ = "Benny " - -import asyncio -import logging -import os -import pathlib -import re -import shutil -import subprocess -import tempfile -import threading -import time -import traceback -import typing -from typing import Any -from urllib.parse import quote_plus - -import filetype -import psutil -import pyrogram.errors -import requests -from apscheduler.schedulers.background import BackgroundScheduler -from celery import Celery -from celery.worker.control import Panel -from pyrogram import Client, enums, idle, types - -from channel import Channel -from client_init import create_app -from config import ( - ARCHIVE_ID, - BROKER, - ENABLE_CELERY, - ENABLE_VIP, - OWNER, - RATE_LIMIT, - RCLONE_PATH, - TMPFILE_PATH, - WORKERS, - FileTooBig, - CAPTION_URL_LENGTH_LIMIT, -) -from constant import BotText -from database import Redis, MySQL -from downloader import edit_text, tqdm_progress, upload_hook, ytdl_download -from sp_downloader import sp_dl -from limit import Payment -from utils import ( - apply_log_formatter, - auto_restart, - customize_logger, - get_metadata, - get_revision, - sizeof_fmt, - shorten_url, - extract_filename, -) - -customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) -apply_log_formatter() -bot_text = BotText() -logging.getLogger("apscheduler.executors.default").propagate = False - -app = Celery("tasks", broker=BROKER) -bot = create_app("tasks") -channel = Channel() - - -def retrieve_message(chat_id: int, message_id: int) -> types.Message | Any: - # this should only be called by celery tasks - try: - return bot.get_messages(chat_id, message_id) - except ConnectionError as e: - logging.critical("BOT IS NOT STARTED YET: %s", e) - bot.start() - return bot.get_messages(chat_id, message_id) - - -def premium_button(user_id): - redis = Redis() - payment = Payment() - used = redis.r.hget("premium", user_id) - ban = redis.r.hget("ban", user_id) - paid_token = payment.get_pay_token(user_id) - - if ban: - return None - # vip mode: vip user can use once per day, normal user can't use - # non vip mode: everyone can use once per day - if used or (ENABLE_VIP and paid_token == 0): - return None - - markup = types.InlineKeyboardMarkup( - [ - [ - types.InlineKeyboardButton("Yes", callback_data="premium-yes"), - types.InlineKeyboardButton("No", callback_data="premium-no"), - ] - ] - ) - return markup - - -@app.task(rate_limit=f"{RATE_LIMIT}/m") -def ytdl_download_task(chat_id: int, message_id: int, url: str): - logging.info("YouTube celery tasks started for %s", url) - bot_msg = retrieve_message(chat_id, message_id) - try: - ytdl_normal_download(bot, bot_msg, url) - except FileTooBig as e: - # if you can go there, that means you have premium users set up - logging.warning("Seeking for help from premium user...") - markup = premium_button(chat_id) - if markup: - bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) - else: - bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") - except Exception: - error_msg = traceback.format_exc().split("yt_dlp.utils.DownloadError: ERROR: ") - if len(error_msg) > 1: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg[-1]}", disable_web_page_preview=True) - else: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) - logging.info("YouTube celery tasks ended.") - - -@app.task() -def audio_task(chat_id: int, message_id: int): - logging.info("Audio celery tasks started for %s-%s", chat_id, message_id) - bot_msg = retrieve_message(chat_id, message_id) - normal_audio(bot, bot_msg) - logging.info("Audio celery tasks ended.") - - -@app.task() -def direct_download_task(chat_id: int, message_id: int, url: str): - logging.info("Direct download celery tasks started for %s", url) - bot_msg = retrieve_message(chat_id, message_id) - direct_normal_download(bot, bot_msg, url) - logging.info("Direct download celery tasks ended.") - - -@app.task() -def leech_download_task(chat_id: int, message_id: int, url: str): - logging.info("Leech download celery tasks started for %s", url) - bot_msg = retrieve_message(chat_id, message_id) - leech_normal_download(bot, bot_msg, url) - logging.info("Leech download celery tasks ended.") - - -def get_unique_clink(original_url: str, user_id: int): - payment = Payment() - settings = payment.get_user_settings(user_id) - clink = channel.extract_canonical_link(original_url) - try: - # different user may have different resolution settings - unique = "{}?p={}{}".format(clink, *settings[1:]) - except IndexError: - unique = clink - return unique - - -def forward_video(client, bot_msg: types.Message | Any, url: str, cached_fid: str): - res_msg = upload_processor(client, bot_msg, url, cached_fid) - obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo - - caption, _ = gen_cap(bot_msg, url, obj) - res_msg.edit_text(caption, reply_markup=gen_video_markup()) - bot_msg.edit_text(f"Download success!โœ…") - return True - - -def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): - # in Local node and forward mode, we pass client from main - # in celery mode, we need to use our own client called bot - payment = Payment() - redis = Redis() - chat_id = bot_msg.chat.id - unique = get_unique_clink(url, chat_id) - cached_fid = redis.get_send_cache(unique) - - try: - if cached_fid: - forward_video(client, bot_msg, url, cached_fid) - redis.update_metrics("cache_hit") - return - redis.update_metrics("cache_miss") - mode = mode or payment.get_user_settings(chat_id)[3] - if ENABLE_CELERY and mode in [None, "Celery"]: - # in celery mode, producer has lost control of this task. - ytdl_download_task.delay(chat_id, bot_msg.id, url) - else: - ytdl_normal_download(client, bot_msg, url) - except FileTooBig as e: - logging.warning("Seeking for help from premium user...") - # this is only for normal node. Celery node will need to do it in celery tasks - markup = premium_button(chat_id) - if markup: - bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) - else: - bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") - except Exception as e: - logging.error("Failed to download %s, error: %s", url, e) - error_msg = traceback.format_exc().split("yt_dlp.utils.DownloadError: ERROR: ") - if len(error_msg) > 1: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg[-1]}", disable_web_page_preview=True) - else: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{traceback.format_exc()[-2000:]}`", disable_web_page_preview=True) - - -def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, new_name): - if ENABLE_CELERY: - direct_normal_download(client, bot_msg, url, new_name) - # direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url) - else: - direct_normal_download(client, bot_msg, url, new_name) - - -def leech_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): - if ENABLE_CELERY: - leech_normal_download(client, bot_msg, url) - # leech_normal_download.delay(bot_msg.chat.id, bot_msg.id, url) - else: - leech_normal_download(client, bot_msg, url) - - -def spdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None): - payment = Payment() - redis = Redis() - chat_id = bot_msg.chat.id - unique = get_unique_clink(url, chat_id) - cached_fid = redis.get_send_cache(unique) - - try: - if cached_fid: - forward_video(client, bot_msg, url, cached_fid) - redis.update_metrics("cache_hit") - return - redis.update_metrics("cache_miss") - mode = mode or payment.get_user_settings(chat_id)[3] - spdl_normal_download(client, bot_msg, url) - except FileTooBig as e: - logging.warning("Seeking for help from premium user...") - # this is only for normal node. Celery node will need to do it in celery tasks - markup = premium_button(chat_id) - if markup: - bot_msg.edit_text(f"{e}\n\n{bot_text.premium_warning}", reply_markup=markup) - else: - bot_msg.edit_text(f"{e}\nBig file download is not available now. Please /buy or try again later ") - except ValueError as e: - logging.error("Invalid URL provided: %s", e) - bot_msg.edit_text(f"Download failed!โŒ\n\n{e}", disable_web_page_preview=True) - except Exception as e: - logging.error("Failed to download %s, error: %s", url, e) - error_msg = "Sorry, Something went wrong." - bot_msg.edit_text(f"Download failed!โŒ\n\n`{error_msg}", disable_web_page_preview=True) - - -def audio_entrance(client: Client, bot_msg: types.Message): - if ENABLE_CELERY: - audio_task.delay(bot_msg.chat.id, bot_msg.id) - else: - normal_audio(client, bot_msg) - - -def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str, new_name): - chat_id = bot_msg.chat.id - headers = { - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" - } - length = 0 - - req = None - try: - req = requests.get(url, headers=headers, stream=True) - length = int(req.headers.get("content-length")) - except Exception as e: - bot_msg.edit_text(f"Download failed!โŒ\n\n```{e}```", disable_web_page_preview=True) - return - - if new_name: - filename = new_name - else: - filename = extract_filename(req) - - with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as f: - filepath = f"{f}/{filename}" - # consume the req.content - downloaded = 0 - for chunk in req.iter_content(1024 * 1024): - text = tqdm_progress("Downloading...", length, downloaded) - edit_text(bot_msg, text) - with open(filepath, "ab") as fp: - fp.write(chunk) - downloaded += len(chunk) - logging.info("Downloaded file %s", filename) - st_size = os.stat(filepath).st_size - ext = filetype.guess_extension(filepath) - # Rename file if it doesn't have extension - if ext is not None and not filepath.endswith(ext): - new_filename = f"{filepath}.{ext}" - os.rename(filepath, new_filename) - filepath = new_filename - - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) - client.send_document( - bot_msg.chat.id, - filepath, - caption=f"filesize: {sizeof_fmt(st_size)}", - progress=upload_hook, - progress_args=(bot_msg,), - ) - bot_msg.edit_text("Download success!โœ…") - - -def leech_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str): - chat_id = bot_msg.chat.id - temp_dir = tempfile.TemporaryDirectory(prefix="leech_dl-", dir=TMPFILE_PATH) - tempdir = temp_dir.name - UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" - response = None - video_paths = None - # Download process using aria2c - try: - bot_msg.edit_text(f"Download Starting...", disable_web_page_preview=True) - # Command to download the link using aria2c - command = [ - "aria2c", - "-U", - UA, - "--max-tries=5", - "--console-log-level=warn", - "-d", - tempdir, - url, - ] - # Run the command using subprocess.Popen - process = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - line = "" - max_iterations = 100 # Set a reasonable maximum number of iterations - iteration = 0 - - while process.poll() is None and iteration < max_iterations: - line = process.stdout.readline().decode("utf-8") - if line.startswith("[#"): - line = line.strip() - bot_msg.edit_text(f"Downloading... \n\n`{line}`", disable_web_page_preview=True) - break - iteration += 1 - - if iteration >= max_iterations: - bot_msg.edit_text("Something went wrong. Please try again.", disable_web_page_preview=True) - except Exception as e: - bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`", disable_web_page_preview=True) - return - # Get filename and extension correctly after download - filepath = list(pathlib.Path(tempdir).glob("*")) - file_path_obj = filepath[0] - path_obj = pathlib.Path(file_path_obj) - filename = path_obj.name - logging.info("Downloaded file %s", filename) - bot_msg.edit_text(f"Download Complete", disable_web_page_preview=True) - ext = filetype.guess_extension(file_path_obj) - # Rename file if it doesn't have extension - if ext is not None and not filename.endswith(ext): - new_filename = f"{tempdir}/{filename}.{ext}" - os.rename(file_path_obj, new_filename) - # Get file path of the downloaded file to upload - video_paths = list(pathlib.Path(tempdir).glob("*")) - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) - upload_processor(client, bot_msg, url, video_paths) - bot_msg.edit_text("Download success!โœ…") - - -def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]): - chat_id = bot_msg.chat.id - # fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None) - status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text( - "Converting to audio...please wait patiently", quote=True - ) - orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0] - with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp: - client.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO) - # just try to download the audio using yt-dlp - filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]") - status_msg.edit_text("Sending audio now...") - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO) - for f in filepath: - client.send_audio(chat_id, f) - status_msg.edit_text("โœ… Conversion complete.") - Redis().update_metrics("audio_success") - - -def ytdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, url: str): - """ - This function is called by celery task or directly by bot - :param client: bot client, either from main or bot(celery) - :param bot_msg: bot message - :param url: url to download - """ - chat_id = bot_msg.chat.id - temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) - - video_paths = ytdl_download(url, temp_dir.name, bot_msg) - logging.info("Download complete.") - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) - bot_msg.edit_text("Download complete. Sending now...") - data = MySQL().get_user_settings(chat_id) - if data[4] == "ON": - logging.info("Adding to history...") - MySQL().add_history(chat_id, url, pathlib.Path(video_paths[0]).name) - try: - upload_processor(client, bot_msg, url, video_paths) - except pyrogram.errors.Flood as e: - logging.critical("FloodWait from Telegram: %s", e) - client.send_message( - chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.", - ) - client.send_message(OWNER, f"CRITICAL INFO: {e}") - time.sleep(e.value) - upload_processor(client, bot_msg, url, video_paths) - - bot_msg.edit_text("Download success!โœ…") - - # setup rclone environment var to back up the downloaded file - if RCLONE_PATH: - for item in os.listdir(temp_dir.name): - logging.info("Copying %s to %s", item, RCLONE_PATH) - shutil.copy(os.path.join(temp_dir.name, item), RCLONE_PATH) - temp_dir.cleanup() - - -def spdl_normal_download(client: Client, bot_msg: types.Message | typing.Any, url: str): - chat_id = bot_msg.chat.id - temp_dir = tempfile.TemporaryDirectory(prefix="spdl-", dir=TMPFILE_PATH) - - video_paths = sp_dl(url, temp_dir.name, bot_msg) - logging.info("Download complete.") - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) - bot_msg.edit_text("Download complete. Sending now...") - data = MySQL().get_user_settings(chat_id) - if data[4] == "ON": - logging.info("Adding to history...") - MySQL().add_history(chat_id, url, pathlib.Path(video_paths[0]).name) - try: - upload_processor(client, bot_msg, url, video_paths) - except pyrogram.errors.Flood as e: - logging.critical("FloodWait from Telegram: %s", e) - client.send_message( - chat_id, - f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.", - ) - client.send_message(OWNER, f"CRITICAL INFO: {e}") - time.sleep(e.value) - upload_processor(client, bot_msg, url, video_paths) - - bot_msg.edit_text("Download success!โœ…") - - if RCLONE_PATH: - for item in os.listdir(temp_dir.name): - logging.info("Copying %s to %s", item, RCLONE_PATH) - shutil.copy(os.path.join(temp_dir.name, item), RCLONE_PATH) - temp_dir.cleanup() - - -def generate_input_media(file_paths: list, cap: str, entities: list) -> list: - input_media = [] - for path in file_paths: - mime = filetype.guess_mime(path) - if "video" in mime: - input_media.append(pyrogram.types.InputMediaVideo(media=path)) - elif "image" in mime: - input_media.append(pyrogram.types.InputMediaPhoto(media=path)) - elif "audio" in mime: - input_media.append(pyrogram.types.InputMediaAudio(media=path)) - else: - input_media.append(pyrogram.types.InputMediaDocument(media=path)) - - # Add caption and entities of the first media - input_media[0].caption = cap - input_media[0].caption_entities = entities - - return input_media - - -def upload_processor(client: Client, bot_msg: types.Message, url: str, vp_or_fid: str | list): - redis = Redis() - # raise pyrogram.errors.exceptions.FloodWait(13) - # if is str, it's a file id; else it's a list of paths - payment = Payment() - chat_id = bot_msg.chat.id - markup = gen_video_markup() - if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1: - # just generate the first for simplicity, send as media group(2-20) - cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid[0]) - res_msg: list["types.Message"] | Any = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap, entities)) - # TODO no cache for now - return res_msg[0] - elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1: - # normal download, just contains one file in video_paths - vp_or_fid = vp_or_fid[0] - cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid) - else: - # just a file id as string - cap, entities, meta = gen_cap(bot_msg, url, vp_or_fid) - - settings = payment.get_user_settings(chat_id) - if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - chat_id = ARCHIVE_ID - - if settings[2] == "document": - logging.info("Sending as document") - try: - # send as document could be sent as video even if it's a document - res_msg = client.send_document( - chat_id, - vp_or_fid, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - reply_markup=markup, - thumb=meta["thumb"], - force_document=True, - ) - except ValueError: - logging.error("Retry to send as video") - res_msg = client.send_video( - chat_id, - vp_or_fid, - supports_streaming=True, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - reply_markup=markup, - **meta, - ) - elif settings[2] == "audio": - logging.info("Sending as audio") - res_msg = client.send_audio( - chat_id, - vp_or_fid, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - ) - else: - # settings==video - logging.info("Sending as video") - try: - res_msg = client.send_video( - chat_id, - vp_or_fid, - supports_streaming=True, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - reply_markup=markup, - **meta, - ) - except Exception: - # try to send as annimation, photo - try: - logging.warning("Retry to send as animation") - res_msg = client.send_animation( - chat_id, - vp_or_fid, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - reply_markup=markup, - **meta, - ) - except Exception: - # this is likely a photo - logging.warning("Retry to send as photo") - res_msg = client.send_photo( - chat_id, - vp_or_fid, - caption=cap, - caption_entities=entities, - progress=upload_hook, - progress_args=(bot_msg,), - ) - - unique = get_unique_clink(url, bot_msg.chat.id) - obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo - redis.add_send_cache(unique, getattr(obj, "file_id", None)) - redis.update_metrics("video_success") - if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path): - client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id) - return res_msg - - -def gen_cap(bm, url, video_path): - payment = Payment() - chat_id = bm.chat.id - user = bm.chat - try: - user_info = "@{}({})-{}".format(user.username or "N/A", user.first_name or "" + user.last_name or "", user.id) - except Exception: - user_info = "" - - if isinstance(video_path, pathlib.Path): - meta = get_metadata(video_path) - file_name = video_path.name - file_size = sizeof_fmt(os.stat(video_path).st_size) - else: - file_name = getattr(video_path, "file_name", "") - file_size = sizeof_fmt(getattr(video_path, "file_size", (2 << 2) + ((2 << 2) + 1) + (2 << 5))) - meta = dict( - width=getattr(video_path, "width", 0), - height=getattr(video_path, "height", 0), - duration=getattr(video_path, "duration", 0), - thumb=getattr(video_path, "thumb", None), - ) - free = payment.get_free_token(chat_id) - pay = payment.get_pay_token(chat_id) - if ENABLE_VIP: - remain = f"Download token count: free {free}, pay {pay}" - else: - remain = "" - - if worker_name := os.getenv("WORKER_NAME"): - worker = f"Downloaded by {worker_name}" - else: - worker = "" - # Shorten the URL if necessary - try: - if len(url) > CAPTION_URL_LENGTH_LIMIT: - url_for_cap = shorten_url(url, CAPTION_URL_LENGTH_LIMIT) - else: - url_for_cap = url - except Exception as e: - logging.warning(f"Error shortening URL: {e}") - url_for_cap = url - - cap = ( - f"{user_info}\n{file_name}\n\n{url_for_cap}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t" - f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}" - ) - - offset = len(f"{user_info}\n`{file_name}`\n\n") - - entities = [ - # For the filename - types.MessageEntity( - type=enums.MessageEntityType.CODE, - offset=len(f"{user_info}\n"), - length=len(file_name) - ), - # For the URL - types.MessageEntity( - type=enums.MessageEntityType.URL, - offset=offset, - length=len(url_for_cap) - ) - ] - - return cap, entities, meta - - -def gen_video_markup(): - markup = types.InlineKeyboardMarkup( - [ - [ # First row - types.InlineKeyboardButton( # Generates a callback query when pressed - "convert to audio", callback_data="convert" - ) - ] - ] - ) - return markup - - -@Panel.register -def ping_revision(*args): - return get_revision() - - -@Panel.register -def hot_patch(*args): - app_path = pathlib.Path().cwd().parent - logging.info("Hot patching on path %s...", app_path) - - pip_install = "pip install -r requirements.txt" - unset = "git config --unset http.https://github.com/.extraheader" - pull_unshallow = "git pull origin --unshallow" - pull = "git pull" - - subprocess.call(unset, shell=True, cwd=app_path) - if subprocess.call(pull_unshallow, shell=True, cwd=app_path) != 0: - logging.info("Already unshallow, pulling now...") - subprocess.call(pull, shell=True, cwd=app_path) - - logging.info("Code is updated, applying hot patch now...") - subprocess.call(pip_install, shell=True, cwd=app_path) - psutil.Process().kill() - - -def purge_tasks(): - count = app.control.purge() - return f"purged {count} tasks." - - -def run_celery(): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - worker_name = os.getenv("WORKER_NAME", "") - argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name] - app.worker_main(argv) - - -if __name__ == "__main__": - print("Bootstrapping Celery worker now.....") - time.sleep(5) - threading.Thread(target=run_celery, daemon=True).start() - - scheduler = BackgroundScheduler(timezone="Europe/London") - scheduler.add_job(auto_restart, "interval", seconds=900) - scheduler.start() - - idle() - bot.stop() diff --git a/ytdlbot/utils.py b/ytdlbot/utils.py deleted file mode 100644 index 59d5c93e..00000000 --- a/ytdlbot/utils.py +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - utils.py -# 9/1/21 22:50 -# - -__author__ = "Benny " - -import contextlib -import inspect as pyinspect -import logging -import os -import pathlib -import re -import shutil -import subprocess -import tempfile -import time -import uuid -from http.cookiejar import MozillaCookieJar -from urllib.parse import quote_plus - -import coloredlogs -import ffmpeg -import psutil - -from config import TMPFILE_PATH -from flower_tasks import app - -inspect = app.control.inspect() - - -def apply_log_formatter(): - coloredlogs.install( - level=logging.INFO, - fmt="[%(asctime)s %(filename)s:%(lineno)d %(levelname).1s] %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - - -def customize_logger(logger: list): - for log in logger: - logging.getLogger(log).setLevel(level=logging.INFO) - - -def sizeof_fmt(num: int, suffix="B"): - for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: - if abs(num) < 1024.0: - return "%3.1f%s%s" % (num, unit, suffix) - num /= 1024.0 - return "%.1f%s%s" % (num, "Yi", suffix) - - -def timeof_fmt(seconds: int): - periods = [("d", 86400), ("h", 3600), ("m", 60), ("s", 1)] - result = "" - for period_name, period_seconds in periods: - if seconds >= period_seconds: - period_value, seconds = divmod(seconds, period_seconds) - result += f"{int(period_value)}{period_name}" - return result - - -def is_youtube(url: str): - if url.startswith("https://www.youtube.com/") or url.startswith("https://youtu.be/"): - return True - - -def adjust_formats(user_id: int, url: str, formats: list, hijack=None): - from database import MySQL - - # high: best quality 1080P, 2K, 4K, 8K - # medium: 720P - # low: 480P - if hijack: - formats.insert(0, hijack) - return - - mapping = {"high": [], "medium": [720], "low": [480]} - settings = MySQL().get_user_settings(user_id) - if settings and is_youtube(url): - for m in mapping.get(settings[1], []): - formats.insert(0, f"bestvideo[ext=mp4][height={m}]+bestaudio[ext=m4a]") - formats.insert(1, f"bestvideo[vcodec^=avc][height={m}]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best") - - if settings[2] == "audio": - formats.insert(0, "bestaudio[ext=m4a]") - - if settings[2] == "document": - formats.insert(0, None) - - -def get_metadata(video_path): - width, height, duration = 1280, 720, 0 - try: - video_streams = ffmpeg.probe(video_path, select_streams="v") - for item in video_streams.get("streams", []): - height = item["height"] - width = item["width"] - duration = int(float(video_streams["format"]["duration"])) - except Exception as e: - logging.error(e) - try: - thumb = pathlib.Path(video_path).parent.joinpath(f"{uuid.uuid4().hex}-thunmnail.png").as_posix() - ffmpeg.input(video_path, ss=duration / 2).filter("scale", width, -1).output(thumb, vframes=1).run() - except ffmpeg._run.Error: - thumb = None - - return dict(height=height, width=width, duration=duration, thumb=thumb) - - -def current_time(ts=None): - return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts)) - - -def get_revision(): - with contextlib.suppress(subprocess.SubprocessError): - return subprocess.check_output("git -C ../ rev-parse --short HEAD".split()).decode("u8").replace("\n", "") - return "unknown" - - -def get_func_queue(func) -> int: - try: - count = 0 - data = getattr(inspect, func)() or {} - for _, task in data.items(): - count += len(task) - return count - except Exception: - return 0 - - -def tail_log(f, lines=1, _buffer=4098): - """Tail a file and get X lines from the end""" - # placeholder for the lines found - lines_found = [] - - # block counter will be multiplied by buffer - # to get the block size from the end - block_counter = -1 - - # loop until we find X lines - while len(lines_found) < lines: - try: - f.seek(block_counter * _buffer, os.SEEK_END) - except IOError: # either file is too small, or too many lines requested - f.seek(0) - lines_found = f.readlines() - break - - lines_found = f.readlines() - - # we found enough lines, get out - # Removed this line because it was redundant the while will catch - # it, I left it for history - # if len(lines_found) > lines: - # break - - # decrement the block counter to get the - # next X bytes - block_counter -= 1 - - return lines_found[-lines:] - - -class Detector: - def __init__(self, logs: str): - self.logs = logs - - @staticmethod - def func_name(): - with contextlib.suppress(Exception): - return pyinspect.stack()[1][3] - return "N/A" - - def auth_key_detector(self): - text = "Server sent transport error: 404 (auth key not found)" - if self.logs.count(text) >= 3: - logging.critical("auth key not found: %s", self.func_name()) - os.unlink("*.session") - return True - - def updates_too_long_detector(self): - # If you're seeing this, that means you have logged more than 10 device - # and the earliest account was kicked out. Restart the program could get you back in. - indicators = [ - "types.UpdatesTooLong", - "Got shutdown from remote", - "Code is updated", - "OSError: Connection lost", - "[Errno -3] Try again", - "MISCONF", - ] - for indicator in indicators: - if indicator in self.logs: - logging.critical("kick out crash: %s", self.func_name()) - return True - logging.debug("No crash detected.") - - def next_salt_detector(self): - text = "Next salt in" - if self.logs.count(text) >= 5: - logging.critical("Next salt crash: %s", self.func_name()) - return True - - def connection_reset_detector(self): - text = "Send exception: ConnectionResetError Connection lost" - if self.logs.count(text) >= 5: - logging.critical("connection lost: %s ", self.func_name()) - return True - - -def auto_restart(): - log_path = "/var/log/ytdl.log" - if not os.path.exists(log_path): - return - with open(log_path) as f: - logs = "".join(tail_log(f, lines=10)) - - det = Detector(logs) - method_list = [getattr(det, func) for func in dir(det) if func.endswith("_detector")] - for method in method_list: - if method(): - logging.critical("%s bye bye world!โ˜ ๏ธ", method) - for item in pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()).glob("ytdl-*"): - shutil.rmtree(item, ignore_errors=True) - time.sleep(5) - psutil.Process().kill() - - -def clean_tempfile(): - patterns = ["ytdl*", "spdl*", "leech*", "direct*"] - temp_path = pathlib.Path(TMPFILE_PATH or tempfile.gettempdir()) - - for pattern in patterns: - for item in temp_path.glob(pattern): - if time.time() - item.stat().st_ctime > 3600: - shutil.rmtree(item, ignore_errors=True) - - -def parse_cookie_file(cookiefile): - jar = MozillaCookieJar(cookiefile) - jar.load() - return {cookie.name: cookie.value for cookie in jar} - - -def extract_code_from_instagram_url(url): - # Regular expression patterns - patterns = [ - # Instagram stories highlights - r'/stories/highlights/([a-zA-Z0-9_-]+)/', - # Posts - r"/p/([a-zA-Z0-9_-]+)/", - # Reels - r"/reel/([a-zA-Z0-9_-]+)/", - # TV - r"/tv/([a-zA-Z0-9_-]+)/", - # Threads post (both with @username and without) - r'(?:https?://)?(?:www\.)?(?:threads\.net)(?:/[@\w.]+)?(?:/post)?/([\w-]+)(?:/?\?.*)?$' - ] - - for pattern in patterns: - match = re.search(pattern, url) - if match: - if pattern == patterns[0]: # Check if it's the stories highlights pattern - # Return the URL as it is - return url - else: - # Return the code part (first group) - return match.group(1) - - return None - - -def shorten_url(url, CAPTION_URL_LENGTH_LIMIT): - # Shortens a URL by cutting it to a specified length. - shortened_url = url[: CAPTION_URL_LENGTH_LIMIT - 3] + "..." - - return shortened_url - - -def extract_filename(response): - try: - content_disposition = response.headers.get("content-disposition") - if content_disposition: - filename = re.findall("filename=(.+)", content_disposition)[0] - return filename - except (TypeError, IndexError): - pass # Handle potential exceptions during extraction - - # Fallback if Content-Disposition header is missing - filename = response.url.rsplit("/")[-1] - if not filename: - filename = quote_plus(response.url) - return filename - - -def extract_url_and_name(message_text): - # Regular expression to match the URL - url_pattern = r'(https?://[^\s]+)' - # Regular expression to match the new name after '-n' - name_pattern = r'-n\s+([^\s]+)' - - # Find the URL in the message_text - url_match = re.search(url_pattern, message_text) - url = url_match.group(0) if url_match else None - - # Find the new name in the message_text - name_match = re.search(name_pattern, message_text) - new_name = name_match.group(1) if name_match else None - - return url, new_name - - -if __name__ == "__main__": - auto_restart() diff --git a/ytdlbot/ytdl_bot.py b/ytdlbot/ytdl_bot.py deleted file mode 100644 index 28cda9e4..00000000 --- a/ytdlbot/ytdl_bot.py +++ /dev/null @@ -1,772 +0,0 @@ -#!/usr/local/bin/python3 -# coding: utf-8 - -# ytdlbot - new.py -# 8/14/21 14:37 -# - -__author__ = "Benny " - -import contextlib -import json -import logging -import random -import re -import tempfile -import threading -import time -import traceback -from io import BytesIO -from typing import Any - -import psutil -import pyrogram.errors -import qrcode -import yt_dlp -from apscheduler.schedulers.background import BackgroundScheduler -from pyrogram import Client, enums, filters, types -from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant -from pyrogram.raw import functions -from pyrogram.raw import types as raw_types -from youtubesearchpython import VideosSearch - -from channel import Channel -from client_init import create_app -from config import ( - AUTHORIZED_USER, - ENABLE_CELERY, - ENABLE_FFMPEG, - ENABLE_VIP, - M3U8_SUPPORT, - OWNER, - PLAYLIST_SUPPORT, - PREMIUM_USER, - PROVIDER_TOKEN, - REQUIRED_MEMBERSHIP, - TOKEN_PRICE, - TRX_SIGNAL, - ENABLE_ARIA2, -) -from constant import BotText -from database import InfluxDB, MySQL, Redis -from limit import Payment, TronTrx -from tasks import app as celery_app -from tasks import ( - audio_entrance, - direct_download_entrance, - leech_download_entrance, - hot_patch, - purge_tasks, - ytdl_download_entrance, - spdl_download_entrance, -) -from utils import ( - sizeof_fmt, - timeof_fmt, - auto_restart, - clean_tempfile, - customize_logger, - get_revision, - extract_url_and_name, -) - -logging.info("Authorized users are %s", AUTHORIZED_USER) -customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"]) -logging.getLogger("apscheduler.executors.default").propagate = False - -app = create_app("main") -channel = Channel() - - -def private_use(func): - def wrapper(client: Client, message: types.Message): - chat_id = getattr(message.from_user, "id", None) - - # message type check - if message.chat.type != enums.ChatType.PRIVATE and not getattr(message, "text", "").lower().startswith("/ytdl"): - logging.debug("%s, it's annoying me...๐Ÿ™„๏ธ ", message.text) - return - - # authorized users check - if AUTHORIZED_USER: - users = [int(i) for i in AUTHORIZED_USER.split(",")] - else: - users = [] - - if users and chat_id and chat_id not in users: - message.reply_text(BotText.private, quote=True) - return - - if REQUIRED_MEMBERSHIP: - try: - member: types.ChatMember | Any = app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id) - if member.status not in [ - enums.ChatMemberStatus.ADMINISTRATOR, - enums.ChatMemberStatus.MEMBER, - enums.ChatMemberStatus.OWNER, - ]: - raise UserNotParticipant() - else: - logging.info("user %s check passed for group/channel %s.", chat_id, REQUIRED_MEMBERSHIP) - except UserNotParticipant: - logging.warning("user %s is not a member of group/channel %s", chat_id, REQUIRED_MEMBERSHIP) - message.reply_text(BotText.membership_require, quote=True) - return - - return func(client, message) - - return wrapper - - -@app.on_message(filters.command(["start"])) -def start_handler(client: Client, message: types.Message): - payment = Payment() - from_id = message.from_user.id - logging.info("%s welcome to youtube-dl bot!", message.from_user.id) - client.send_chat_action(from_id, enums.ChatAction.TYPING) - is_old_user = payment.check_old_user(from_id) - if is_old_user: - info = "" - if ENABLE_VIP: - free_token, pay_token, reset = payment.get_token(from_id) - info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}" - else: - info = "" - text = f"{BotText.start}\n\n{info}\n{BotText.custom_text}" - client.send_message(message.chat.id, text, disable_web_page_preview=True) - - -@app.on_message(filters.command(["help"])) -def help_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - client.send_message(chat_id, BotText.help, disable_web_page_preview=True) - - -@app.on_message(filters.command(["about"])) -def about_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - client.send_message(chat_id, BotText.about) - - -@app.on_message(filters.command(["sub"])) -def subscribe_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - if message.text == "/sub": - result = channel.get_user_subscription(chat_id) - else: - link = message.text.split()[1] - try: - result = channel.subscribe_channel(chat_id, link) - except (IndexError, ValueError): - result = f"Error: \n{traceback.format_exc()}" - client.send_message(chat_id, result or "You have no subscription.", disable_web_page_preview=True) - - -@app.on_message(filters.command(["unsub"])) -def unsubscribe_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - text = message.text.split(" ") - if len(text) == 1: - client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True) - return - - rows = channel.unsubscribe_channel(chat_id, text[1]) - if rows: - text = f"Unsubscribed from {text[1]}" - else: - text = "Unable to find the channel." - client.send_message(chat_id, text, disable_web_page_preview=True) - - -@app.on_message(filters.command(["patch"])) -def patch_handler(client: Client, message: types.Message): - username = message.from_user.username - chat_id = message.chat.id - if username == OWNER: - celery_app.control.broadcast("hot_patch") - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - client.send_message(chat_id, "Oorah!") - hot_patch() - - -@app.on_message(filters.command(["uncache"])) -def uncache_handler(client: Client, message: types.Message): - username = message.from_user.username - link = message.text.split()[1] - if username == OWNER: - count = channel.del_cache(link) - message.reply_text(f"{count} cache(s) deleted.", quote=True) - - -@app.on_message(filters.command(["purge"])) -def purge_handler(client: Client, message: types.Message): - username = message.from_user.username - if username == OWNER: - message.reply_text(purge_tasks(), quote=True) - - -@app.on_message(filters.command(["ping"])) -def ping_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_sent = False - - def send_message_and_measure_ping(): - start_time = int(round(time.time() * 1000)) - reply = client.send_message(chat_id, "Starting Ping...") - end_time = int(round(time.time() * 1000)) - ping_time = int(round(end_time - start_time)) - message_sent = True - if message_sent: - message.reply_text(f"Ping: {ping_time:.2f} ms", quote=True) - time.sleep(0.5) - client.edit_message_text(chat_id=reply.chat.id, message_id=reply.id, text="Ping Calculation Complete.") - time.sleep(1) - client.delete_messages(chat_id=reply.chat.id, message_ids=reply.id) - - thread = threading.Thread(target=send_message_and_measure_ping) - thread.start() - - -@app.on_message(filters.command(["stats"])) -def stats_handler(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - cpu_usage = psutil.cpu_percent() - total, used, free, disk = psutil.disk_usage("/") - swap = psutil.swap_memory() - memory = psutil.virtual_memory() - boot_time = psutil.boot_time() - - owner_stats = ( - "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" - f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" - f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" - f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" - f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" - f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" - f"Memory Total: {sizeof_fmt(memory.total)}\n" - f"Memory Free: {sizeof_fmt(memory.available)}\n" - f"Memory Used: {sizeof_fmt(memory.used)}\n" - f"SWAP Total: {sizeof_fmt(swap.total)} | SWAP Usage: {swap.percent}%\n\n" - f"Total Disk Space: {sizeof_fmt(total)}\n" - f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" - f"Physical Cores: {psutil.cpu_count(logical=False)}\n" - f"Total Cores: {psutil.cpu_count(logical=True)}\n\n" - f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" - f"โฒ๏ธOS Uptime: {timeof_fmt(time.time() - boot_time)}\n" - ) - - user_stats = ( - "\n\nโŒฌโ”€โ”€โ”€โ”€โ”€ใ€Œ Stats ใ€โ”€โ”€โ”€โ”€โ”€โŒฌ\n\n" - f"โ•ญ๐Ÿ–ฅ๏ธ **CPU Usage ยป** __{cpu_usage}%__\n" - f"โ”œ๐Ÿ’พ **RAM Usage ยป** __{memory.percent}%__\n" - f"โ•ฐ๐Ÿ—ƒ๏ธ **DISK Usage ยป** __{disk}%__\n\n" - f"โ•ญ๐Ÿ“คUpload: {sizeof_fmt(psutil.net_io_counters().bytes_sent)}\n" - f"โ•ฐ๐Ÿ“ฅDownload: {sizeof_fmt(psutil.net_io_counters().bytes_recv)}\n\n\n" - f"Memory Total: {sizeof_fmt(memory.total)}\n" - f"Memory Free: {sizeof_fmt(memory.available)}\n" - f"Memory Used: {sizeof_fmt(memory.used)}\n" - f"Total Disk Space: {sizeof_fmt(total)}\n" - f"Used: {sizeof_fmt(used)} | Free: {sizeof_fmt(free)}\n\n" - f"๐Ÿค–Bot Uptime: {timeof_fmt(time.time() - botStartTime)}\n" - ) - - if message.chat.username == OWNER: - message.reply_text(owner_stats, quote=True) - else: - message.reply_text(user_stats, quote=True) - - -@app.on_message(filters.command(["sub_count"])) -def sub_count_handler(client: Client, message: types.Message): - username = message.from_user.username - chat_id = message.chat.id - if username == OWNER: - with BytesIO() as f: - f.write(channel.sub_count().encode("u8")) - f.name = "subscription count.txt" - client.send_document(chat_id, f) - - -@app.on_message(filters.command(["show_history"])) -def show_history(client: Client, message: types.Message): - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - data = MySQL().show_history(chat_id) - if data: - client.send_message(chat_id, data, disable_web_page_preview=True) - else: - client.send_message(chat_id, "No history found.") - - -@app.on_message(filters.command(["clear_history"])) -def clear_history(client: Client, message: types.Message): - chat_id = message.chat.id - MySQL().clear_history(chat_id) - message.reply_text("History cleared.", quote=True) - - -@app.on_message(filters.command(["settings"])) -def settings_handler(client: Client, message: types.Message): - chat_id = message.chat.id - payment = Payment() - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - data = MySQL().get_user_settings(chat_id) - set_mode = data[3] - text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local") - mode_text = f"Download mode: **{set_mode}**\nHistory record: {data[4]}" - if message.chat.username == OWNER or payment.get_pay_token(chat_id): - extra = [types.InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)] - else: - extra = [] - - markup = types.InlineKeyboardMarkup( - [ - [ # First row - types.InlineKeyboardButton("send as document", callback_data="document"), - types.InlineKeyboardButton("send as video", callback_data="video"), - types.InlineKeyboardButton("send as audio", callback_data="audio"), - ], - [ # second row - types.InlineKeyboardButton("High Quality", callback_data="high"), - types.InlineKeyboardButton("Medium Quality", callback_data="medium"), - types.InlineKeyboardButton("Low Quality", callback_data="low"), - ], - [ - types.InlineKeyboardButton("Toggle History", callback_data=f"history-{data[4]}"), - ], - extra, - ] - ) - - try: - client.send_message(chat_id, BotText.settings.format(data[1], data[2]) + mode_text, reply_markup=markup) - except: - client.send_message( - chat_id, BotText.settings.format(data[1] + ".", data[2] + ".") + mode_text, reply_markup=markup - ) - - -@app.on_message(filters.command(["buy"])) -def buy_handler(client: Client, message: types.Message): - return client.send_message(message.chat.id, "This feature will comback soon...") - - # process as chat.id, not from_user.id - chat_id = message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - # currency USD - token_count = message.text.replace("/buy", "").strip() - if token_count.isdigit(): - price = int(int(token_count) / TOKEN_PRICE * 100) - else: - price = 100 - - markup = types.InlineKeyboardMarkup( - [ - [ - types.InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"), - types.InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"), - ], - ] - ) - client.send_message(chat_id, BotText.buy, disable_web_page_preview=True, reply_markup=markup) - - -@app.on_callback_query(filters.regex(r"tron-trx")) -def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery): - callback_query.answer("Generating QR code...") - chat_id = callback_query.message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - - addr = TronTrx().get_payment_address(chat_id) - with BytesIO() as bio: - qr = qrcode.make(addr) - qr.save(bio) - client.send_photo(chat_id, bio, caption=f"Send any amount of TRX to `{addr}`") - - -@app.on_callback_query(filters.regex(r"premium.*")) -def premium_click(client: Client, callback_query: types.CallbackQuery): - data = callback_query.data - if data == "premium-yes": - callback_query.answer("Seeking premium user...") - callback_query.message.edit_text("Please wait patiently...no progress bar will be shown.") - replied = callback_query.message.reply_to_message - data = {"url": replied.text, "user_id": callback_query.message.chat.id} - client.send_message(PREMIUM_USER, json.dumps(data), disable_notification=True, disable_web_page_preview=True) - else: - callback_query.answer("Cancelled.") - original_text = callback_query.message.text - callback_query.message.edit_text(original_text.split("\n")[0]) - - -@app.on_callback_query(filters.regex(r"bot-payments-.*")) -def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery): - callback_query.answer("Generating invoice...") - chat_id = callback_query.message.chat.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - - data = callback_query.data - price = int(data.split("-")[-1]) - payload = f"{chat_id}-buy" - invoice = generate_invoice(price, f"Buy {TOKEN_PRICE} download tokens", "Pay by card", payload) - app.invoke( - functions.messages.SendMedia( - peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)), - media=invoice, - random_id=app.rnd_id(), - message="Buy more download token", - ) - ) - - -@app.on_message(filters.command(["redeem"])) -def redeem_handler(client: Client, message: types.Message): - payment = Payment() - chat_id = message.chat.id - text = message.text.strip() - unique = text.replace("/redeem", "").strip() - msg = payment.verify_payment(chat_id, unique) - message.reply_text(msg, quote=True) - - -@app.on_message(filters.user(PREMIUM_USER) & filters.incoming & filters.caption) -def premium_forward(client: Client, message: types.Message): - media = message.video or message.audio or message.document - target_user = media.file_name.split(".")[0] - client.forward_messages(target_user, message.chat.id, message.id) - - -@app.on_message(filters.command(["ban"]) & filters.user(PREMIUM_USER)) -def ban_handler(client: Client, message: types.Message): - replied = message.reply_to_message.text - user_id = json.loads(replied).get("user_id") - redis = Redis() - redis.r.hset("ban", user_id, 1) - message.reply_text(f"Done, banned {user_id}.", quote=True) - - -def generate_invoice(amount: int, title: str, description: str, payload: str): - invoice = raw_types.input_media_invoice.InputMediaInvoice( - invoice=raw_types.invoice.Invoice( - currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)] - ), - title=title, - description=description, - provider=PROVIDER_TOKEN, - provider_data=raw_types.DataJSON(data="{}"), - payload=payload.encode(), - start_param=payload, - ) - return invoice - - -def link_checker(url: str) -> str: - if url.startswith("https://www.instagram.com"): - return "" - ytdl = yt_dlp.YoutubeDL() - - if not PLAYLIST_SUPPORT and ( - re.findall(r"^https://www\.youtube\.com/channel/", Channel.extract_canonical_link(url)) or "list" in url - ): - return "Playlist or channel links are disabled." - - if not M3U8_SUPPORT and (re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower())): - return "m3u8 links are disabled." - - with contextlib.suppress(yt_dlp.utils.DownloadError): - if ytdl.extract_info(url, download=False).get("live_status") == "is_live": - return "Live stream links are disabled. Please download it after the stream ends." - - -def search_ytb(kw: str): - videos_search = VideosSearch(kw, limit=10) - text = "" - results = videos_search.result()["result"] - for item in results: - title = item.get("title") - link = item.get("link") - index = results.index(item) + 1 - text += f"{index}. {title}\n{link}\n\n" - return text - - -@app.on_message(filters.command(["spdl"])) -def spdl_handler(client: Client, message: types.Message): - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text - url, new_name = extract_url_and_name(message_text) - logging.info("spdl start %s", url) - if url is None or not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("spdl_request") - spdl_download_entrance(client, bot_msg, url) - - -@app.on_message(filters.command(["direct"])) -def direct_handler(client: Client, message: types.Message): - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text - url, new_name = extract_url_and_name(message_text) - logging.info("direct start %s", url) - if url is None or not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Send me a DIRECT LINK.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("direct_request") - direct_download_entrance(client, bot_msg, url, new_name) - - -@app.on_message(filters.command(["leech"])) -def leech_handler(client: Client, message: types.Message): - if not ENABLE_ARIA2: - message.reply_text("Aria2 Not Enabled.", quote=True) - return - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text - url, new_name = extract_url_and_name(message_text) - logging.info("leech using aria2 start %s", url) - if url is None or not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Send me a correct LINK.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("leech_request") - leech_download_entrance(client, bot_msg, url) - - -@app.on_message(filters.command(["ytdl"])) -def ytdl_handler(client: Client, message: types.Message): - redis = Redis() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text - url, new_name = extract_url_and_name(message_text) - logging.info("ytdl start %s", url) - if url is None or not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - redis.update_metrics("ytdl_request") - ytdl_download_entrance(client, bot_msg, url) - - -@app.on_message(filters.incoming & (filters.text | filters.document)) -@private_use -def download_handler(client: Client, message: types.Message): - redis = Redis() - payment = Payment() - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - redis.user_count(chat_id) - if message.document: - with tempfile.NamedTemporaryFile(mode="r+") as tf: - logging.info("Downloading file to %s", tf.name) - message.download(tf.name) - contents = open(tf.name, "r").read() # don't know why - urls = contents.split() - else: - urls = [message.text] - logging.info("start %s", urls) - - for url in urls: - # check url - if not re.findall(r"^https?://", url.lower()): - redis.update_metrics("bad_request") - text = search_ytb(url) - message.reply_text(text, quote=True, disable_web_page_preview=True) - return - - if text := link_checker(url): - message.reply_text(text, quote=True) - redis.update_metrics("reject_link_checker") - return - - # old user is not limited by token - if ENABLE_VIP and not payment.check_old_user(chat_id): - free, pay, reset = payment.get_token(chat_id) - if free + pay <= 0: - message.reply_text(f"You don't have enough token. Please wait until {reset} or /buy .", quote=True) - redis.update_metrics("reject_token") - return - else: - payment.use_token(chat_id) - - redis.update_metrics("video_request") - - text = BotText.get_receive_link_text() - try: - # raise pyrogram.errors.exceptions.FloodWait(10) - bot_msg: types.Message | Any = message.reply_text(text, quote=True) - except pyrogram.errors.Flood as e: - f = BytesIO() - f.write(str(e).encode()) - f.write(b"Your job will be done soon. Just wait! Don't rush.") - f.name = "Please don't flood me.txt" - bot_msg = message.reply_document( - f, caption=f"Flood wait! Please wait {e} seconds...." f"Your job will start automatically", quote=True - ) - f.close() - client.send_message(OWNER, f"Flood wait! ๐Ÿ™ {e} seconds....") - time.sleep(e.value) - - client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) - bot_msg.chat = message.chat - ytdl_download_entrance(client, bot_msg, url) - - -@app.on_callback_query(filters.regex(r"document|video|audio")) -def send_method_callback(client: Client, callback_query: types.CallbackQuery): - chat_id = callback_query.message.chat.id - data = callback_query.data - logging.info("Setting %s file type to %s", chat_id, data) - MySQL().set_user_settings(chat_id, "method", data) - callback_query.answer(f"Your send type was set to {callback_query.data}") - - -@app.on_callback_query(filters.regex(r"high|medium|low")) -def download_resolution_callback(client: Client, callback_query: types.CallbackQuery): - chat_id = callback_query.message.chat.id - data = callback_query.data - logging.info("Setting %s file type to %s", chat_id, data) - MySQL().set_user_settings(chat_id, "resolution", data) - callback_query.answer(f"Your default download quality was set to {callback_query.data}") - - -@app.on_callback_query(filters.regex(r"history.*")) -def set_history_callback(client: Client, callback_query: types.CallbackQuery): - chat_id = callback_query.message.chat.id - data = callback_query.data.split("-")[-1] - - r = "OFF" if data == "ON" else "ON" - logging.info("Setting %s file type to %s", chat_id, data) - MySQL().set_user_settings(chat_id, "history", r) - callback_query.answer("History setting updated.") - - -@app.on_inline_query() -def inline_query(client: Client, inline_query: types.InlineQuery): - kw = inline_query.query - user_id = inline_query.from_user.id - data = MySQL().search_history(user_id, kw) - if data: - results = [ - types.InlineQueryResultArticle( - id=str(i), - title=item[1], - description=item[2], - input_message_content=types.InputTextMessageContent(item[1]), - ) - for i, item in enumerate(data) - ] - client.answer_inline_query(inline_query.id, results) - - -@app.on_callback_query(filters.regex(r"convert")) -def audio_callback(client: Client, callback_query: types.CallbackQuery): - redis = Redis() - if not ENABLE_FFMPEG: - callback_query.answer("Request rejected.") - callback_query.message.reply_text("Audio conversion is disabled now.") - return - - callback_query.answer(f"Converting to audio...please wait patiently") - redis.update_metrics("audio_request") - audio_entrance(client, callback_query.message) - - -@app.on_callback_query(filters.regex(r"Local|Celery")) -def owner_local_callback(client: Client, callback_query: types.CallbackQuery): - chat_id = callback_query.message.chat.id - MySQL().set_user_settings(chat_id, "mode", callback_query.data) - callback_query.answer(f"Download mode was changed to {callback_query.data}") - - -def periodic_sub_check(): - exceptions = pyrogram.errors.exceptions - for cid, uids in channel.group_subscriber().items(): - video_url = channel.has_newer_update(cid) - if video_url: - logging.info(f"periodic update:{video_url} - {uids}") - for uid in uids: - try: - app.send_message(uid, f"{video_url} is out. Watch it on YouTube") - except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e: - logging.warning("User is blocked or deleted. %s", e) - channel.deactivate_user_subscription(uid) - except Exception: - logging.error("Unknown error when sending message to user. %s", traceback.format_exc()) - finally: - time.sleep(random.random() * 3) - - -@app.on_raw_update() -def raw_update(client: Client, update, users, chats): - payment = Payment() - action = getattr(getattr(update, "message", None), "action", None) - if update.QUALNAME == "types.UpdateBotPrecheckoutQuery": - client.invoke( - functions.messages.SetBotPrecheckoutResults( - query_id=update.query_id, - success=True, - ) - ) - elif action and action.QUALNAME == "types.MessageActionPaymentSentMe": - logging.info("Payment received. %s", action) - uid = update.message.peer_id.user_id - amount = action.total_amount / 100 - payment.add_pay_user([uid, amount, action.charge.provider_charge_id, 0, amount * TOKEN_PRICE]) - client.send_message(uid, f"Thank you {uid}. Payment received: {amount} {action.currency}") - - -def trx_notify(_, **kwargs): - user_id = kwargs.get("user_id") - text = kwargs.get("text") - logging.info("Sending trx notification to %s", user_id) - app.send_message(user_id, text) - - -if __name__ == "__main__": - botStartTime = time.time() - MySQL() - TRX_SIGNAL.connect(trx_notify) - scheduler = BackgroundScheduler(timezone="Europe/London") - scheduler.add_job(auto_restart, "interval", seconds=600) - scheduler.add_job(clean_tempfile, "interval", seconds=120) - scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0) - scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120) - # scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1) - # default quota allocation of 10,000 units per day - # scheduler.add_job(periodic_sub_check, "interval", seconds=3600) - scheduler.start() - banner = f""" -โ–Œ โ–Œ โ–€โ–›โ–˜ โ–Œ โ–›โ–€โ–– โ–œ โ–Œ -โ–โ–ž โ–žโ–€โ–– โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–›โ–€โ–– โ–žโ–€โ–– โ–Œ โ–Œ โ–žโ–€โ–– โ–Œ โ–Œ โ–›โ–€โ–– โ– โ–žโ–€โ–– โ–โ–€โ–– โ–žโ–€โ–Œ - โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–Œ โ–›โ–€ โ–Œ โ–Œ โ–Œ โ–Œ โ–โ–โ– โ–Œ โ–Œ โ– โ–Œ โ–Œ โ–žโ–€โ–Œ โ–Œ โ–Œ - โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–˜ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€โ–˜ โ–€โ–€ โ–โ–€ โ–˜โ–˜ โ–˜ โ–˜ โ–˜ โ–โ–€ โ–โ–€โ–˜ โ–โ–€โ–˜ - -By @BennyThink, VIP mode: {ENABLE_VIP}, Celery Mode: {ENABLE_CELERY} -Version: {get_revision()} - """ - print(banner) - app.run() From 69c7ac5e2210a9987b5b57e9db7eaa3f6514db5b Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 19:34:04 +0100 Subject: [PATCH 238/297] update packages --- pdm.lock | 2 +- pre-push.py | 2 +- pyproject.toml | 2 +- requirements.txt | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index 46692ece..cfb303b8 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:a7c051a17c96e18dbeb071ab2c3ae06ece5e873dcda49778dce65ae94a1a82d5" +content_hash = "sha256:62a5c348249ff26e0426565d8acc8aeb10885e823a22478d917302b0b3635a4b" [[metadata.targets]] requires_python = ">3.9" diff --git a/pre-push.py b/pre-push.py index 17f6e49b..809fd2b2 100755 --- a/pre-push.py +++ b/pre-push.py @@ -19,4 +19,4 @@ # commit with amend subprocess.run(["git", "add", "requirements.txt"]) -subprocess.run(["git", "commit", "--amend", "--no-edit"]) +subprocess.run(["git", "commit", "-m", "pre-push"]) diff --git a/pyproject.toml b/pyproject.toml index 6cfb9c82..b5acb677 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.12", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.1", "redis>=5.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.12", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index ae5ffdeb..498353ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,8 @@ ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 filetype>=1.2.0 beautifulsoup4>=4.12.3 -fakeredis>=2.26.1 -redis>=5.2.0 +fakeredis>=2.26.2 +redis==5.2.1 requests>=2.32.3 tqdm>=4.67.1 token-bucket>=0.3.0 From 16617157954c77c84299665868a935b8c5b17d59 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 19:50:59 +0100 Subject: [PATCH 239/297] update dbdsn --- src/database/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/model.py b/src/database/model.py index 7db10ff1..d78d32c6 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -56,7 +56,7 @@ class Payment(Base): def create_session(): engine = create_engine( - "mysql+pymysql://root:root@localhost/ytdlbot", + os.getenv("DB_DSN"), pool_size=50, max_overflow=100, pool_timeout=30, From 58f205866eb50982030794aa4db7e3144cd16aa4 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 19:57:58 +0100 Subject: [PATCH 240/297] add logging for options --- src/engine/generic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/generic.py b/src/engine/generic.py index 6dba3060..92ea1b1d 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -3,11 +3,11 @@ # ytdlbot - generic.py +import logging import os from pathlib import Path import yt_dlp -from pyrogram import types from database.model import get_format_settings, get_quality_settings from engine.base import BaseDownloader @@ -96,6 +96,7 @@ def _download(self, formats) -> list: files = None for f in formats: ydl_opts["format"] = f + logging.info("yt-dlp options: %s", ydl_opts) with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([self._url]) files = list(Path(self._tempdir.name).glob("*")) From d2080f5046abc1748dad080bcdf56b3ef2e66190 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 20:04:21 +0100 Subject: [PATCH 241/297] use bigint --- src/database/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/model.py b/src/database/model.py index d78d32c6..848fabe8 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -5,7 +5,7 @@ from contextlib import contextmanager from typing import Literal -from sqlalchemy import Column, Enum, Float, ForeignKey, Integer, String, create_engine +from sqlalchemy import Column, Enum, Float, ForeignKey, Integer, String, create_engine, BigInteger from sqlalchemy.dialects.mysql import JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker @@ -22,7 +22,7 @@ class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) - user_id = Column(Integer, unique=True, nullable=False) # telegram user id + user_id = Column(BigInteger, unique=True, nullable=False) # telegram user id free = Column(Integer, default=FREE_DOWNLOAD) paid = Column(Integer, default=0) config = Column(JSON) From 5c52f46ef516533aa541fe41540dc3c458af7f55 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 14 Jan 2025 20:19:20 +0100 Subject: [PATCH 242/297] live detection --- src/engine/generic.py | 7 +++++++ src/main.py | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/engine/generic.py b/src/engine/generic.py index 92ea1b1d..6a8cc989 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -13,6 +13,12 @@ from engine.base import BaseDownloader +def match_filter(info_dict): + if info_dict.get("is_live"): + raise NotImplementedError("Skipping live video") + return None # Allow download for non-live videos + + class YoutubeDownload(BaseDownloader): @staticmethod def get_format(m): @@ -82,6 +88,7 @@ def _download(self, formats) -> list: "outtmpl": output, "restrictfilenames": False, "quiet": True, + "match_filter": match_filter, } # setup cookies for youtube only if self._url.startswith("https://www.youtube.com/") or self._url.startswith("https://youtu.be/"): diff --git a/src/main.py b/src/main.py index 40ebe353..53c0a02e 100644 --- a/src/main.py +++ b/src/main.py @@ -281,11 +281,6 @@ def check_link(url: str): if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): raise ValueError("m3u8 links are not supported.") - with contextlib.suppress(yt_dlp.utils.DownloadError): - # TODO remove it or try 'match_filter': '!is_live', - if ytdl.extract_info(url, download=False).get("live_status") == "is_live": - raise ValueError("Live stream links are disabled. Please engine it after the stream ends.") - @app.on_message(filters.incoming & filters.text) @private_use From da5ae16aeef7761e9228812b518bde75ebd2b67f Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 15 Jan 2025 01:16:23 +0530 Subject: [PATCH 243/297] Remove Terabox details from README.md (#476) --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 0f5b7ae9..c06e30d5 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,6 @@ Just send a link directly to the bot. * Instagram (Videos, Photos, Reels, IGTV & carousel) * Pixeldrain * KrakenFiles - * Terabox (file/~~folders~~) (you need to add cookies txt in ytdlbot folder with name) - [terabox.txt](https://github.com/ytdl-org/youtube-dl#how-do-i-pass-cookies-to-youtube-dl). # Features @@ -122,10 +120,6 @@ https://pixeldrain.com/u/765ijw9i https://krakenfiles.com/view/oqmSTF0T5t/file.html -## Test TeraBox - -https://terabox.com/s/1mpgNshrZVl6KuH717Hs23Q - # Donation From b8157dc1ffb80c950530de71f5b1d1a18cde6cee Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 15 Jan 2025 07:39:24 +0100 Subject: [PATCH 244/297] detect cookie file size --- src/engine/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/generic.py b/src/engine/generic.py index 6a8cc989..e3cd8fa6 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -92,7 +92,7 @@ def _download(self, formats) -> list: } # setup cookies for youtube only if self._url.startswith("https://www.youtube.com/") or self._url.startswith("https://youtu.be/"): - if os.path.isfile("youtube-cookies.txt"): + if os.path.isfile("youtube-cookies.txt") and os.path.getsize("youtube-cookies.txt") > 100: # src/cookies.txt ydl_opts["cookiefile"] = "youtube-cookies.txt" From 1554758705eda858a473fcc641b3357fb95d208b Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 15 Jan 2025 19:17:42 +0100 Subject: [PATCH 245/297] group fix --- src/database/model.py | 11 ++++++++++- src/engine/base.py | 20 ++++++++++++-------- src/engine/generic.py | 2 +- src/main.py | 14 +++++++------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/database/model.py b/src/database/model.py index 848fabe8..780a95f7 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -5,7 +5,16 @@ from contextlib import contextmanager from typing import Literal -from sqlalchemy import Column, Enum, Float, ForeignKey, Integer, String, create_engine, BigInteger +from sqlalchemy import ( + BigInteger, + Column, + Enum, + Float, + ForeignKey, + Integer, + String, + create_engine, +) from sqlalchemy.dialects.mysql import JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker diff --git a/src/engine/base.py b/src/engine/base.py index aa5b5d06..70b95b7e 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -50,7 +50,11 @@ class BaseDownloader(ABC): def __init__(self, client: Types.Client, bot_msg: Types.Message, url: str): self._client = client self._url = url - self._user_id = getattr(bot_msg.from_user, "id", None) or bot_msg.chat.id + # chat id is the same for private chat + self._chat_id = self._from_user = bot_msg.chat.id + if bot_msg.chat.type == enums.ChatType.GROUP or bot_msg.chat.type == enums.ChatType.SUPERGROUP: + # if in group, we need to find out who send the message + self._from_user = bot_msg.reply_to_message.from_user.id self._id = bot_msg.id self._tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") self._bot_msg: Types.Message = bot_msg @@ -61,12 +65,12 @@ def __del__(self): self._tempdir.cleanup() def _record_usage(self): - free, paid = get_free_quota(self._user_id), get_paid_quota(self._user_id) - logging.info("User %s has %s free and %s paid quota", self._user_id, free, paid) + free, paid = get_free_quota(self._from_user), get_paid_quota(self._from_user) + logging.info("User %s has %s free and %s paid quota", self._from_user, free, paid) if free + paid < 0: raise Exception("Usage limit exceeded") - use_quota(self._user_id) + use_quota(self._from_user) @staticmethod def __remove_bash_color(text): @@ -194,7 +198,7 @@ def get_metadata(self): return dict(height=height, width=width, duration=duration, thumb=thumb, caption=caption) def _upload(self): - upload = get_format_settings(self._user_id) + upload = get_format_settings(self._chat_id) # we only support single file upload files = list(Path(self._tempdir.name).glob("*")) meta = self.get_metadata() @@ -202,7 +206,7 @@ def _upload(self): success = SimpleNamespace(document=None, video=None, audio=None, animation=None, photo=None) if upload == "document": success = self.send_something( - chat_id=self._user_id, + chat_id=self._chat_id, files=files, _type="document", thumb=meta["thumb"], @@ -211,7 +215,7 @@ def _upload(self): ) elif upload == "audio": success = self.send_something( - chat_id=self._user_id, + chat_id=self._chat_id, files=files, _type="audio", caption=meta["caption"], @@ -220,7 +224,7 @@ def _upload(self): methods = {"video": meta, "animation": {}, "photo": {}} for method, thumb in methods.items(): try: - success = self.send_something(chat_id=self._user_id, files=files, _type=method, **meta) + success = self.send_something(chat_id=self._chat_id, files=files, _type=method, **meta) break except Exception as e: logging.error("Retry to send as %s, error:", method, e) diff --git a/src/engine/generic.py b/src/engine/generic.py index e3cd8fa6..be58910e 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -28,7 +28,7 @@ def get_format(m): ] def _setup_formats(self) -> list | None: - quality, format_ = get_quality_settings(self._user_id), get_format_settings(self._user_id) + quality, format_ = get_quality_settings(self._chat_id), get_format_settings(self._chat_id) # quality: high, medium, low, custom # format: audio, video, document formats = [] diff --git a/src/main.py b/src/main.py index 53c0a02e..ba7a37d9 100644 --- a/src/main.py +++ b/src/main.py @@ -255,20 +255,20 @@ def leech_handler(client: Client, message: types.Message): leech_download_entrance(client, bot_msg, url) -@app.on_message(filters.command(["ytdl"])) +@app.on_message(filters.command(["ytdl"]) & filters.group) def ytdl_handler(client: Client, message: types.Message): - # for group usage - chat_id = message.from_user.id - init_user(chat_id) - client.send_chat_action(chat_id, enums.ChatAction.TYPING) + # for group only + init_user(message.from_user.id) + client.send_chat_action(message.chat.id, enums.ChatAction.TYPING) message_text = message.text url, new_name = extract_url_and_name(message_text) logging.info("ytdl start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): - message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) + message.reply_text("Check your URL.", quote=True) return - bot_msg = message.reply_text("Request received.", quote=True) + bot_msg = message.reply_text("Group download request received.", quote=True) + youtube_entrance(client, bot_msg, url) From 7dddca31e5a1a69b8c0685197c760f8feae19923 Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 15 Jan 2025 19:49:04 +0100 Subject: [PATCH 246/297] cache done --- src/database/cache.py | 9 +++-- src/engine/base.py | 74 ++++++++++++++++++++++++++++++----------- src/engine/direct.py | 2 +- src/engine/generic.py | 3 +- src/engine/instagram.py | 2 +- src/main.py | 1 + 6 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/database/cache.py b/src/database/cache.py index 5fca7a0c..68089667 100644 --- a/src/database/cache.py +++ b/src/database/cache.py @@ -24,9 +24,8 @@ def __init__(self): def __del__(self): self.r.close() - def add_send_cache(self, link: str, file_id: str, _type: str): - # one link might have multiple files, so we use hset - self.r.hset(link, mapping={"file_id": file_id, "type": _type}) + def add_cache(self, key, mapping): + self.r.hset(key, mapping=mapping) - def get_send_cache(self, link: str): - return self.r.hgetall(link) + def get_cache(self, k: str): + return self.r.hgetall(k) diff --git a/src/engine/base.py b/src/engine/base.py index 70b95b7e..0cba9421 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -3,6 +3,8 @@ # ytdlbot - types.py +import hashlib +import json import logging import re import tempfile @@ -11,6 +13,7 @@ from io import StringIO from pathlib import Path from types import SimpleNamespace +from typing import final import ffmpeg import filetype @@ -59,6 +62,9 @@ def __init__(self, client: Types.Client, bot_msg: Types.Message, url: str): self._tempdir = tempfile.TemporaryDirectory(prefix="ytdl-") self._bot_msg: Types.Message = bot_msg self._redis = Redis() + self._quality = get_quality_settings(self._chat_id) + self._format = get_format_settings(self._chat_id) + self._record_usage() def __del__(self): @@ -132,10 +138,6 @@ def upload_hook(self, current, total): def edit_text(self, text: str): self._bot_msg.edit_text(text) - def get_cache_fileid(self): - unique = self._url + get_quality_settings(self._url) - return self._redis.get_send_cache(unique) - @abstractmethod def _setup_formats(self) -> list | None: pass @@ -157,7 +159,8 @@ def _methods(self): def send_something(self, *, chat_id, files, _type, caption=None, thumb=None, **kwargs): self._client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT) - if len(files) > 1: + is_cache = kwargs.pop("cache", False) + if len(files) > 1 and is_cache == False: inputs = generate_input_media(files, caption) return self._client.send_media_group(chat_id, inputs)[0] else: @@ -182,7 +185,7 @@ def get_metadata(self): width = item["width"] duration = int(float(video_streams["format"]["duration"])) except Exception as e: - logging.error(e) + logging.error("Error while getting metadata: %s", e) try: thumb = Path(video_path).parent.joinpath(f"{uuid.uuid4().hex}-thunmnail.png").as_posix() # A thumbnail's width and height should not exceed 320 pixels. @@ -197,14 +200,15 @@ def get_metadata(self): caption = f"{self._url}\n{filename}\n\nResolution: {width}x{height}\nDuration: {duration} seconds" return dict(height=height, width=width, duration=duration, thumb=thumb, caption=caption) - def _upload(self): - upload = get_format_settings(self._chat_id) - # we only support single file upload - files = list(Path(self._tempdir.name).glob("*")) - meta = self.get_metadata() + def _upload(self, files=None, meta=None): + if files is None: + files = list(Path(self._tempdir.name).glob("*")) + if meta is None: + meta = self.get_metadata() success = SimpleNamespace(document=None, video=None, audio=None, animation=None, photo=None) - if upload == "document": + if self._format == "document": + logging.info("Sending as document for %s", self._url) success = self.send_something( chat_id=self._chat_id, files=files, @@ -213,14 +217,16 @@ def _upload(self): force_document=True, caption=meta["caption"], ) - elif upload == "audio": + elif self._format == "audio": + logging.info("Sending as audio for %s", self._url) success = self.send_something( chat_id=self._chat_id, files=files, _type="audio", caption=meta["caption"], ) - elif upload == "video": + elif self._format == "video": + logging.info("Sending as video for %s", self._url) methods = {"video": meta, "animation": {}, "photo": {}} for method, thumb in methods.items(): try: @@ -229,17 +235,47 @@ def _upload(self): except Exception as e: logging.error("Retry to send as %s, error:", method, e) else: - logging.error("Unknown upload settings") + logging.error("Unknown upload format settings") return - # unique link is link+download_format - unique = self._url + get_quality_settings(self._url) + upload + video_key = self._calc_video_key() obj = success.document or success.video or success.audio or success.animation or success.photo - self._redis.add_send_cache(unique, getattr(obj, "file_id", None), upload) + mapping = { + "file_id": json.dumps([getattr(obj, "file_id", None)]), + "meta": json.dumps({k: v for k, v in meta.items() if k != "thumb"}, ensure_ascii=False), + } + + self._redis.add_cache(video_key, mapping) # change progress bar to done self._bot_msg.edit_text("โœ… Success") return success - @abstractmethod + def _get_video_cache(self): + return self._redis.get_cache(self._calc_video_key()) + + def _calc_video_key(self): + h = hashlib.md5() + h.update((self._url + self._quality + self._format).encode()) + key = h.hexdigest() + return key + + @final def start(self): + if cache := self._get_video_cache(): + logging.info("Cache hit for %s", self._url) + sample = { + "meta": '{"height": 628, "width": 1280, "duration": 4, "caption": "https://www.youtube.com/watch?v=V3RtA-1b_2E\\ntest1.mp4\\n\\nResolution: 1280x628\\nDuration: 4 seconds"}', + "format": "video", # not needed? + "quality": "high", # not needed? + "file_id": "BAACAgUAAxkDAAJkPmeICCtKWhS-uK5TyU_7r8ppPIR6AAJxFQACV8xAVLn6LvM7FMvzHgQ", + } + meta, file_id = json.loads(cache["meta"]), json.loads(cache["file_id"]) + meta["cache"] = True + self._upload(file_id, meta) + + else: + self._start() + + @abstractmethod + def _start(self): pass diff --git a/src/engine/direct.py b/src/engine/direct.py index 187fb08f..7e3664a6 100644 --- a/src/engine/direct.py +++ b/src/engine/direct.py @@ -90,6 +90,6 @@ def _download(self, formats=None) -> list: return self._aria2_download() return self._requests_download() - def start(self): + def _start(self): self._download() self._upload() diff --git a/src/engine/generic.py b/src/engine/generic.py index be58910e..374f7898 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -111,7 +111,8 @@ def _download(self, formats) -> list: return files - def start(self, formats=None): + def _start(self, formats=None): + # start download and upload, no cache hit # user can choose format by clicking on the button(custom config) default_formats = self._setup_formats() if formats is not None: diff --git a/src/engine/instagram.py b/src/engine/instagram.py index 3921a7f2..7aa479d7 100644 --- a/src/engine/instagram.py +++ b/src/engine/instagram.py @@ -70,5 +70,5 @@ def _download(self, formats): return video_paths - def start(self): + def _start(self): pass diff --git a/src/main.py b/src/main.py index ba7a37d9..672b1633 100644 --- a/src/main.py +++ b/src/main.py @@ -309,6 +309,7 @@ def download_handler(client: Client, message: types.Message): except ValueError as e: message.reply_text(e.__str__(), quote=True) except Exception as e: + logging.error("Download failed", exc_info=True) message.reply_text(f"โŒ Download failed: {e}", quote=True) From bac0b4f7c23cecc949a4c07677ffb357c26da884 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 16 Jan 2025 07:30:42 +0100 Subject: [PATCH 247/297] add potoken --- .env.example | 3 +++ src/engine/generic.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.env.example b/.env.example index ff8d3cb8..c7644f62 100644 --- a/.env.example +++ b/.env.example @@ -59,3 +59,6 @@ TG_NORMAL_MAX_SIZE=2000 # Maximum URL length in captions CAPTION_URL_LENGTH_LIMIT=150 + +# potoken https://github.com/yt-dlp/yt-dlp/wiki/Extractors#po-token-guide +POTOKEN=11 \ No newline at end of file diff --git a/src/engine/generic.py b/src/engine/generic.py index 374f7898..1d32b985 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -95,6 +95,9 @@ def _download(self, formats) -> list: if os.path.isfile("youtube-cookies.txt") and os.path.getsize("youtube-cookies.txt") > 100: # src/cookies.txt ydl_opts["cookiefile"] = "youtube-cookies.txt" + # try add extract_args if present + if potoken := os.getenv("POTOKEN"): + ydl_opts["extractor_args"] = {"youtube": ["player-client=web,default", f"po_token=web+{potoken}"]} if self._url.startswith("https://drive.google.com"): # Always use the `source` format for Google Drive URLs. From 22b51619242b7cce7e5ced5957e4f19608b03a27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:43:03 +0100 Subject: [PATCH 248/297] Bump yt-dlp from 2025.1.12 to 2025.1.15 (#477) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2025.1.12 to 2025.1.15. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.01.12...2025.01.15) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 498353ad..48a323c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp==2025.1.12 +yt-dlp==2025.1.15 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 6526bd557bbfb8c58cfabfb8ef1e33eccd681858 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 16 Jan 2025 19:42:36 +0100 Subject: [PATCH 249/297] only record success --- src/engine/base.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/engine/base.py b/src/engine/base.py index 0cba9421..5edba30a 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -65,8 +65,6 @@ def __init__(self, client: Types.Client, bot_msg: Types.Message, url: str): self._quality = get_quality_settings(self._chat_id) self._format = get_format_settings(self._chat_id) - self._record_usage() - def __del__(self): self._tempdir.cleanup() @@ -263,18 +261,12 @@ def _calc_video_key(self): def start(self): if cache := self._get_video_cache(): logging.info("Cache hit for %s", self._url) - sample = { - "meta": '{"height": 628, "width": 1280, "duration": 4, "caption": "https://www.youtube.com/watch?v=V3RtA-1b_2E\\ntest1.mp4\\n\\nResolution: 1280x628\\nDuration: 4 seconds"}', - "format": "video", # not needed? - "quality": "high", # not needed? - "file_id": "BAACAgUAAxkDAAJkPmeICCtKWhS-uK5TyU_7r8ppPIR6AAJxFQACV8xAVLn6LvM7FMvzHgQ", - } meta, file_id = json.loads(cache["meta"]), json.loads(cache["file_id"]) meta["cache"] = True self._upload(file_id, meta) - else: self._start() + self._record_usage() @abstractmethod def _start(self): From b81befb2c9d2e1e1fe08c9f21c7a8818674114aa Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 16 Jan 2025 19:43:48 +0100 Subject: [PATCH 250/297] update yt-dlp --- pdm.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index cfb303b8..08792779 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:62a5c348249ff26e0426565d8acc8aeb10885e823a22478d917302b0b3635a4b" +content_hash = "sha256:c6252b55edc18b06e437fbcbcf1779d6ad0258fc74837a573cff764bbe31b0c8" [[metadata.targets]] requires_python = ">3.9" @@ -833,11 +833,11 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.1.12" +version = "2025.1.15" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.1.12-py3-none-any.whl", hash = "sha256:f7ea19afb64f8e457a1b9598ddb67f8deaa313bf1d57abd5612db9272ab10795"}, - {file = "yt_dlp-2025.1.12.tar.gz", hash = "sha256:8e7e246e2a5a2cff0a9c13db46844a37a547680702012058c94ec18fce0ca25a"}, + {file = "yt_dlp-2025.1.15-py3-none-any.whl", hash = "sha256:b8666b88e23c3fa5ee1e80920f4a9dfac7c405504a447214c0cf3d0c386edcfc"}, + {file = "yt_dlp-2025.1.15.tar.gz", hash = "sha256:e8ec515d49bb62704915d13a22ee6fe03a5658d651e4e64574e3a17ee01f6e3b"}, ] diff --git a/pyproject.toml b/pyproject.toml index b5acb677..182c8ff3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.12", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.15", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} From 96eb708dcacd434415338bdeea38d5678353e7a3 Mon Sep 17 00:00:00 2001 From: Benny Date: Thu, 16 Jan 2025 20:28:38 +0100 Subject: [PATCH 251/297] payment --- src/database/model.py | 42 ++++++++++++++++++++++++++++++++++++++++-- src/main.py | 39 ++++++++++++++++++++++++++++++++++++++- src/test.py | 14 ++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/test.py diff --git a/src/database/model.py b/src/database/model.py index 780a95f7..90a5904e 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # coding: utf-8 +import logging import math import os from contextlib import contextmanager @@ -21,7 +22,12 @@ from config import FREE_DOWNLOAD -# ytdlbot - model.py + +class PaymentStatus: + PENDING = "pending" + COMPLETED = "completed" + FAILED = "failed" + REFUNDED = "refunded" Base = declarative_base() @@ -57,7 +63,16 @@ class Payment(Base): id = Column(Integer, primary_key=True, autoincrement=True) method = Column(String(50), nullable=False) amount = Column(Float, nullable=False) - status = Column(Enum("pending", "completed", "failed", "refunded"), nullable=False) + status = Column( + Enum( + PaymentStatus.PENDING, + PaymentStatus.COMPLETED, + PaymentStatus.FAILED, + PaymentStatus.REFUNDED, + ), + nullable=False, + ) + transaction_id = Column(String(100)) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) user = relationship("User", back_populates="payments") @@ -181,3 +196,26 @@ def reset_free(): for user in users: user.free = FREE_DOWNLOAD session.commit() + + +def credit_account(who, total_amount, transaction, method="stripe"): + with session_manager() as session: + user = session.query(User).filter(User.user_id == who).first() + if user: + dollar = total_amount / 100 + price = int(os.getenv("TOKEN_PRICE")) # per one dollar + user.paid += int(dollar * price) + logging.info("user %d credited with %d tokens, payment:$%.2f", who, user.paid, dollar) + session.add( + Payment( + method=method, + amount=total_amount, + status=PaymentStatus.COMPLETED, + transaction_id=transaction, + user_id=user.id, + ) + ) + session.commit() + return user.free, user.paid + + return None, None diff --git a/src/main.py b/src/main.py index 672b1633..06789738 100644 --- a/src/main.py +++ b/src/main.py @@ -7,8 +7,8 @@ __author__ = "Benny " -import contextlib import logging +import os import re import threading import time @@ -36,6 +36,7 @@ BotText, ) from database.model import ( + credit_account, get_format_settings, get_free_quota, get_paid_quota, @@ -144,6 +145,42 @@ def send_message_and_measure_ping(): thread.start() +@app.on_message(filters.command(["buy"])) +def buy(client: Client, message: types.Message): + chat_id = message.chat.id + logging.info("Generating invoice for %s", chat_id) + price = os.getenv("TOKEN_PRICE") + client.send_invoice( + chat_id, + f"{price} permanent download", + "Please make a payment via Stripe", + f"vip-{chat_id}", + "USD", + [types.LabeledPrice(label="VIP", amount=100)], + provider_token=os.getenv("PROVIDER_TOKEN"), + protect_content=True, + start_parameter="no-forward", + ) + + +@app.on_pre_checkout_query() +def pre_checkout(client: Client, query: types.PreCheckoutQuery): + client.answer_pre_checkout_query(query.id, ok=True) + + +@app.on_message(filters.successful_payment) +def successful_payment(client: Client, message: types.Message): + who = message.chat.id + amount = message.successful_payment.total_amount # in cents + ch = message.successful_payment.provider_payment_charge_id + free, paid = credit_account(who, amount, ch) + if paid > 0: + message.reply_text(f"Payment successful! You now have {free} free and {paid} paid quota.") + else: + message.reply_text("Something went wrong. Please contact the admin.") + message.delete() + + @app.on_message(filters.command(["stats"])) def stats_handler(client: Client, message: types.Message): chat_id = message.chat.id diff --git a/src/test.py b/src/test.py new file mode 100644 index 00000000..cd8bd8fb --- /dev/null +++ b/src/test.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +# ytdlbot - test.py + +import yt_dlp + +url = "https://www.youtube.com/watch?v=e19kTVgb2c8" +opts = { + "cookiefile": "cookies.txt", +} + +with yt_dlp.YoutubeDL(opts) as ydl: + ydl.download([url]) From f8dfdbb28aad4958c42f61d4133c3cf279731a9a Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 18 Jan 2025 17:10:23 +0100 Subject: [PATCH 252/297] add cookies from browser --- .env.example | 4 +++- src/engine/generic.py | 4 +++- src/test.py | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index c7644f62..96b28788 100644 --- a/.env.example +++ b/.env.example @@ -61,4 +61,6 @@ TG_NORMAL_MAX_SIZE=2000 CAPTION_URL_LENGTH_LIMIT=150 # potoken https://github.com/yt-dlp/yt-dlp/wiki/Extractors#po-token-guide -POTOKEN=11 \ No newline at end of file +POTOKEN=11 + +BROWSERS=firefox \ No newline at end of file diff --git a/src/engine/generic.py b/src/engine/generic.py index 1d32b985..a12a90d6 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -92,8 +92,10 @@ def _download(self, formats) -> list: } # setup cookies for youtube only if self._url.startswith("https://www.youtube.com/") or self._url.startswith("https://youtu.be/"): + # use cookies from browser firstly + if browsers := os.getenv("BROWSERS"): + ydl_opts["cookiesfrombrowser"] = browsers.split(",") if os.path.isfile("youtube-cookies.txt") and os.path.getsize("youtube-cookies.txt") > 100: - # src/cookies.txt ydl_opts["cookiefile"] = "youtube-cookies.txt" # try add extract_args if present if potoken := os.getenv("POTOKEN"): diff --git a/src/test.py b/src/test.py index cd8bd8fb..f8a0f71e 100644 --- a/src/test.py +++ b/src/test.py @@ -8,6 +8,7 @@ url = "https://www.youtube.com/watch?v=e19kTVgb2c8" opts = { "cookiefile": "cookies.txt", + "cookiesfrombrowser": ["firefox"], } with yt_dlp.YoutubeDL(opts) as ydl: From f8313d1d2d0e62893d39f14b9b0313f0d1c0beec Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 18 Jan 2025 20:06:34 +0100 Subject: [PATCH 253/297] update links --- README.md | 2 +- src/config/constant.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c06e30d5..74252925 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This Telegram bot allows you to download videos from YouTube and [other supporte * EU๐Ÿ‡ช๐Ÿ‡บ: [https://t.me/benny_2ytdlbot](https://t.me/benny_2ytdlbot) * Singapore๐Ÿ‡ธ๐Ÿ‡ฌ:[https://t.me/benny_ytdlbot](https://t.me/benny_ytdlbot) -* Join Telegram Channel https://t.me/+OGRC8tp9-U9mZDZl for updates. +* Join Telegram Channel https://t.me/ytdlbot0 for updates. Just send a link directly to the bot. diff --git a/src/config/constant.py b/src/config/constant.py index 1bf5d787..e6378d60 100644 --- a/src/config/constant.py +++ b/src/config/constant.py @@ -19,14 +19,14 @@ class BotText: EU๐Ÿ‡ช๐Ÿ‡บ: @benny_2ytdlbot SG๐Ÿ‡ธ๐Ÿ‡ฌ๏ผš@benny_ytdlbot - Join https://t.me/+OGRC8tp9-U9mZDZl for updates.\n\n""" + Join https://t.me/ytdlbot0 for updates.\n\n""" help = """ 1. For YouTube and any websites supported by yt-dlp, just send the link and we will engine and send it to you. 2. For specific links use `/spdl {URL}`. More info at https://github.com/SanujaNS/ytdlbot-telegram#supported-websites -3. If the bot doesn't work, try again or join https://t.me/+OGRC8tp9-U9mZDZl for updates. +3. If the bot doesn't work, try again or join https://t.me/ytdlbot0 for updates. 4. Want to deploy it yourself?\nHere's the source code: https://github.com/tgbot-collection/ytdlbot """ From 85de9da6c4715dba7deb29d6fb1c945a578ce34f Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 19 Jan 2025 16:51:48 +0100 Subject: [PATCH 254/297] flex payment --- .env.example | 3 --- src/database/model.py | 5 ++--- src/main.py | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 96b28788..a22dbd43 100644 --- a/.env.example +++ b/.env.example @@ -45,9 +45,6 @@ PROVIDER_TOKEN= # Free downloads allowed per user FREE_DOWNLOAD=5 -# Token price (default: 10 credits for 1 USD) -TOKEN_PRICE=10 - # Rate limit for requests RATE_LIMIT=120 diff --git a/src/database/model.py b/src/database/model.py index 90a5904e..d9f9ae34 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -198,13 +198,12 @@ def reset_free(): session.commit() -def credit_account(who, total_amount, transaction, method="stripe"): +def credit_account(who, total_amount: int, quota: int, transaction, method="stripe"): with session_manager() as session: user = session.query(User).filter(User.user_id == who).first() if user: dollar = total_amount / 100 - price = int(os.getenv("TOKEN_PRICE")) # per one dollar - user.paid += int(dollar * price) + user.paid += quota logging.info("user %d credited with %d tokens, payment:$%.2f", who, user.paid, dollar) session.add( Payment( diff --git a/src/main.py b/src/main.py index 06789738..a95f39b2 100644 --- a/src/main.py +++ b/src/main.py @@ -147,19 +147,39 @@ def send_message_and_measure_ping(): @app.on_message(filters.command(["buy"])) def buy(client: Client, message: types.Message): - chat_id = message.chat.id - logging.info("Generating invoice for %s", chat_id) - price = os.getenv("TOKEN_PRICE") + markup = types.InlineKeyboardMarkup( + [ + [ # First row + types.InlineKeyboardButton("10-$1", callback_data="buy-10-1"), + types.InlineKeyboardButton("20-$2", callback_data="buy-20-2"), + types.InlineKeyboardButton("40-$3.5", callback_data="buy-40-3.5"), + ], + [ # second row + types.InlineKeyboardButton("50-$4", callback_data="buy-50-4"), + types.InlineKeyboardButton("75-$6", callback_data="buy-75-6"), + types.InlineKeyboardButton("100-$8", callback_data="buy-100-8"), + ], + ] + ) + message.reply_text("Please choose the amount you want to buy.", reply_markup=markup) + + +@app.on_callback_query(filters.regex(r"buy.*")) +def send_invoice(client: Client, callback_query: types.CallbackQuery): + chat_id = callback_query.message.chat.id + data = callback_query.data + _, count, price = data.split("-") + price = int(float(price) * 100) client.send_invoice( chat_id, - f"{price} permanent download", + f"{count} permanent download quota", "Please make a payment via Stripe", - f"vip-{chat_id}", + f"{count}", "USD", - [types.LabeledPrice(label="VIP", amount=100)], + [types.LabeledPrice(label="VIP", amount=price)], provider_token=os.getenv("PROVIDER_TOKEN"), protect_content=True, - start_parameter="no-forward", + start_parameter="no-forward-placeholder", ) @@ -172,8 +192,9 @@ def pre_checkout(client: Client, query: types.PreCheckoutQuery): def successful_payment(client: Client, message: types.Message): who = message.chat.id amount = message.successful_payment.total_amount # in cents + quota = int(message.successful_payment.invoice_payload) ch = message.successful_payment.provider_payment_charge_id - free, paid = credit_account(who, amount, ch) + free, paid = credit_account(who, amount, quota, ch) if paid > 0: message.reply_text(f"Payment successful! You now have {free} free and {paid} paid quota.") else: From fceab0097d06c9ebd8dd8fe98f7e27e44c5c9c37 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 24 Jan 2025 17:27:11 +0100 Subject: [PATCH 255/297] quota fix --- src/database/model.py | 9 ++++++++- src/engine/base.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/database/model.py b/src/database/model.py index d9f9ae34..17838a00 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -170,6 +170,13 @@ def add_paid_quota(uid: int, amount: int): data.paid += amount +def check_quota(uid: int): + with session_manager() as session: + data = session.query(User).filter(User.user_id == uid).first() + if data and (data.free + data.paid) <= 0: + raise Exception("Quota exhausted. Please /buy or wait until free quota is reset") + + def use_quota(uid: int): # use free first, then paid with session_manager() as session: @@ -180,7 +187,7 @@ def use_quota(uid: int): elif user.paid > 0: user.paid -= 1 else: - raise Exception("Quota exhausted") + raise Exception("Quota exhausted. Please /buy or wait until free quota is reset") def init_user(uid: int): diff --git a/src/engine/base.py b/src/engine/base.py index 5edba30a..4390e64d 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -23,6 +23,7 @@ from config import TG_NORMAL_MAX_SIZE, Types from database import Redis from database.model import ( + check_quota, get_format_settings, get_free_quota, get_paid_quota, @@ -259,6 +260,7 @@ def _calc_video_key(self): @final def start(self): + check_quota(self._from_user) if cache := self._get_video_cache(): logging.info("Cache hit for %s", self._url) meta, file_id = json.loads(cache["meta"]), json.loads(cache["file_id"]) From b97955153c6cae776f920208f026e9ea8261b93f Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 26 Jan 2025 23:18:35 +0530 Subject: [PATCH 256/297] Improve README.md (#479) * Improve README.md * adjust font sizes * Update po-token guide URL * Change URL in help text to upstream repository --- .env.example | 2 +- README.md | 55 +++++++++++++++++++++++++++++++++++++----- src/config/constant.py | 2 +- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index a22dbd43..dbe01f22 100644 --- a/.env.example +++ b/.env.example @@ -57,7 +57,7 @@ TG_NORMAL_MAX_SIZE=2000 # Maximum URL length in captions CAPTION_URL_LENGTH_LIMIT=150 -# potoken https://github.com/yt-dlp/yt-dlp/wiki/Extractors#po-token-guide +# potoken 'https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide' POTOKEN=11 BROWSERS=firefox \ No newline at end of file diff --git a/README.md b/README.md index 74252925..e069fed6 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,54 @@ This bot can be deployed on any platform that supports Python. ## Run natively on your machine -* use pdm -* pdm install -* copy .env.example to .env -* python main.py +> Project use PDM to manage dependencies. + +1.

+ Install PDM + You can install using pip: `pip install --user pdm` + or for more ways: [Official Docs](https://pdm-project.org/en/latest/#installation) +
+ +2. Install modules using PDM: `pdm install`, or the old way use `requirements.txt` + +3.
+ Setting up config file + + ``` + cp .env.example .env + ``` + + Fill the fields in `.env`. + + **- Required Fields** + - `WORKERS`: Number of workers (default is 100) + - `APP_ID`: Telegram app ID + - `APP_HASH`: Telegram app hash + - `BOT_TOKEN`: Your telegram bot token + - `OWNER`: Owner ID (separate by `,`) + - `AUTHORIZED_USER`: List of authorized users (separate by `,`) + - `DB_DSN`: Your database URL (mysql+pymysql://user:pass@some_mariadb/dbname) + - `REDIS_HOST`: Redis host + + **- Optional Fields** + - `ENABLE_FFMPEG`: Enable FFMPEG for video processing (True/False) + - `AUDIO_FORMAT`: Desired audio format (e.g.:- mp3, wav) + - `ENABLE_ARIA2`: Enable Aria2 for downloads (True/False) + - `RCLONE_PATH`: Path to Rclone executable + - `ENABLE_VIP`: Enable VIP features (True/False) + - `PROVIDER_TOKEN`: Payment provider token + - `FREE_DOWNLOAD`: Free downloads allowed per user + - `RATE_LIMIT`: Rate limit for requests + - `TMPFILE_PATH`: Path for temporary/download files (ensure the directory exists and is writable) + - `TG_NORMAL_MAX_SIZE`: Maximum size for Telegram uploads in MB + - `CAPTION_URL_LENGTH_LIMIT`: Maximum URL length in captions + - `POTOKEN`: Your PO Token. [PO-Token-Guide](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide) + - `BROWSERS`: Browser to handle 'cookies from browser' +
+ +4. Activate virtual environment that created by PDM: `source .venv/bin/activate` + +5. Finally run the bot: `python src/main.py` ## Docker @@ -71,8 +115,6 @@ One line command to run the bot docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot ``` -# Complete deployment guide for docker-compose - # Command ``` @@ -85,6 +127,7 @@ aria2 - Download file using aria2 settings - Set your preference unsub - Unsubscribe from YouTube Channel ping - Ping the Bot +stats - Server and bot stats ``` # Test data diff --git a/src/config/constant.py b/src/config/constant.py index e6378d60..bfa27908 100644 --- a/src/config/constant.py +++ b/src/config/constant.py @@ -24,7 +24,7 @@ class BotText: help = """ 1. For YouTube and any websites supported by yt-dlp, just send the link and we will engine and send it to you. -2. For specific links use `/spdl {URL}`. More info at https://github.com/SanujaNS/ytdlbot-telegram#supported-websites +2. For specific links use `/spdl {URL}`. More info at https://github.com/tgbot-collection/ytdlbot#supported-websites 3. If the bot doesn't work, try again or join https://t.me/ytdlbot0 for updates. From 0ac49e12b430d976d8f15d6736f97290e3e403a7 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 26 Jan 2025 18:57:43 +0100 Subject: [PATCH 257/297] add comments --- .env.example | 14 +++++++------- README.md | 17 ++++++++--------- pre-push.py | 4 ++-- src/engine/generic.py | 12 +++++++++--- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index dbe01f22..4be0e685 100644 --- a/.env.example +++ b/.env.example @@ -12,22 +12,22 @@ APP_HASH= # Telegram bot token BOT_TOKEN= -# Owner ID, separate by , +# Owner ID, comma-separated OWNER= -# List of authorized users (comma-separated) +# List of authorized users, comma-separated AUTHORIZED_USER= -# database Data Source Name +# database Data Source Name, i.e. mysql+pymysql://user:pass@mysql/dbname SQLite sqlite:///db.sqlite DB_DSN= -# Redis host -REDIS_HOST= +# Redis host, leave it empty to use fakeredis +REDIS_HOST=redis # Enable FFMPEG for video processing (True/False) ENABLE_FFMPEG=False -# Desired audio format (e.g., mp3, wav) +# Desired audio format (e.g., mp3, wav), leave it empty to use m4a AUDIO_FORMAT= # Enable Aria2 for downloads (True/False) @@ -39,7 +39,7 @@ RCLONE_PATH= # Enable VIP features (True/False) ENABLE_VIP=False -# Payment provider token +# Payment provider token from Bot Father PROVIDER_TOKEN= # Free downloads allowed per user diff --git a/README.md b/README.md index e069fed6..62860090 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![docker image](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml/badge.svg)](https://github.com/tgbot-collection/ytdlbot/actions/workflows/builder.yaml) **YouTube Download Bot๐Ÿš€๐ŸŽฌโฌ‡๏ธ** -๐ŸงฐRefactor ongoing๐Ÿงฐ This Telegram bot allows you to download videos from YouTube and [other supported websites](#supported-websites). @@ -38,7 +37,7 @@ Just send a link directly to the bot. > ## Limitations > Due to limitations on servers and bandwidth, there are some restrictions on this free service. -> * Each user is limited to 3 free downloads every day. +> * Each user is limited to 1 free downloads every day. # Screenshots @@ -66,7 +65,7 @@ This bot can be deployed on any platform that supports Python. or for more ways: [Official Docs](https://pdm-project.org/en/latest/#installation) -2. Install modules using PDM: `pdm install`, or the old way use `requirements.txt` +2. Install modules using PDM: `pdm install`, or the old way use `pip install -r requirements.txt` 3.
Setting up config file @@ -75,7 +74,7 @@ This bot can be deployed on any platform that supports Python. cp .env.example .env ``` - Fill the fields in `.env`. + Fill the fields in `.env`. For more information, see the comments in the `.env.example` file. **- Required Fields** - `WORKERS`: Number of workers (default is 100) @@ -83,8 +82,8 @@ This bot can be deployed on any platform that supports Python. - `APP_HASH`: Telegram app hash - `BOT_TOKEN`: Your telegram bot token - `OWNER`: Owner ID (separate by `,`) - - `AUTHORIZED_USER`: List of authorized users (separate by `,`) - - `DB_DSN`: Your database URL (mysql+pymysql://user:pass@some_mariadb/dbname) + - `AUTHORIZED_USER`: List of authorized users ids, (separate by `,`) + - `DB_DSN`: Your database URL (mysql+pymysql://user:pass@mysql/dbname) or SQLite (sqlite:///db.sqlite) - `REDIS_HOST`: Redis host **- Optional Fields** @@ -93,14 +92,14 @@ This bot can be deployed on any platform that supports Python. - `ENABLE_ARIA2`: Enable Aria2 for downloads (True/False) - `RCLONE_PATH`: Path to Rclone executable - `ENABLE_VIP`: Enable VIP features (True/False) - - `PROVIDER_TOKEN`: Payment provider token + - `PROVIDER_TOKEN`: Payment provider token from Stripe - `FREE_DOWNLOAD`: Free downloads allowed per user - `RATE_LIMIT`: Rate limit for requests - `TMPFILE_PATH`: Path for temporary/download files (ensure the directory exists and is writable) - `TG_NORMAL_MAX_SIZE`: Maximum size for Telegram uploads in MB - `CAPTION_URL_LENGTH_LIMIT`: Maximum URL length in captions - `POTOKEN`: Your PO Token. [PO-Token-Guide](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide) - - `BROWSERS`: Browser to handle 'cookies from browser' + - `BROWSERS`: Browser to handle 'cookies from browser', i.e. firefox
4. Activate virtual environment that created by PDM: `source .venv/bin/activate` @@ -112,7 +111,7 @@ This bot can be deployed on any platform that supports Python. One line command to run the bot ```shell -docker run -e APP_ID=111 -e APP_HASH=111 -e TOKEN=370FXI bennythink/ytdlbot +docker run --env-file .env bennythink/ytdlbot ``` # Command diff --git a/pre-push.py b/pre-push.py index 809fd2b2..38d54884 100755 --- a/pre-push.py +++ b/pre-push.py @@ -18,5 +18,5 @@ file.write(f"{item}\n") # commit with amend -subprocess.run(["git", "add", "requirements.txt"]) -subprocess.run(["git", "commit", "-m", "pre-push"]) +# subprocess.run(["git", "add", "requirements.txt"]) +# subprocess.run(["git", "commit", "-m", "pre-push"]) diff --git a/src/engine/generic.py b/src/engine/generic.py index a12a90d6..2659130b 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -11,6 +11,7 @@ from database.model import get_format_settings, get_quality_settings from engine.base import BaseDownloader +from config import AUDIO_FORMAT def match_filter(info_dict): @@ -38,14 +39,15 @@ def _setup_formats(self) -> list | None: "bestvideo[vcodec^=avc]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", None, ] + audio = AUDIO_FORMAT or "m4a" maps = { - "high-audio": ["bestaudio[ext=m4a]"], + "high-audio": [f"bestaudio[ext=={audio}]"], "high-video": defaults, "high-document": defaults, - "medium-audio": ["bestaudio[ext=m4a]"], # no mediumaudio :-( + "medium-audio": [f"bestaudio[ext={audio}]"], # no mediumaudio :-( "medium-video": self.get_format(720), "medium-document": self.get_format(720), - "low-audio": ["bestaudio[ext=m4a]"], + "low-audio": [f"bestaudio[ext={audio}]"], "low-video": self.get_format(480), "low-document": self.get_format(480), "custom-audio": "", @@ -100,6 +102,10 @@ def _download(self, formats) -> list: # try add extract_args if present if potoken := os.getenv("POTOKEN"): ydl_opts["extractor_args"] = {"youtube": ["player-client=web,default", f"po_token=web+{potoken}"]} + # for new version? https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide + # ydl_opts["extractor_args"] = { + # "youtube": [f"po_token=web.player+{potoken}", f"po_token=web.gvs+{potoken}"] + # } if self._url.startswith("https://drive.google.com"): # Always use the `source` format for Google Drive URLs. From d064ce66ba40fe741e319a41f3259826a1b6c6b8 Mon Sep 17 00:00:00 2001 From: Vladislav Sobolev <36929696+sviperm@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:28:04 +0100 Subject: [PATCH 258/297] Update Docker compose (#480) * Update Docker compose * Changed .env file * Improve docker-compose.yml * little change in mysql environment: * Update .env.example --------- Co-authored-by: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> --- .env.example | 6 ++-- docker-compose.yml | 66 +++++++++++++++++++++++++++++++++++-------- src/database/model.py | 13 +++++++-- youtube-cookies.txt | 0 4 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 youtube-cookies.txt diff --git a/.env.example b/.env.example index 4be0e685..a75f13fe 100644 --- a/.env.example +++ b/.env.example @@ -18,8 +18,8 @@ OWNER= # List of authorized users, comma-separated AUTHORIZED_USER= -# database Data Source Name, i.e. mysql+pymysql://user:pass@mysql/dbname SQLite sqlite:///db.sqlite -DB_DSN= +# Database connection address, i.e. mysql+pymysql://user:pass@mysql/dbname +DB_DSN=mysql+pymysql://ytdlbot:your_password@mysql/ytdlbot # Redis host, leave it empty to use fakeredis REDIS_HOST=redis @@ -60,4 +60,4 @@ CAPTION_URL_LENGTH_LIMIT=150 # potoken 'https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide' POTOKEN=11 -BROWSERS=firefox \ No newline at end of file +BROWSERS=firefox diff --git a/docker-compose.yml b/docker-compose.yml index 47320511..a6b4b8b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,73 @@ -version: '3.1' - services: redis: image: redis:7-alpine - restart: always + container_name: redis + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - ./redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + command: redis-server --appendonly yes logging: - driver: none + options: + max-size: "10m" + max-file: "3" mysql: - image: ubuntu/mysql:8.0-22.04_beta - restart: always + image: mysql:8.0-debian + container_name: mysql + restart: unless-stopped + ports: + - "3306:3306" volumes: - ./db_data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD: 'root' - command: --default-authentication-plugin=mysql_native_password + MYSQL_RANDOM_ROOT_PASSWORD: "yes" + MYSQL_DATABASE: "ytdlbot" + MYSQL_USER: "ytdlbot" + MYSQL_PASSWORD: "your_password" + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 3 + command: + - --default-authentication-plugin=mysql_native_password + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --explicit_defaults_for_timestamp=1 + - --max_allowed_packet=64M logging: - driver: none + options: + max-size: "10m" + max-file: "3" ytdl: image: bennythink/ytdlbot + build: . + container_name: ytdl env_file: - .env - restart: always + restart: unless-stopped volumes: - ./youtube-cookies.txt:/app/youtube-cookies.txt depends_on: - - redis \ No newline at end of file + redis: + condition: service_healthy + mysql: + condition: service_healthy + +volumes: + redis_data: + name: ytdl_redis_data + mysql_data: + name: ytdl_mysql_data + +networks: + default: + name: ytdl_network diff --git a/src/database/model.py b/src/database/model.py index 17838a00..f6b57077 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -20,7 +20,7 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker -from config import FREE_DOWNLOAD +from config import FREE_DOWNLOAD, ENABLE_VIP class PaymentStatus: @@ -137,6 +137,9 @@ def set_user_settings(tgid: int, key: str, value: str): def get_free_quota(uid: int): + if not ENABLE_VIP: + return math.inf + with session_manager() as session: data = session.query(User).filter(User.user_id == uid).first() if data: @@ -145,7 +148,7 @@ def get_free_quota(uid: int): def get_paid_quota(uid: int): - if os.getenv("ENABLE_VIP"): + if ENABLE_VIP: with session_manager() as session: data = session.query(User).filter(User.user_id == uid).first() if data: @@ -171,6 +174,9 @@ def add_paid_quota(uid: int, amount: int): def check_quota(uid: int): + if not ENABLE_VIP: + return + with session_manager() as session: data = session.query(User).filter(User.user_id == uid).first() if data and (data.free + data.paid) <= 0: @@ -179,6 +185,9 @@ def check_quota(uid: int): def use_quota(uid: int): # use free first, then paid + if not ENABLE_VIP: + return + with session_manager() as session: user = session.query(User).filter(User.user_id == uid).first() if user: diff --git a/youtube-cookies.txt b/youtube-cookies.txt new file mode 100644 index 00000000..e69de29b From 9e96d74e1ddc2ccb48e58bd27b9e8919414a281e Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 28 Jan 2025 18:31:28 +0100 Subject: [PATCH 259/297] tidy compose file --- docker-compose.yml | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a6b4b8b7..c2af93d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,36 +1,24 @@ services: redis: image: redis:7-alpine - container_name: redis restart: unless-stopped - ports: - - "6379:6379" - volumes: - - ./redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 - command: redis-server --appendonly yes logging: options: max-size: "10m" max-file: "3" mysql: - image: mysql:8.0-debian - container_name: mysql - restart: unless-stopped - ports: - - "3306:3306" + image: ubuntu/mysql:8.0-22.04_beta + restart: always volumes: - ./db_data:/var/lib/mysql environment: - MYSQL_RANDOM_ROOT_PASSWORD: "yes" - MYSQL_DATABASE: "ytdlbot" - MYSQL_USER: "ytdlbot" - MYSQL_PASSWORD: "your_password" + MYSQL_RANDOM_ROOT_PASSWORD: "root" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s @@ -49,11 +37,9 @@ services: ytdl: image: bennythink/ytdlbot - build: . - container_name: ytdl env_file: - .env - restart: unless-stopped + restart: always volumes: - ./youtube-cookies.txt:/app/youtube-cookies.txt depends_on: @@ -61,13 +47,3 @@ services: condition: service_healthy mysql: condition: service_healthy - -volumes: - redis_data: - name: ytdl_redis_data - mysql_data: - name: ytdl_mysql_data - -networks: - default: - name: ytdl_network From cd56ca5de56543ddf10354e48a4c05847f41a8c2 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 28 Jan 2025 18:50:16 +0100 Subject: [PATCH 260/297] direct download fix #483 --- src/config/config.py | 9 +++++---- src/database/model.py | 2 +- src/engine/__init__.py | 6 ++++++ src/engine/direct.py | 17 +++++++++-------- src/engine/generic.py | 2 +- src/main.py | 16 +++++++++++++++- 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/config/config.py b/src/config/config.py index fb996a4e..54949891 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -19,13 +19,14 @@ AUTHORIZED_USER: str = os.getenv("AUTHORIZED_USER", "") DB_DSN = os.getenv("DB_DSN") REDIS_HOST = os.getenv("REDIS_HOST") -ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False) -AUDIO_FORMAT = os.getenv("AUDIO_FORMAT") -ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False) +ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG") == "True" +AUDIO_FORMAT = os.getenv("AUDIO_FORMAT", "m4a") +ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2") == "True" + RCLONE_PATH = os.getenv("RCLONE") # payment settings -ENABLE_VIP = os.getenv("ENABLE_VIP", False) +ENABLE_VIP = os.getenv("ENABLE_VIP") == "True" PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") FREE_DOWNLOAD = int(os.getenv("FREE_DOWNLOAD", 3)) TOKEN_PRICE = os.getenv("TOKEN_PRICE", 10) # 1 USD=10 downloads diff --git a/src/database/model.py b/src/database/model.py index f6b57077..bf878560 100644 --- a/src/database/model.py +++ b/src/database/model.py @@ -20,7 +20,7 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker -from config import FREE_DOWNLOAD, ENABLE_VIP +from config import ENABLE_VIP, FREE_DOWNLOAD class PaymentStatus: diff --git a/src/engine/__init__.py b/src/engine/__init__.py index bda81ef7..084dd286 100644 --- a/src/engine/__init__.py +++ b/src/engine/__init__.py @@ -5,6 +5,7 @@ from urllib.parse import urlparse +from engine.direct import DirectDownload from engine.generic import YoutubeDownload @@ -13,6 +14,11 @@ def youtube_entrance(client, bot_message, url): youtube.start() +def direct_entrance(client, bot_message, url): + dl = DirectDownload(client, bot_message, url) + dl.start() + + def special_download_entrance(url: str, tempdir: str, bm, **kwargs) -> list: """Specific link downloader""" domain = urlparse(url).hostname diff --git a/src/engine/direct.py b/src/engine/direct.py index 7e3664a6..42a2cbde 100644 --- a/src/engine/direct.py +++ b/src/engine/direct.py @@ -13,29 +13,30 @@ import filetype import requests -from base import BaseDownloader -from pyrogram import enums from config import ENABLE_ARIA2, TMPFILE_PATH +from engine.base import BaseDownloader -class DirectDownloader(BaseDownloader): +class DirectDownload(BaseDownloader): def _setup_formats(self) -> list | None: # direct download doesn't need to setup formats pass def _requests_download(self): + logging.info("Requests download with url %s", self._url) response = requests.get(self._url, stream=True) response.raise_for_status() - file = Path(self._tempdir).joinpath(uuid4().hex) - ext = filetype.guess_extension(file) - if ext is not None: - file = file.with_suffix(ext) - + file = Path(self._tempdir.name).joinpath(uuid4().hex) with open(file, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) + ext = filetype.guess_extension(file) + if ext is not None: + new_name = file.with_suffix(f".{ext}") + file.rename(new_name) + return [file.as_posix()] def _aria2_download(self): diff --git a/src/engine/generic.py b/src/engine/generic.py index 2659130b..89243ea1 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -9,9 +9,9 @@ import yt_dlp +from config import AUDIO_FORMAT from database.model import get_format_settings, get_quality_settings from engine.base import BaseDownloader -from config import AUDIO_FORMAT def match_filter(info_dict): diff --git a/src/main.py b/src/main.py index a95f39b2..2a5fe25d 100644 --- a/src/main.py +++ b/src/main.py @@ -45,7 +45,7 @@ reset_free, set_user_settings, ) -from engine import youtube_entrance +from engine import direct_entrance, youtube_entrance from utils import extract_url_and_name, sizeof_fmt, timeof_fmt logging.info("Authorized users are %s", AUTHORIZED_USER) @@ -278,6 +278,20 @@ def settings_handler(client: Client, message: types.Message): client.send_message(chat_id, BotText.settings.format(quality, send_type), reply_markup=markup) +@app.on_message(filters.command(["direct"])) +def direct_download(client: Client, message: types.Message): + chat_id = message.chat.id + init_user(chat_id) + client.send_chat_action(chat_id, enums.ChatAction.TYPING) + text = message.text.split() + + if len(text) == 1: + message.reply_text("Send me a direct link to download, e.g. /direct https://example.com/1.mp4", quote=True) + return + bot_msg = message.reply_text("Direct download request received.", quote=True) + direct_entrance(client, bot_msg, text[1]) + + @app.on_message(filters.command(["spdl"])) def spdl_handler(client: Client, message: types.Message): chat_id = message.from_user.id From a9d8e0ecdd3d3e8a1bb271eacfa1e18652940cfd Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 28 Jan 2025 19:01:08 +0100 Subject: [PATCH 261/297] env bool fix #478 --- src/config/config.py | 50 +++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/config/config.py b/src/config/config.py index 54949891..2c837da7 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -9,27 +9,43 @@ import os + +def get_env(name: str, default=None): + val = os.getenv(name, default) + if val is None: + return None + if val.lower() == "true": + return True + if val.lower() == "false": + return False + if val.isdigit(): + return int(val) + return val + + # general settings -WORKERS: int = int(os.getenv("WORKERS", 100)) -APP_ID: int = int(os.getenv("APP_ID")) -APP_HASH = os.getenv("APP_HASH") -BOT_TOKEN = os.getenv("BOT_TOKEN") -OWNER = [int(i) for i in os.getenv("OWNER").split(",")] +WORKERS: int = get_env("WORKERS", 100) +APP_ID: int = get_env("APP_ID") +APP_HASH = get_env("APP_HASH") +BOT_TOKEN = get_env("BOT_TOKEN") +OWNER = [int(i) for i in str(get_env("OWNER")).split(",")] # db settings -AUTHORIZED_USER: str = os.getenv("AUTHORIZED_USER", "") -DB_DSN = os.getenv("DB_DSN") -REDIS_HOST = os.getenv("REDIS_HOST") -ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG") == "True" -AUDIO_FORMAT = os.getenv("AUDIO_FORMAT", "m4a") -ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2") == "True" +AUTHORIZED_USER: str = get_env("AUTHORIZED_USER", "") +DB_DSN = get_env("DB_DSN") +REDIS_HOST = get_env("REDIS_HOST") + +ENABLE_FFMPEG = get_env("ENABLE_FFMPEG") + +AUDIO_FORMAT = get_env("AUDIO_FORMAT", "m4a") +ENABLE_ARIA2 = get_env("ENABLE_ARIA2") -RCLONE_PATH = os.getenv("RCLONE") +RCLONE_PATH = get_env("RCLONE") # payment settings -ENABLE_VIP = os.getenv("ENABLE_VIP") == "True" -PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") -FREE_DOWNLOAD = int(os.getenv("FREE_DOWNLOAD", 3)) -TOKEN_PRICE = os.getenv("TOKEN_PRICE", 10) # 1 USD=10 downloads +ENABLE_VIP = get_env("ENABLE_VIP") +PROVIDER_TOKEN = get_env("PROVIDER_TOKEN") +FREE_DOWNLOAD = get_env("FREE_DOWNLOAD", 3) +TOKEN_PRICE = get_env("TOKEN_PRICE", 10) # 1 USD=10 downloads # For advance users # Please do not change, if you don't know what these are. @@ -38,4 +54,4 @@ # This will set the value for the tmpfile path(engine path). If not, will return None and use systemโ€™s default path. # Please ensure that the directory exists and you have necessary permissions to write to it. -TMPFILE_PATH = os.getenv("TMPFILE_PATH") +TMPFILE_PATH = get_env("TMPFILE_PATH") From eb7ba7732ff0be8eebaab44e351c40fac7c79503 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:48:13 +0530 Subject: [PATCH 262/297] Fix typo (#484) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c2af93d3..b78b6207 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: volumes: - ./db_data:/var/lib/mysql environment: - MYSQL_RANDOM_ROOT_PASSWORD: "root" + MYSQL_ROOT_PASSWORD: "root" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s From 2386b5926a3a32bccbb0b46b0ae045a6d8041564 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:11:04 +0530 Subject: [PATCH 263/297] Fix #483 & `get_env` (#485) --- src/config/config.py | 13 +++++++------ src/engine/generic.py | 8 +++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/config/config.py b/src/config/config.py index 2c837da7..d936a18f 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -14,12 +14,13 @@ def get_env(name: str, default=None): val = os.getenv(name, default) if val is None: return None - if val.lower() == "true": - return True - if val.lower() == "false": - return False - if val.isdigit(): - return int(val) + if isinstance(val, str): + if val.lower() == "true": + return True + if val.lower() == "false": + return False + if val.isdigit() and name != "AUTHORIZED_USER": + return int(val) return val diff --git a/src/engine/generic.py b/src/engine/generic.py index 89243ea1..476be210 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -28,7 +28,13 @@ def get_format(m): f"bestvideo[vcodec^=avc][height={m}]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", ] + def is_youtube(self): + return self._url.startswith(("https://www.youtube.com/", "https://youtu.be/")) + def _setup_formats(self) -> list | None: + if not self.is_youtube(): + return [None] + quality, format_ = get_quality_settings(self._chat_id), get_format_settings(self._chat_id) # quality: high, medium, low, custom # format: audio, video, document @@ -93,7 +99,7 @@ def _download(self, formats) -> list: "match_filter": match_filter, } # setup cookies for youtube only - if self._url.startswith("https://www.youtube.com/") or self._url.startswith("https://youtu.be/"): + if self.is_youtube(): # use cookies from browser firstly if browsers := os.getenv("BROWSERS"): ydl_opts["cookiesfrombrowser"] = browsers.split(",") From cd7703398a0fa3196269bb19fcf5d87f41227d0e Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:06:41 +0530 Subject: [PATCH 264/297] Improve `/direct` (aria2) command (#486) --- README.md | 11 ++- src/engine/direct.py | 174 +++++++++++++++++++++++++++++++++---------- src/main.py | 29 ++------ 3 files changed, 148 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 62860090..56e5e3e4 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Just send a link directly to the bot. # Supported websites -* YouTube ๐Ÿ˜… +* YouTube * Any websites [supported by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) ### Specific link downloader (Use /spdl for these links) @@ -61,9 +61,11 @@ This bot can be deployed on any platform that supports Python. 1.
Install PDM + You can install using pip: `pip install --user pdm` - or for more ways: [Official Docs](https://pdm-project.org/en/latest/#installation) -
+ or for detailed instructions: [Official Docs](https://pdm-project.org/en/latest/#installation) + + 2. Install modules using PDM: `pdm install`, or the old way use `pip install -r requirements.txt` @@ -121,12 +123,13 @@ start - Let's start about - What's this bot? help - Help spdl - Use to download specific link downloader links +direct - Download using aria2/requests engines ytdl - Download video in group -aria2 - Download file using aria2 settings - Set your preference unsub - Unsubscribe from YouTube Channel ping - Ping the Bot stats - Server and bot stats +buy - Buy quota. ``` # Test data diff --git a/src/engine/direct.py b/src/engine/direct.py index 42a2cbde..175ea363 100644 --- a/src/engine/direct.py +++ b/src/engine/direct.py @@ -5,6 +5,7 @@ import logging import os +import re import pathlib import subprocess import tempfile @@ -24,6 +25,19 @@ def _setup_formats(self) -> list | None: # direct download doesn't need to setup formats pass + def _get_aria2_name(self): + try: + cmd = f"aria2c --truncate-console-readout=true -x10 --dry-run --file-allocation=none {self._url}" + result = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) + stdout_str = result.stdout.decode("utf-8") + name = os.path.basename(stdout_str).split("\n")[0] + if len(name) == 0: + name = os.path.basename(self._url) + return name + except Exception: + name = os.path.basename(self._url) + return name + def _requests_download(self): logging.info("Requests download with url %s", self._url) response = requests.get(self._url, stream=True) @@ -40,51 +54,133 @@ def _requests_download(self): return [file.as_posix()] def _aria2_download(self): - ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" - response = None - video_paths = None - # Download process using aria2c + ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.4472.124 Safari/537.36" + filename = self._get_aria2_name() + self._process = None try: - self._bot_msg.edit_text(f"Aria2 download starting...") - # Command to engine the link using aria2c + self._bot_msg.edit_text("Aria2 download starting...") + temp_dir = self._tempdir.name command = [ "aria2c", - "-U", - ua, - "--max-tries=5", - "--console-log-level=warn", - "-d", - self._tempdir, + "--max-tries=3", + "--max-concurrent-downloads=8", + "--max-connection-per-server=16", + "--split=16", + "--summary-interval=1", + "--console-log-level=notice", + "--show-console-readout=true", + "--quiet=false", + "--human-readable=true", + f"--user-agent={ua}", + "-d", temp_dir, + "-o", filename, self._url, ] - # Run the command using subprocess.Popen - process = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - max_iterations = 100 # Set a reasonable maximum number of iterations - iteration = 0 - while process.poll() is None and iteration < max_iterations: - line: str = process.stdout.readline().decode("utf-8") - if line.startswith("[#"): - line = line.strip() - self.edit_text(f"Aria2 downloading... \n\n`{line}`", disable_web_page_preview=True) - break - iteration += 1 - - if iteration >= max_iterations: - self.edit_text("Download exceed max iteration. Please try again later.", disable_web_page_preview=True) + + self._process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + bufsize=1 + ) + + while True: + line = self._process.stdout.readline() + if not line: + if self._process.poll() is not None: + break + continue + + progress = self.__parse_progress(line) + if progress: + self.download_hook(progress) + elif "Download complete:" in line: + self.download_hook({"status": "complete"}) + + self._process.wait(timeout=300) + success = self._process.wait() == 0 + if not success: + raise subprocess.CalledProcessError( + self._process.returncode, + command, + self._process.stderr.read() + ) + if self._process.returncode != 0: + raise subprocess.CalledProcessError( + self._process.returncode, + command, + stderr + ) + + files = [f for f in Path(temp_dir).glob("*") if f.is_file()] + if not files: + raise FileNotFoundError(f"No files found in {temp_dir}") + + file = files[0] + # Handle file extension + if not file.suffix: + ext = filetype.guess_extension(file) + if ext: + new_file = file.with_suffix(f".{ext}") + file.rename(new_file) + file = new_file + + logging.info("Successfully downloaded file: %s", file) + + return [file.as_posix()] + + except subprocess.TimeoutExpired: + error_msg = "Download timed out after 5 minutes." + logging.error(error_msg) + self._bot_msg.edit_text(f"Download failed!โŒ\n\n{error_msg}") + return [] except Exception as e: - self.edit_text(f"Download failed!โŒ\n\n`{e}`", disable_web_page_preview=True) - return - # Get filename and extension correctly after engine - file: Path = next(Path(self._tempdir).glob("*")) - filename = file.name - logging.info("Downloaded file %s", filename) - self.edit_text(f"Download Complete", disable_web_page_preview=True) - ext = filetype.guess_extension(file) - # Rename file if it doesn't have extension - if ext is not None and not filename.endswith(ext): - file.rename(f"{self._tempdir}/{filename}.{ext}") - # Get file path of the downloaded file to upload - return [file.as_posix()] + self._bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`") + return [] + finally: + if self._process: + self._process.terminate() + self._process = None + + def __parse_progress(self, line: str) -> dict | None: + if "Download complete:" in line or "(OK):download completed" in line: + return {"status": "complete"} + + progress_match = re.search( + r'\[#\w+\s+(?P[\d.]+[KMGTP]?iB)/(?P[\d.]+[KMGTP]?iB)\(.*?\)\s+CN:\d+\s+DL:(?P[\d.]+[KMGTP]?iB)\s+ETA:(?P[\dhms]+)', + line + ) + + if progress_match: + return { + "status": "downloading", + "downloaded_bytes": self.__parse_size(progress_match.group("progress")), + "total_bytes": self.__parse_size(progress_match.group("total")), + "_speed_str": f"{progress_match.group('speed')}/s", + "_eta_str": progress_match.group("eta") + } + + # Fallback check for summary lines + if "Download Progress Summary" in line and "MiB" in line: + return {"status": "progress", "details": line} + + return None + + def __parse_size(self, size_str: str) -> int: + units = { + "B": 1, + "K": 1024, "KB": 1024, "KIB": 1024, + "M": 1024**2, "MB": 1024**2, "MIB": 1024**2, + "G": 1024**3, "GB": 1024**3, "GIB": 1024**3, + "T": 1024**4, "TB": 1024**4, "TIB": 1024**4 + } + match = re.match(r"([\d.]+)([A-Za-z]*)", size_str.replace("i", "").upper()) + if match: + number, unit = match.groups() + unit = unit or "B" + return int(float(number) * units.get(unit, 1)) + return 0 def _download(self, formats=None) -> list: if ENABLE_ARIA2: diff --git a/src/main.py b/src/main.py index 2a5fe25d..d0d31ddb 100644 --- a/src/main.py +++ b/src/main.py @@ -283,13 +283,15 @@ def direct_download(client: Client, message: types.Message): chat_id = message.chat.id init_user(chat_id) client.send_chat_action(chat_id, enums.ChatAction.TYPING) - text = message.text.split() + message_text = message.text + url, new_name = extract_url_and_name(message_text) - if len(text) == 1: - message.reply_text("Send me a direct link to download, e.g. /direct https://example.com/1.mp4", quote=True) + logging.info("Direct download using aria2/requests start %s", url) + if url is None or not re.findall(r"^https?://", url.lower()): + message.reply_text("Send me a correct LINK.", quote=True) return bot_msg = message.reply_text("Direct download request received.", quote=True) - direct_entrance(client, bot_msg, text[1]) + direct_entrance(client, bot_msg, url) @app.on_message(filters.command(["spdl"])) @@ -308,25 +310,6 @@ def spdl_handler(client: Client, message: types.Message): spdl_download_entrance(client, bot_msg, url) -@app.on_message(filters.command(["leech"])) -def leech_handler(client: Client, message: types.Message): - if not ENABLE_ARIA2: - message.reply_text("Aria2 Not Enabled.", quote=True) - return - - chat_id = message.from_user.id - client.send_chat_action(chat_id, enums.ChatAction.TYPING) - message_text = message.text - url, new_name = extract_url_and_name(message_text) - logging.info("leech using aria2 start %s", url) - if url is None or not re.findall(r"^https?://", url.lower()): - message.reply_text("Send me a correct LINK.", quote=True) - return - - bot_msg = message.reply_text("Request received.", quote=True) - leech_download_entrance(client, bot_msg, url) - - @app.on_message(filters.command(["ytdl"]) & filters.group) def ytdl_handler(client: Client, message: types.Message): # for group only From 876f8ace75f9065face911dad1cfc035377f274d Mon Sep 17 00:00:00 2001 From: Benny Date: Sat, 1 Feb 2025 12:01:00 +0100 Subject: [PATCH 265/297] fix #487 typo --- src/engine/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/generic.py b/src/engine/generic.py index 476be210..80651161 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -47,7 +47,7 @@ def _setup_formats(self) -> list | None: ] audio = AUDIO_FORMAT or "m4a" maps = { - "high-audio": [f"bestaudio[ext=={audio}]"], + "high-audio": [f"bestaudio[ext={audio}]"], "high-video": defaults, "high-document": defaults, "medium-audio": [f"bestaudio[ext={audio}]"], # no mediumaudio :-( From 8bd1607545f09a6d346d1ffd10fdaf5127abc8f8 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 18 Feb 2025 00:38:35 +0530 Subject: [PATCH 266/297] Fix `/spdl` (refactor #460) (#488) --- src/engine/__init__.py | 16 ++++--- src/engine/direct.py | 5 +- src/engine/instagram.py | 97 +++++++++++++++++++++++++++++---------- src/engine/krakenfiles.py | 83 ++++++++++++++++++++++++--------- src/engine/pixeldrain.py | 46 +++++++++++++++---- src/main.py | 10 ++-- 6 files changed, 190 insertions(+), 67 deletions(-) diff --git a/src/engine/__init__.py b/src/engine/__init__.py index 084dd286..1d6b97a7 100644 --- a/src/engine/__init__.py +++ b/src/engine/__init__.py @@ -5,8 +5,11 @@ from urllib.parse import urlparse -from engine.direct import DirectDownload from engine.generic import YoutubeDownload +from engine.direct import DirectDownload +from engine.pixeldrain import pixeldrain_download +from engine.instagram import InstagramDownload +from engine.krakenfiles import krakenfiles_download def youtube_entrance(client, bot_message, url): @@ -19,16 +22,17 @@ def direct_entrance(client, bot_message, url): dl.start() -def special_download_entrance(url: str, tempdir: str, bm, **kwargs) -> list: +def special_download_entrance(client, bot_message, url): """Specific link downloader""" domain = urlparse(url).hostname if "youtube.com" in domain or "youtu.be" in domain: - raise ValueError("ERROR: This is ytdl bot for Youtube links just send the link.") + raise ValueError("ERROR: For Youtube links, just send the link.") elif "www.instagram.com" in domain: - return instagram(url, tempdir, bm, **kwargs) + dl = InstagramDownload(client, bot_message, url) + dl.start() elif "pixeldrain.com" in domain: - return pixeldrain(url, tempdir, bm, **kwargs) + return pixeldrain_download(client, bot_message, url) elif "krakenfiles.com" in domain: - return krakenfiles(url, tempdir, bm, **kwargs) + return krakenfiles_download(client, bot_message, url) else: raise ValueError(f"Invalid URL: No specific link function found for {url}") diff --git a/src/engine/direct.py b/src/engine/direct.py index 175ea363..06db3d38 100644 --- a/src/engine/direct.py +++ b/src/engine/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # coding: utf-8 -# ytdlbot - leech.py +# ytdlbot - direct.py import logging import os @@ -120,8 +120,7 @@ def _aria2_download(self): file = files[0] # Handle file extension if not file.suffix: - ext = filetype.guess_extension(file) - if ext: + if ext := filetype.guess_extension(file): new_file = file.with_suffix(f".{ext}") file.rename(new_file) file = new_file diff --git a/src/engine/instagram.py b/src/engine/instagram.py index 7aa479d7..a039d4be 100644 --- a/src/engine/instagram.py +++ b/src/engine/instagram.py @@ -3,18 +3,17 @@ # ytdlbot - instagram.py - +import time import pathlib import re import filetype import requests -from base import BaseDownloader +from engine.base import BaseDownloader class InstagramDownload(BaseDownloader): def extract_code(self): - # Regular expression patterns patterns = [ # Instagram stories highlights r"/stories/highlights/([a-zA-Z0-9_-]+)/", @@ -43,32 +42,84 @@ def extract_code(self): def _setup_formats(self) -> list | None: pass - def _download(self, formats): - # TODO: Implement download method - resp = requests.get(f"http://instagram:15000/?url={self._url}").json() + def _download(self, formats=None): + try: + resp = requests.get(f"http://instagram:15000/?url={self._url}").json() + except Exception as e: + self._bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`") + pass + code = self.extract_code() counter = 1 video_paths = [] + if url_results := resp.get("data"): for link in url_results: - req = requests.get(link, stream=True) - length = int(req.headers.get("content-length")) - content = req.content - ext = filetype.guess_extension(content) - filename = f"{code}_{counter}.{ext}" - save_path = pathlib.Path(self._tempdir, filename) - chunk_size = 4096 - downloaded = 0 - for chunk in req.iter_content(chunk_size): - text = tqdm_progress(f"Downloading: {filename}", length, downloaded) - edit_text(bm, text) - with open(save_path, "ab") as fp: - fp.write(chunk) - downloaded += len(chunk) - video_paths.append(save_path) - counter += 1 + try: + req = requests.get(link, stream=True) + length = int(req.headers.get("content-length", 0) or req.headers.get("x-full-image-content-length", 0)) + filename = f"Instagram_{code}-{counter}" + save_path = pathlib.Path(self._tempdir.name, filename) + chunk_size = 8192 + downloaded = 0 + start_time = time.time() + + with open(save_path, "wb") as fp: + for chunk in req.iter_content(chunk_size): + if chunk: + downloaded += len(chunk) + fp.write(chunk) + + elapsed_time = time.time() - start_time + if elapsed_time > 0: + speed = downloaded / elapsed_time # bytes per second + + if speed >= 1024 * 1024: # MB/s + speed_str = f"{speed / (1024 * 1024):.2f}MB/s" + elif speed >= 1024: # KB/s + speed_str = f"{speed / 1024:.2f}KB/s" + else: # B/s + speed_str = f"{speed:.2f}B/s" + + if length > 0: + eta_seconds = (length - downloaded) / speed + if eta_seconds >= 3600: + eta_str = f"{eta_seconds / 3600:.1f}h" + elif eta_seconds >= 60: + eta_str = f"{eta_seconds / 60:.1f}m" + else: + eta_str = f"{eta_seconds:.0f}s" + else: + eta_str = "N/A" + else: + speed_str = "N/A" + eta_str = "N/A" + + # dictionary for calling the download_hook + d = { + "status": "downloading", + "downloaded_bytes": downloaded, + "total_bytes": length, + "_speed_str": speed_str, + "_eta_str": eta_str + } + + self.download_hook(d) + + if ext := filetype.guess_extension(save_path): + new_path = save_path.with_suffix(f".{ext}") + save_path.rename(new_path) + save_path = new_path + + video_paths.append(save_path) + counter += 1 + + except Exception as e: + self._bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`") + return [] return video_paths def _start(self): - pass + self._download() + self._upload() diff --git a/src/engine/krakenfiles.py b/src/engine/krakenfiles.py index 1ac4dfcb..795422a4 100644 --- a/src/engine/krakenfiles.py +++ b/src/engine/krakenfiles.py @@ -7,26 +7,67 @@ import requests from bs4 import BeautifulSoup +from engine.direct import DirectDownload -def krakenfiles(url: str, tempdir: str, bm, **kwargs): - resp = requests.get(url) - html = resp.content - soup = BeautifulSoup(html, "html.parser") - link_parts = [] - token_parts = [] - for form_tag in soup.find_all("form"): - action = form_tag.get("action") - if action and "krakenfiles.com" in action: - link_parts.append(action) - input_tag = form_tag.find("input", {"name": "token"}) - if input_tag: - value = input_tag.get("value") - token_parts.append(value) - for link_part, token_part in zip(link_parts, token_parts): - link = f"https:{link_part}" - data = {"token": token_part} - response = requests.post(link, data=data) - json_data = response.json() - url = json_data["url"] - return sp_ytdl_download(url, tempdir, bm, **kwargs) +def krakenfiles_download(client, bot_message, url: str): + session = requests.Session() + + def _extract_form_data(url: str) -> list[tuple[str, str]]: + try: + resp = session.get(url) + resp.raise_for_status() + soup = BeautifulSoup(resp.content, "html.parser") + + if post_url := soup.xpath('//form[@id="dl-form"]/@action'): + post_url = f"https://krakenfiles.com{post_url[0]}" + else: + raise ValueError("ERROR: Unable to find post link.") + if token := soup.xpath('//input[@id="dl-token"]/@value'): + data = {"token": token[0]} + else: + raise ValueError("ERROR: Unable to find token for post.") + + return list(zip(post_url, data)) + + except requests.RequestException as e: + raise ValueError(f"Failed to fetch page: {str(e)}") + except Exception as e: + raise ValueError(f"Failed to parse page: {str(e)}") + + def _get_download_url(form_data: list[tuple[str, str]]) -> str: + for post_url, data in form_data: + try: + response = session.post(post_url, data=data) + response.raise_for_status() + + json_data = response.json() + if "url" in json_data: + return json_data["url"] + + except requests.RequestException as e: + bot_message.edit_text(f"Error during form submission: {str(e)}") + except ValueError as e: + bot_message.edit_text(f"Error parsing response: {str(e)}") + + raise ValueError("Could not obtain download URL") + + def _download(url: str): + try: + bot_message.edit_text("Processing krakenfiles download link...") + form_data = _extract_form_data(url) + download_url = _get_download_url(form_data) + + bot_message.edit_text("Starting download...") + downloader = DirectDownload(client, bot_message, download_url) + downloader.start() + + except ValueError as e: + bot_message.edit_text(f"Download failed!โŒ\n{str(e)}") + except Exception as e: + bot_message.edit_text( + f"Download failed!โŒ\nAn error occurred: {str(e)}\n" + "Please check your URL and try again." + ) + + _download(url) diff --git a/src/engine/pixeldrain.py b/src/engine/pixeldrain.py index 14210341..a248a575 100644 --- a/src/engine/pixeldrain.py +++ b/src/engine/pixeldrain.py @@ -5,14 +5,44 @@ __author__ = "SanujaNS " +import tempfile +import pathlib import re +from urllib.parse import urlparse +from engine.direct import DirectDownload -def pixeldrain(url: str, tempdir: str, bm, **kwargs): - user_page_url_regex = r"https://pixeldrain.com/u/(\w+)" - match = re.match(user_page_url_regex, url) - if match: - url = "https://pixeldrain.com/api/file/{}?engine".format(match.group(1)) - return sp_ytdl_download(url, tempdir, bm, **kwargs) - else: - return url +def pixeldrain_download(client, bot_message, url): + FILE_URL_FORMAT = "https://pixeldrain.com/api/file/{}?download" + USER_PAGE_PATTERN = re.compile(r"https://pixeldrain.com/u/(\w+)") + + def _extract_file_id(url): + if match := USER_PAGE_PATTERN.match(url): + return match.group(1) + + parsed = urlparse(url) + if parsed.path.startswith('/file/'): + return parsed.path.split('/')[-1] + + raise ValueError("Invalid Pixeldrain URL format") + + def _get_download_url(file_id): + return FILE_URL_FORMAT.format(file_id) + + def _download(url): + try: + file_id = _extract_file_id(url) + download_url = _get_download_url(file_id) + + ddl = DirectDownload(client, bot_message, download_url) + ddl.start() + + except ValueError as e: + bot_message.edit_text(f"Download failed!โŒ\n\n`{e}`") + except Exception as e: + bot_message.edit_text( + f"Download failed!โŒ\nAn error occurred: {str(e)}\n" + "Please check your URL and try again." + ) + + _download(url) \ No newline at end of file diff --git a/src/main.py b/src/main.py index d0d31ddb..9903910a 100644 --- a/src/main.py +++ b/src/main.py @@ -45,7 +45,7 @@ reset_free, set_user_settings, ) -from engine import direct_entrance, youtube_entrance +from engine import direct_entrance, youtube_entrance, special_download_entrance from utils import extract_url_and_name, sizeof_fmt, timeof_fmt logging.info("Authorized users are %s", AUTHORIZED_USER) @@ -285,7 +285,6 @@ def direct_download(client: Client, message: types.Message): client.send_chat_action(chat_id, enums.ChatAction.TYPING) message_text = message.text url, new_name = extract_url_and_name(message_text) - logging.info("Direct download using aria2/requests start %s", url) if url is None or not re.findall(r"^https?://", url.lower()): message.reply_text("Send me a correct LINK.", quote=True) @@ -296,7 +295,7 @@ def direct_download(client: Client, message: types.Message): @app.on_message(filters.command(["spdl"])) def spdl_handler(client: Client, message: types.Message): - chat_id = message.from_user.id + chat_id = message.chat.id init_user(chat_id) client.send_chat_action(chat_id, enums.ChatAction.TYPING) message_text = message.text @@ -305,9 +304,8 @@ def spdl_handler(client: Client, message: types.Message): if url is None or not re.findall(r"^https?://", url.lower()): message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) return - - bot_msg = message.reply_text("Request received.", quote=True) - spdl_download_entrance(client, bot_msg, url) + bot_msg = message.reply_text("SPDL request received.", quote=True) + special_download_entrance(client, bot_msg, url) @app.on_message(filters.command(["ytdl"]) & filters.group) From e39d9e93adc834db3b8f6d361e3d975ee944011f Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 18 Feb 2025 01:25:46 +0530 Subject: [PATCH 267/297] Fix #489 (#490) * Fix #489 * lock working kurigram version --- pdm.lock | 8 ++++---- pyproject.toml | 2 +- requirements.txt | 2 +- src/engine/generic.py | 8 +++----- src/utils/__init__.py | 15 +++++++++++---- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pdm.lock b/pdm.lock index 08792779..e69f143c 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:c6252b55edc18b06e437fbcbcf1779d6ad0258fc74837a573cff764bbe31b0c8" +content_hash = "sha256:800671e4b05ee35a97cafaa11414ecbae466a83098c1c42de2b1e8c769f34a35" [[metadata.targets]] requires_python = ">3.9" @@ -468,7 +468,7 @@ files = [ [[package]] name = "kurigram" -version = "2.1.36" +version = "2.1.38" requires_python = "~=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -477,8 +477,8 @@ dependencies = [ "pysocks==1.7.1", ] files = [ - {file = "Kurigram-2.1.36-py3-none-any.whl", hash = "sha256:04bacdecfdf9cb91c3698f66b2ab9c5cfcf6b7b7cf30c5b84066b54612e40a75"}, - {file = "kurigram-2.1.36.tar.gz", hash = "sha256:341815a38b71c75c8fe76e3d55b079fea6477548aa58459dd4d984512f467ba2"}, + {file = "Kurigram-2.1.38-py3-none-any.whl", hash = "sha256:6489803d829e90ee10a9e09d163fa95278e14ed5a29761818487c43ec8961ecc"}, + {file = "kurigram-2.1.38.tar.gz", hash = "sha256:d26fe47eaa8b29e494cceda6742045b96ff2d3f429fd28c2bfb25b3216eb8873"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 182c8ff3..050100b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.15", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram>=2.1.36", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.15", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.1.38", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 48a323c2..e2930fa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram>=2.1.36 +kurigram==2.1.38 cryptography>=43.0.3 diff --git a/src/engine/generic.py b/src/engine/generic.py index 80651161..3516e554 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -10,6 +10,7 @@ import yt_dlp from config import AUDIO_FORMAT +from utils import is_youtube from database.model import get_format_settings, get_quality_settings from engine.base import BaseDownloader @@ -28,11 +29,8 @@ def get_format(m): f"bestvideo[vcodec^=avc][height={m}]+bestaudio[acodec^=mp4a]/best[vcodec^=avc]/best", ] - def is_youtube(self): - return self._url.startswith(("https://www.youtube.com/", "https://youtu.be/")) - def _setup_formats(self) -> list | None: - if not self.is_youtube(): + if not is_youtube(self._url): return [None] quality, format_ = get_quality_settings(self._chat_id), get_format_settings(self._chat_id) @@ -99,7 +97,7 @@ def _download(self, formats) -> list: "match_filter": match_filter, } # setup cookies for youtube only - if self.is_youtube(): + if is_youtube(self._url): # use cookies from browser firstly if browsers := os.getenv("BROWSERS"): ydl_opts["cookiesfrombrowser"] = browsers.split(",") diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 105d5d2e..5dfdf518 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -12,7 +12,7 @@ import time import uuid from http.cookiejar import MozillaCookieJar -from urllib.parse import quote_plus +from urllib.parse import quote_plus, urlparse import ffmpeg @@ -35,9 +35,16 @@ def timeof_fmt(seconds: int | float): return result -def is_youtube(url: str): - if url.startswith("https://www.youtube.com/") or url.startswith("https://youtu.be/"): - return True +def is_youtube(url: str) -> bool: + try: + if not url or not isinstance(url, str): + return False + + parsed = urlparse(url) + return parsed.netloc.lower() in {'youtube.com', 'www.youtube.com', 'youtu.be'} + + except Exception: + return False def adjust_formats(formats): From ab0882e39acca05c2486ed17cf9e5705b67b7869 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Mon, 24 Feb 2025 22:57:37 +0530 Subject: [PATCH 268/297] m3u8 support and toggle (#493) --- .env.example | 3 +++ src/config/config.py | 2 +- src/main.py | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index a75f13fe..c46b9245 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,9 @@ ENABLE_FFMPEG=False # Desired audio format (e.g., mp3, wav), leave it empty to use m4a AUDIO_FORMAT= +# Enable m3u8 link support (True/False) +M3U8_SUPPORT=False + # Enable Aria2 for downloads (True/False) ENABLE_ARIA2=False diff --git a/src/config/config.py b/src/config/config.py index d936a18f..ccf130f9 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -36,8 +36,8 @@ def get_env(name: str, default=None): REDIS_HOST = get_env("REDIS_HOST") ENABLE_FFMPEG = get_env("ENABLE_FFMPEG") - AUDIO_FORMAT = get_env("AUDIO_FORMAT", "m4a") +M3U8_SUPPORT = get_env("M3U8_SUPPORT") ENABLE_ARIA2 = get_env("ENABLE_ARIA2") RCLONE_PATH = get_env("RCLONE") diff --git a/src/main.py b/src/main.py index 9903910a..44c5fd6e 100644 --- a/src/main.py +++ b/src/main.py @@ -29,6 +29,7 @@ BOT_TOKEN, ENABLE_ARIA2, ENABLE_FFMPEG, + M3U8_SUPPORT, ENABLE_VIP, OWNER, PROVIDER_TOKEN, @@ -331,8 +332,8 @@ def check_link(url: str): # TODO maybe using ytdl.extract_info raise ValueError("Playlist or channel download are not supported at this moment.") - if re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower()): - raise ValueError("m3u8 links are not supported.") + if not M3U8_SUPPORT and (re.findall(r"m3u8|\.m3u8|\.m3u$", url.lower())): + return "m3u8 links are disabled." @app.on_message(filters.incoming & filters.text) From 63a1fcdfa9d9e6272ab9f4aea0ad0f4a56a02c84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Mar 2025 12:15:01 +0100 Subject: [PATCH 269/297] Bump yt-dlp from 2025.1.15 to 2025.2.19 (#492) Bumps [yt-dlp](https://github.com/yt-dlp/yt-dlp) from 2025.1.15 to 2025.2.19. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.01.15...2025.02.19) --- updated-dependencies: - dependency-name: yt-dlp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e2930fa2..d03ced7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp==2025.1.15 +yt-dlp==2025.2.19 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 5a0decbc500923c8edb59d503cc8094f0d70d48a Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 22 Mar 2025 21:46:05 +0530 Subject: [PATCH 270/297] Bump yt-dlp from 2025.2.19 to 2025.3.21 with standard tag `[default]` (#495) --- pdm.lock | 272 +++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 268 insertions(+), 8 deletions(-) diff --git a/pdm.lock b/pdm.lock index e69f143c..94630c6f 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:800671e4b05ee35a97cafaa11414ecbae466a83098c1c42de2b1e8c769f34a35" +content_hash = "sha256:88a10a1ea7ef558cb7e9ffbb1cf1743c89e9e997c3d99a4495e7aea3b927689b" [[metadata.targets]] requires_python = ">3.9" @@ -91,6 +91,124 @@ files = [ {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] +[[package]] +name = "brotli" +version = "1.1.0" +summary = "Python bindings for the Brotli compression library" +groups = ["default"] +marker = "implementation_name == \"cpython\"" +files = [ + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, + {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, + {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, + {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, + {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, + {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, + {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, + {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, + {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, + {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, +] + +[[package]] +name = "brotlicffi" +version = "1.1.0.0" +requires_python = ">=3.7" +summary = "Python CFFI bindings to the Brotli library" +groups = ["default"] +marker = "implementation_name != \"cpython\"" +dependencies = [ + "cffi>=1.0.0", +] +files = [ + {file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"}, + {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"}, + {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814"}, + {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820"}, + {file = "brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb"}, + {file = "brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613"}, + {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e4aeb0bd2540cb91b069dbdd54d458da8c4334ceaf2d25df2f4af576d6766ca"}, + {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b7b0033b0d37bb33009fb2fef73310e432e76f688af76c156b3594389d81391"}, + {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54a07bb2374a1eba8ebb52b6fafffa2afd3c4df85ddd38fcc0511f2bb387c2a8"}, + {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7901a7dc4b88f1c1475de59ae9be59799db1007b7d059817948d8e4f12e24e35"}, + {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce01c7316aebc7fce59da734286148b1d1b9455f89cf2c8a4dfce7d41db55c2d"}, + {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a807d760763e398bbf2c6394ae9da5815901aa93ee0a37bca5efe78d4ee3171"}, + {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa8ca0623b26c94fccc3a1fdd895be1743b838f3917300506d04aa3346fd2a14"}, + {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3de0cf28a53a3238b252aca9fed1593e9d36c1d116748013339f0949bfc84112"}, + {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6be5ec0e88a4925c91f3dea2bb0013b3a2accda6f77238f76a34a1ea532a1cb0"}, + {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d9eb71bb1085d996244439154387266fd23d6ad37161f6f52f1cd41dd95a3808"}, + {file = "brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13"}, +] + [[package]] name = "certifi" version = "2024.8.30" @@ -108,7 +226,7 @@ version = "1.17.1" requires_python = ">=3.8" summary = "Foreign Function Interface for Python calling C code." groups = ["default"] -marker = "platform_python_implementation != \"PyPy\"" +marker = "platform_python_implementation != \"PyPy\" or implementation_name != \"cpython\"" dependencies = [ "pycparser", ] @@ -481,6 +599,17 @@ files = [ {file = "kurigram-2.1.38.tar.gz", hash = "sha256:d26fe47eaa8b29e494cceda6742045b96ff2d3f429fd28c2bfb25b3216eb8873"}, ] +[[package]] +name = "mutagen" +version = "1.47.0" +requires_python = ">=3.7" +summary = "read and write audio tags for many formats" +groups = ["default"] +files = [ + {file = "mutagen-1.47.0-py3-none-any.whl", hash = "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719"}, + {file = "mutagen-1.47.0.tar.gz", hash = "sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -557,12 +686,42 @@ version = "2.22" requires_python = ">=3.8" summary = "C parser in Python" groups = ["default"] -marker = "platform_python_implementation != \"PyPy\"" +marker = "platform_python_implementation != \"PyPy\" or implementation_name != \"cpython\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +[[package]] +name = "pycryptodomex" +version = "3.22.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cryptographic library for Python" +groups = ["default"] +files = [ + {file = "pycryptodomex-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:aef4590263b9f2f6283469e998574d0bd45c14fb262241c27055b82727426157"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5ac608a6dce9418d4f300fab7ba2f7d499a96b462f2b9b5c90d8d994cd36dcad"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a24f681365ec9757ccd69b85868bbd7216ba451d0f86f6ea0eed75eeb6975db"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:259664c4803a1fa260d5afb322972813c5fe30ea8b43e54b03b7e3a27b30856b"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7127d9de3c7ce20339e06bcd4f16f1a1a77f1471bcf04e3b704306dde101b719"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ee75067b35c93cc18b38af47b7c0664998d8815174cfc66dd00ea1e244eb27e6"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a8b0c5ba061ace4bcd03496d42702c3927003db805b8ec619ea6506080b381d"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:bfe4fe3233ef3e58028a3ad8f28473653b78c6d56e088ea04fe7550c63d4d16b"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-win32.whl", hash = "sha256:2cac9ed5c343bb3d0075db6e797e6112514764d08d667c74cb89b931aac9dddd"}, + {file = "pycryptodomex-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:ff46212fda7ee86ec2f4a64016c994e8ad80f11ef748131753adb67e9b722ebd"}, + {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c8cffb03f5dee1026e3f892f7cffd79926a538c67c34f8b07c90c0bd5c834e27"}, + {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:140b27caa68a36d0501b05eb247bd33afa5f854c1ee04140e38af63c750d4e39"}, + {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:644834b1836bb8e1d304afaf794d5ae98a1d637bd6e140c9be7dd192b5374811"}, + {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c506aba3318505dbeecf821ed7b9a9f86f422ed085e2d79c4fba0ae669920a"}, + {file = "pycryptodomex-3.22.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7cd39f7a110c1ab97ce9ee3459b8bc615920344dc00e56d1b709628965fba3f2"}, + {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e4eaaf6163ff13788c1f8f615ad60cdc69efac6d3bf7b310b21e8cfe5f46c801"}, + {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eac39e237d65981554c2d4c6668192dc7051ad61ab5fc383ed0ba049e4007ca2"}, + {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab0d89d1761959b608952c7b347b0e76a32d1a5bb278afbaa10a7f3eaef9a0a"}, + {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e64164f816f5e43fd69f8ed98eb28f98157faf68208cd19c44ed9d8e72d33e8"}, + {file = "pycryptodomex-3.22.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f005de31efad6f9acefc417296c641f13b720be7dbfec90edeaca601c0fab048"}, + {file = "pycryptodomex-3.22.0.tar.gz", hash = "sha256:a1da61bacc22f93a91cbe690e3eb2022a03ab4123690ab16c46abb693a9df63d"}, +] + [[package]] name = "pymysql" version = "1.1.1" @@ -831,13 +990,114 @@ files = [ {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] +[[package]] +name = "websockets" +version = "15.0.1" +requires_python = ">=3.9" +summary = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +groups = ["default"] +files = [ + {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"}, + {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"}, + {file = "websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a"}, + {file = "websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e"}, + {file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf"}, + {file = "websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb"}, + {file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d"}, + {file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9"}, + {file = "websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c"}, + {file = "websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256"}, + {file = "websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41"}, + {file = "websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431"}, + {file = "websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57"}, + {file = "websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905"}, + {file = "websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562"}, + {file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792"}, + {file = "websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413"}, + {file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8"}, + {file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3"}, + {file = "websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf"}, + {file = "websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85"}, + {file = "websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065"}, + {file = "websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3"}, + {file = "websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665"}, + {file = "websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2"}, + {file = "websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215"}, + {file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5"}, + {file = "websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65"}, + {file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe"}, + {file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4"}, + {file = "websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597"}, + {file = "websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9"}, + {file = "websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7"}, + {file = "websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931"}, + {file = "websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675"}, + {file = "websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151"}, + {file = "websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22"}, + {file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f"}, + {file = "websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8"}, + {file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375"}, + {file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d"}, + {file = "websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4"}, + {file = "websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa"}, + {file = "websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561"}, + {file = "websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5"}, + {file = "websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a"}, + {file = "websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b"}, + {file = "websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770"}, + {file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb"}, + {file = "websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054"}, + {file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee"}, + {file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed"}, + {file = "websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880"}, + {file = "websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411"}, + {file = "websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04"}, + {file = "websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f"}, + {file = "websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123"}, + {file = "websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f"}, + {file = "websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee"}, +] + [[package]] name = "yt-dlp" -version = "2025.1.15" +version = "2025.3.21" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.1.15-py3-none-any.whl", hash = "sha256:b8666b88e23c3fa5ee1e80920f4a9dfac7c405504a447214c0cf3d0c386edcfc"}, - {file = "yt_dlp-2025.1.15.tar.gz", hash = "sha256:e8ec515d49bb62704915d13a22ee6fe03a5658d651e4e64574e3a17ee01f6e3b"}, + {file = "yt_dlp-2025.3.21-py3-none-any.whl", hash = "sha256:80d5ce15f9223e0c27020b861a4c5b72c6ba5d6c957c1b8fd2a022a69783f482"}, + {file = "yt_dlp-2025.3.21.tar.gz", hash = "sha256:5bcf47b2897254ea3816935a8dde47d243bff556782cced6b16a2b85e6b682ba"}, +] + +[[package]] +name = "yt-dlp" +version = "2025.3.21" +extras = ["default"] +requires_python = ">=3.9" +summary = "A feature-rich command-line audio/video downloader" +groups = ["default"] +dependencies = [ + "brotli; implementation_name == \"cpython\"", + "brotlicffi; implementation_name != \"cpython\"", + "certifi", + "mutagen", + "pycryptodomex", + "requests<3,>=2.32.2", + "urllib3<3,>=1.26.17", + "websockets>=13.0", + "yt-dlp==2025.3.21", +] +files = [ + {file = "yt_dlp-2025.3.21-py3-none-any.whl", hash = "sha256:80d5ce15f9223e0c27020b861a4c5b72c6ba5d6c957c1b8fd2a022a69783f482"}, + {file = "yt_dlp-2025.3.21.tar.gz", hash = "sha256:5bcf47b2897254ea3816935a8dde47d243bff556782cced6b16a2b85e6b682ba"}, ] diff --git a/pyproject.toml b/pyproject.toml index 050100b7..58beeccb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp==2025.1.15", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.1.38", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[default]==2025.3.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.1.38", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index d03ced7a..42bb9286 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp==2025.2.19 +yt-dlp[default]==2025.3.21 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 5e4552c251857ec0d20b0d3cc61cb12b8d0146e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 13:55:29 +0100 Subject: [PATCH 271/297] Bump yt-dlp[default] from 2025.3.21 to 2025.3.27 (#498) Bumps [yt-dlp[default]](https://github.com/yt-dlp/yt-dlp) from 2025.3.21 to 2025.3.27. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.03.21...2025.03.27) --- updated-dependencies: - dependency-name: yt-dlp[default] dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 42bb9286..619b5412 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default]==2025.3.21 +yt-dlp[default]==2025.3.27 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From a033e05bac456b6419468a1bfeabac943427e6d7 Mon Sep 17 00:00:00 2001 From: Benny Date: Fri, 28 Mar 2025 13:59:09 +0100 Subject: [PATCH 272/297] tag with commit hash fix #494 --- .github/workflows/builder.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index e6d548a0..47431167 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -62,7 +62,9 @@ jobs: push: true tags: | ${{ steps.dh_string.outputs.lowercase }} + ${{ steps.dh_string.outputs.lowercase }}:${{ github.sha }} ghcr.io/${{ steps.ghcr_string.outputs.lowercase }} + ghcr.io/${{ steps.ghcr_string.outputs.lowercase }}:${{ github.sha }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max From a58ce2c59815370d77832871df79280ebeeffcaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:23:27 +0200 Subject: [PATCH 273/297] Bump yt-dlp[default] from 2025.3.27 to 2025.3.31 (#499) Bumps [yt-dlp[default]](https://github.com/yt-dlp/yt-dlp) from 2025.3.27 to 2025.3.31. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.03.27...2025.03.31) --- updated-dependencies: - dependency-name: yt-dlp[default] dependency-version: 2025.3.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 619b5412..98f55d7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default]==2025.3.27 +yt-dlp[default]==2025.3.31 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 8a75208170658fdbd70d6bb984f48fe8b9434520 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:53:21 +0530 Subject: [PATCH 274/297] Bump kurigram from 2.1.38 to 2.2.0 (PDM) (#503) --- pdm.lock | 8 ++++---- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pdm.lock b/pdm.lock index 94630c6f..7ecb2e88 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:88a10a1ea7ef558cb7e9ffbb1cf1743c89e9e997c3d99a4495e7aea3b927689b" +content_hash = "sha256:8860d4315231990d3db2a776b2812c4ecba31afab2516fdbde7a347e340e9dfa" [[metadata.targets]] requires_python = ">3.9" @@ -586,7 +586,7 @@ files = [ [[package]] name = "kurigram" -version = "2.1.38" +version = "2.2.0" requires_python = "~=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -595,8 +595,8 @@ dependencies = [ "pysocks==1.7.1", ] files = [ - {file = "Kurigram-2.1.38-py3-none-any.whl", hash = "sha256:6489803d829e90ee10a9e09d163fa95278e14ed5a29761818487c43ec8961ecc"}, - {file = "kurigram-2.1.38.tar.gz", hash = "sha256:d26fe47eaa8b29e494cceda6742045b96ff2d3f429fd28c2bfb25b3216eb8873"}, + {file = "kurigram-2.2.0-py3-none-any.whl", hash = "sha256:e412a100805bd057cf504079b4b2a01739fbbe0d1d4bf9eeeb4df88cc5003e52"}, + {file = "kurigram-2.2.0.tar.gz", hash = "sha256:d644316ce64c7ecbca57052e646ae63a0ef251afc6957db767012456f040c02d"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 58beeccb..1b5864fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[default]==2025.3.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.1.38", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[default]==2025.3.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.0", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 98f55d7e..13604155 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.1.38 +kurigram==2.2.0 cryptography>=43.0.3 From 68948c47f8f87c4ec97e2cd91bfdb8b776bd5b32 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 1 May 2025 12:57:42 +0530 Subject: [PATCH 275/297] Bump dependencies; (#509) kurigram 2.2.0 -> 2..2.1 yt-dlp 2025.3.31 -> 2025.4.30 --- pdm.lock | 51 +++++++++++++++++++++++++++++++++++------------- pyproject.toml | 2 +- requirements.txt | 4 ++-- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pdm.lock b/pdm.lock index 7ecb2e88..12a8412d 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:8860d4315231990d3db2a776b2812c4ecba31afab2516fdbde7a347e340e9dfa" +content_hash = "sha256:cf1a7e621bc582ac3cb1c8e6f22f33f42fa871c0e9a164a05226c88d17d3b13f" [[metadata.targets]] requires_python = ">3.9" @@ -226,7 +226,6 @@ version = "1.17.1" requires_python = ">=3.8" summary = "Foreign Function Interface for Python calling C code." groups = ["default"] -marker = "platform_python_implementation != \"PyPy\" or implementation_name != \"cpython\"" dependencies = [ "pycparser", ] @@ -444,6 +443,30 @@ files = [ {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, ] +[[package]] +name = "curl-cffi" +version = "0.10.0" +requires_python = ">=3.9" +summary = "libcurl ffi bindings for Python, with impersonation support." +groups = ["default"] +marker = "implementation_name == \"cpython\"" +dependencies = [ + "certifi>=2024.2.2", + "cffi>=1.12.0", +] +files = [ + {file = "curl_cffi-0.10.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:15053d01c6a3e3c4c5331ce9e07e1dc31ca5aa063babca05d18b1b5aad369fac"}, + {file = "curl_cffi-0.10.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:3969e4260ad4dab638fb6dbe349623f9f5f022435c7fd21daf760231380367fa"}, + {file = "curl_cffi-0.10.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:458f53c41bd76d90d8974d60c3a8a0dd902a1af1f9056215cf24f454bcedc6fd"}, + {file = "curl_cffi-0.10.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfc74f09e44d2d8d61b8e8fda3a7004b5bc0217a703fbbe9e16ef8caa1f3d4e4"}, + {file = "curl_cffi-0.10.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f03f4b17dc679c82bd3c946feb1ad38749b2ad731d7c26daefaac857d1c72fd9"}, + {file = "curl_cffi-0.10.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f1b0c7b7b81afca15a0e56c593d3c2bdcd4fd4c9ca49b9ded5b9d8076ba78ff9"}, + {file = "curl_cffi-0.10.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:04b1d23f0f54f94b8298ed417e6bece85a635d674723cde2b155da686efbf78f"}, + {file = "curl_cffi-0.10.0-cp39-abi3-win32.whl", hash = "sha256:1e60b8ecc80bfb0da4ff73ac9d194e80482b50ecbb8aefec1b0edaf45fafd80e"}, + {file = "curl_cffi-0.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:59389773a1556e087120e91eac1e33f84f1599d853e1bc168b153e4cdf360002"}, + {file = "curl_cffi-0.10.0.tar.gz", hash = "sha256:3e37b35268ca58492f54ed020ae4b50c33ee0debad4145db9f746f04ed466eb0"}, +] + [[package]] name = "fakeredis" version = "2.26.2" @@ -586,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.0" +version = "2.2.1" requires_python = "~=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -595,8 +618,8 @@ dependencies = [ "pysocks==1.7.1", ] files = [ - {file = "kurigram-2.2.0-py3-none-any.whl", hash = "sha256:e412a100805bd057cf504079b4b2a01739fbbe0d1d4bf9eeeb4df88cc5003e52"}, - {file = "kurigram-2.2.0.tar.gz", hash = "sha256:d644316ce64c7ecbca57052e646ae63a0ef251afc6957db767012456f040c02d"}, + {file = "kurigram-2.2.1-py3-none-any.whl", hash = "sha256:94c9f23ee5cd2a868a557929b3ecf24fb4ff530a7f0a0f633676db2391fc9545"}, + {file = "kurigram-2.2.1.tar.gz", hash = "sha256:63fae5471d3824d5e08188180edf21ecfe13ed1bb53504308d0015469e4c9581"}, ] [[package]] @@ -686,7 +709,6 @@ version = "2.22" requires_python = ">=3.8" summary = "C parser in Python" groups = ["default"] -marker = "platform_python_implementation != \"PyPy\" or implementation_name != \"cpython\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1070,19 +1092,19 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.3.21" +version = "2025.4.30" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.3.21-py3-none-any.whl", hash = "sha256:80d5ce15f9223e0c27020b861a4c5b72c6ba5d6c957c1b8fd2a022a69783f482"}, - {file = "yt_dlp-2025.3.21.tar.gz", hash = "sha256:5bcf47b2897254ea3816935a8dde47d243bff556782cced6b16a2b85e6b682ba"}, + {file = "yt_dlp-2025.4.30-py3-none-any.whl", hash = "sha256:53cd82bf13f12a1fe9c564b0004e001156b153c9247fa3cef14d1400ab359150"}, + {file = "yt_dlp-2025.4.30.tar.gz", hash = "sha256:d01367d0c3ae94e35cb1e2eccb7a7c70e181c4ca448f4ee2374f26489d263603"}, ] [[package]] name = "yt-dlp" -version = "2025.3.21" -extras = ["default"] +version = "2025.4.30" +extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] @@ -1090,14 +1112,15 @@ dependencies = [ "brotli; implementation_name == \"cpython\"", "brotlicffi; implementation_name != \"cpython\"", "certifi", + "curl-cffi!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.11,>=0.5.10; implementation_name == \"cpython\"", "mutagen", "pycryptodomex", "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.3.21", + "yt-dlp==2025.4.30", ] files = [ - {file = "yt_dlp-2025.3.21-py3-none-any.whl", hash = "sha256:80d5ce15f9223e0c27020b861a4c5b72c6ba5d6c957c1b8fd2a022a69783f482"}, - {file = "yt_dlp-2025.3.21.tar.gz", hash = "sha256:5bcf47b2897254ea3816935a8dde47d243bff556782cced6b16a2b85e6b682ba"}, + {file = "yt_dlp-2025.4.30-py3-none-any.whl", hash = "sha256:53cd82bf13f12a1fe9c564b0004e001156b153c9247fa3cef14d1400ab359150"}, + {file = "yt_dlp-2025.4.30.tar.gz", hash = "sha256:d01367d0c3ae94e35cb1e2eccb7a7c70e181c4ca448f4ee2374f26489d263603"}, ] diff --git a/pyproject.toml b/pyproject.toml index 1b5864fc..6c870394 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[default]==2025.3.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.0", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.4.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.1", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 13604155..e9e7b74e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default]==2025.3.31 +yt-dlp[default,curl-cffi]==2025.4.30 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.0 +kurigram==2.2.1 cryptography>=43.0.3 From b4869742e76c636c03620d6883fa1fa846d5b71f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 19:04:44 +0800 Subject: [PATCH 276/297] Bump kurigram from 2.2.1 to 2.2.3 (#511) Bumps [kurigram](https://github.com/KurimuzonAkuma/pyrogram) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/KurimuzonAkuma/pyrogram/releases) - [Commits](https://github.com/KurimuzonAkuma/pyrogram/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: kurigram dependency-version: 2.2.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e9e7b74e..12bc687c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.1 +kurigram==2.2.3 cryptography>=43.0.3 From 1045b93b95036383ee8715090aea33ea664c7f04 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 15 May 2025 13:25:37 +0530 Subject: [PATCH 277/297] Update kurigram 2.2.1 -> 2.2.3 (PDM) (#513) --- pdm.lock | 14 +++++++------- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pdm.lock b/pdm.lock index 12a8412d..3bf62730 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:cf1a7e621bc582ac3cb1c8e6f22f33f42fa871c0e9a164a05226c88d17d3b13f" +content_hash = "sha256:9525cb30638a9b0150858d1aa9148e7a2f54f92c9ae220b8157d2d7f0196f514" [[metadata.targets]] requires_python = ">3.9" @@ -609,17 +609,17 @@ files = [ [[package]] name = "kurigram" -version = "2.2.1" -requires_python = "~=3.8" +version = "2.2.3" +requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] dependencies = [ - "pyaes==1.6.1", - "pysocks==1.7.1", + "pyaes<=1.6.1", + "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.1-py3-none-any.whl", hash = "sha256:94c9f23ee5cd2a868a557929b3ecf24fb4ff530a7f0a0f633676db2391fc9545"}, - {file = "kurigram-2.2.1.tar.gz", hash = "sha256:63fae5471d3824d5e08188180edf21ecfe13ed1bb53504308d0015469e4c9581"}, + {file = "kurigram-2.2.3-py3-none-any.whl", hash = "sha256:60b01fdb7094dab1d704fd794ce84feb6798c96c212457d19a80aa96f0104f62"}, + {file = "kurigram-2.2.3.tar.gz", hash = "sha256:8c986dc4646412a84208db5404bdd1b4bb06c39ca6787b4d1c2425a97ee21087"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 6c870394..a85f57dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.4.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.1", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.4.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.3", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} From 7c9eeec7ecd928cc7a85ae1e74e75d88f111fa5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 15:55:49 +0800 Subject: [PATCH 278/297] Bump redis from 5.2.1 to 6.1.0 (#512) Bumps [redis](https://github.com/redis/redis-py) from 5.2.1 to 6.1.0. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.2.1...v6.1.0) --- updated-dependencies: - dependency-name: redis dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 12bc687c..e3b3678e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ PyMySQL>=1.1.1 filetype>=1.2.0 beautifulsoup4>=4.12.3 fakeredis>=2.26.2 -redis==5.2.1 +redis==6.1.0 requests>=2.32.3 tqdm>=4.67.1 token-bucket>=0.3.0 From f881308c6a7223943c431dce8cda89f67829f623 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 31 May 2025 06:04:51 +0530 Subject: [PATCH 279/297] Bump kurigram, redis and yt-dlp to the latest version (#519) --- pdm.lock | 30 +++++++++++++++--------------- pyproject.toml | 2 +- requirements.txt | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pdm.lock b/pdm.lock index 3bf62730..df80e99b 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:9525cb30638a9b0150858d1aa9148e7a2f54f92c9ae220b8157d2d7f0196f514" +content_hash = "sha256:3d49e721debb3976f953f20e91bd3bc6edf2c5be49c47f7dc884cac882741cfd" [[metadata.targets]] requires_python = ">3.9" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.3" +version = "2.2.4" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.3-py3-none-any.whl", hash = "sha256:60b01fdb7094dab1d704fd794ce84feb6798c96c212457d19a80aa96f0104f62"}, - {file = "kurigram-2.2.3.tar.gz", hash = "sha256:8c986dc4646412a84208db5404bdd1b4bb06c39ca6787b4d1c2425a97ee21087"}, + {file = "kurigram-2.2.4-py3-none-any.whl", hash = "sha256:c6bbe02075cfa8f27660698c3ebcc5ecfa4801fdc8034a50c14e67e5165739fd"}, + {file = "kurigram-2.2.4.tar.gz", hash = "sha256:3d00716c969d83e1bd40df9b158707ced3dc15ea082d3df97e1215e1f07dd7c2"}, ] [[package]] @@ -779,16 +779,16 @@ files = [ [[package]] name = "redis" -version = "5.2.1" -requires_python = ">=3.8" +version = "6.2.0" +requires_python = ">=3.9" summary = "Python client for Redis database and key-value store" groups = ["default"] dependencies = [ "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", ] files = [ - {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, - {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, + {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, + {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.4.30" +version = "2025.5.22" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.4.30-py3-none-any.whl", hash = "sha256:53cd82bf13f12a1fe9c564b0004e001156b153c9247fa3cef14d1400ab359150"}, - {file = "yt_dlp-2025.4.30.tar.gz", hash = "sha256:d01367d0c3ae94e35cb1e2eccb7a7c70e181c4ca448f4ee2374f26489d263603"}, + {file = "yt_dlp-2025.5.22-py3-none-any.whl", hash = "sha256:a49c4b76afeaded6254c3e2b759d8d5a13271aa963d5fccb51fe059d1c313151"}, + {file = "yt_dlp-2025.5.22.tar.gz", hash = "sha256:ea73854c5dabc124f29a35a8fae9bc5d422ef3231bebeea2bdfa82ac191a9c29"}, ] [[package]] name = "yt-dlp" -version = "2025.4.30" +version = "2025.5.22" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.4.30", + "yt-dlp==2025.5.22", ] files = [ - {file = "yt_dlp-2025.4.30-py3-none-any.whl", hash = "sha256:53cd82bf13f12a1fe9c564b0004e001156b153c9247fa3cef14d1400ab359150"}, - {file = "yt_dlp-2025.4.30.tar.gz", hash = "sha256:d01367d0c3ae94e35cb1e2eccb7a7c70e181c4ca448f4ee2374f26489d263603"}, + {file = "yt_dlp-2025.5.22-py3-none-any.whl", hash = "sha256:a49c4b76afeaded6254c3e2b759d8d5a13271aa963d5fccb51fe059d1c313151"}, + {file = "yt_dlp-2025.5.22.tar.gz", hash = "sha256:ea73854c5dabc124f29a35a8fae9bc5d422ef3231bebeea2bdfa82ac191a9c29"}, ] diff --git a/pyproject.toml b/pyproject.toml index a85f57dc..ad5e0e5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.4.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==5.2.1", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.3", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.5.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.4", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index e3b3678e..89909ee5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.4.30 +yt-dlp[default,curl-cffi]==2025.5.22 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 filetype>=1.2.0 beautifulsoup4>=4.12.3 fakeredis>=2.26.2 -redis==6.1.0 +redis==6.2.0 requests>=2.32.3 tqdm>=4.67.1 token-bucket>=0.3.0 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.3 +kurigram==2.2.4 cryptography>=43.0.3 From e347a7f8b6170b01b86b18c81070b4a7921f1147 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 13 Jun 2025 15:55:02 +0530 Subject: [PATCH 280/297] Bump yt-dlp[curl-cffi,default] from 2025.5.22 to 2025.6.9 (#521) --- pdm.lock | 22 +++++++++++----------- pyproject.toml | 2 +- requirements.txt | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pdm.lock b/pdm.lock index df80e99b..0c6fc923 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:3d49e721debb3976f953f20e91bd3bc6edf2c5be49c47f7dc884cac882741cfd" +content_hash = "sha256:b9571eba6704a3b307ac05afcca5ba3d6286b827da24f54bad27cfead2ff6211" [[metadata.targets]] requires_python = ">3.9" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.4" +version = "2.2.6" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.4-py3-none-any.whl", hash = "sha256:c6bbe02075cfa8f27660698c3ebcc5ecfa4801fdc8034a50c14e67e5165739fd"}, - {file = "kurigram-2.2.4.tar.gz", hash = "sha256:3d00716c969d83e1bd40df9b158707ced3dc15ea082d3df97e1215e1f07dd7c2"}, + {file = "kurigram-2.2.6-py3-none-any.whl", hash = "sha256:0fd245a7117e2b6479149995857c81b93cb19979ddadcf3fc8063dca05465847"}, + {file = "kurigram-2.2.6.tar.gz", hash = "sha256:15b64199a076c12b2c73915f66fb580294ea4ca6c1cba05f33e19a364a569d3d"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.5.22" +version = "2025.6.9" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.5.22-py3-none-any.whl", hash = "sha256:a49c4b76afeaded6254c3e2b759d8d5a13271aa963d5fccb51fe059d1c313151"}, - {file = "yt_dlp-2025.5.22.tar.gz", hash = "sha256:ea73854c5dabc124f29a35a8fae9bc5d422ef3231bebeea2bdfa82ac191a9c29"}, + {file = "yt_dlp-2025.6.9-py3-none-any.whl", hash = "sha256:ebdfda9ffa807f6a26aed7c8f906e5557cd06b4c388dc547df1ec2078631fca8"}, + {file = "yt_dlp-2025.6.9.tar.gz", hash = "sha256:751f53a3b61353522bf805fa30bbcbd16666126537e39706eab4f8c368f111ac"}, ] [[package]] name = "yt-dlp" -version = "2025.5.22" +version = "2025.6.9" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.5.22", + "yt-dlp==2025.6.9", ] files = [ - {file = "yt_dlp-2025.5.22-py3-none-any.whl", hash = "sha256:a49c4b76afeaded6254c3e2b759d8d5a13271aa963d5fccb51fe059d1c313151"}, - {file = "yt_dlp-2025.5.22.tar.gz", hash = "sha256:ea73854c5dabc124f29a35a8fae9bc5d422ef3231bebeea2bdfa82ac191a9c29"}, + {file = "yt_dlp-2025.6.9-py3-none-any.whl", hash = "sha256:ebdfda9ffa807f6a26aed7c8f906e5557cd06b4c388dc547df1ec2078631fca8"}, + {file = "yt_dlp-2025.6.9.tar.gz", hash = "sha256:751f53a3b61353522bf805fa30bbcbd16666126537e39706eab4f8c368f111ac"}, ] diff --git a/pyproject.toml b/pyproject.toml index ad5e0e5f..a69578c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.5.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.4", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.9", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 89909ee5..1f52cadc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.5.22 +yt-dlp[default,curl-cffi]==2025.6.9 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.4 +kurigram==2.2.6 cryptography>=43.0.3 From a2f61acb19edf9a9b9205377ac24aa95ef1690c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:10:19 +0800 Subject: [PATCH 281/297] Bump yt-dlp[curl-cffi,default] from 2025.6.9 to 2025.6.25 (#524) Bumps [yt-dlp[curl-cffi,default]](https://github.com/yt-dlp/yt-dlp) from 2025.6.9 to 2025.6.25. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.06.09...2025.06.25) --- updated-dependencies: - dependency-name: yt-dlp[curl-cffi,default] dependency-version: 2025.6.25 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1f52cadc..39ef1c87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.6.9 +yt-dlp[default,curl-cffi]==2025.6.25 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 61b5017a89c7becc534931f0f8ff98ce74d750b6 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:29:08 +0530 Subject: [PATCH 282/297] Bump yt-dlp[curl-cffi,default] from 2025.6.9 to 2025.6.25 (PDM) (#525) --- pdm.lock | 16 ++++++++-------- pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pdm.lock b/pdm.lock index 0c6fc923..71c61702 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:b9571eba6704a3b307ac05afcca5ba3d6286b827da24f54bad27cfead2ff6211" +content_hash = "sha256:1f8790b2678afab570955b745664ddc1840854dbf1d89d9a510c6083520eab21" [[metadata.targets]] requires_python = ">3.9" @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.6.9" +version = "2025.6.25" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.6.9-py3-none-any.whl", hash = "sha256:ebdfda9ffa807f6a26aed7c8f906e5557cd06b4c388dc547df1ec2078631fca8"}, - {file = "yt_dlp-2025.6.9.tar.gz", hash = "sha256:751f53a3b61353522bf805fa30bbcbd16666126537e39706eab4f8c368f111ac"}, + {file = "yt_dlp-2025.6.25-py3-none-any.whl", hash = "sha256:1eb31c9a47d56c7433be23a6ae084c640bd4e14961ad43076927ef05280871ea"}, + {file = "yt_dlp-2025.6.25.tar.gz", hash = "sha256:242b648e1a18ab04bdd4cc175a317fe8ec3ad7d0175eee9f981912624b3d6c8b"}, ] [[package]] name = "yt-dlp" -version = "2025.6.9" +version = "2025.6.25" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.6.9", + "yt-dlp==2025.6.25", ] files = [ - {file = "yt_dlp-2025.6.9-py3-none-any.whl", hash = "sha256:ebdfda9ffa807f6a26aed7c8f906e5557cd06b4c388dc547df1ec2078631fca8"}, - {file = "yt_dlp-2025.6.9.tar.gz", hash = "sha256:751f53a3b61353522bf805fa30bbcbd16666126537e39706eab4f8c368f111ac"}, + {file = "yt_dlp-2025.6.25-py3-none-any.whl", hash = "sha256:1eb31c9a47d56c7433be23a6ae084c640bd4e14961ad43076927ef05280871ea"}, + {file = "yt_dlp-2025.6.25.tar.gz", hash = "sha256:242b648e1a18ab04bdd4cc175a317fe8ec3ad7d0175eee9f981912624b3d6c8b"}, ] diff --git a/pyproject.toml b/pyproject.toml index a69578c7..bbb645f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.9", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.25", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} From f39d3639b4bad28d7c9d99497796609b5d35ba09 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sat, 28 Jun 2025 06:03:11 +0530 Subject: [PATCH 283/297] Improve media upload reliability and error handling (#527) * Bump yt-dlp[curl-cffi,default] from 2025.6.9 to 2025.6.25 (PDM) * Improve upload reliability and error handling --- src/engine/__init__.py | 45 ++++++++++++++------ src/engine/base.py | 94 ++++++++++++++++++++++++++++++++++------- src/engine/instagram.py | 28 ++++++++++-- src/main.py | 22 ++++++++-- 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/src/engine/__init__.py b/src/engine/__init__.py index 1d6b97a7..ec9b2a54 100644 --- a/src/engine/__init__.py +++ b/src/engine/__init__.py @@ -4,6 +4,7 @@ # ytdlbot - __init__.py.py from urllib.parse import urlparse +from typing import Any, Callable from engine.generic import YoutubeDownload from engine.direct import DirectDownload @@ -22,17 +23,33 @@ def direct_entrance(client, bot_message, url): dl.start() -def special_download_entrance(client, bot_message, url): - """Specific link downloader""" - domain = urlparse(url).hostname - if "youtube.com" in domain or "youtu.be" in domain: - raise ValueError("ERROR: For Youtube links, just send the link.") - elif "www.instagram.com" in domain: - dl = InstagramDownload(client, bot_message, url) - dl.start() - elif "pixeldrain.com" in domain: - return pixeldrain_download(client, bot_message, url) - elif "krakenfiles.com" in domain: - return krakenfiles_download(client, bot_message, url) - else: - raise ValueError(f"Invalid URL: No specific link function found for {url}") +# --- Handler for the Instagram class, to make the interface consistent --- +def instagram_handler(client: Any, bot_message: Any, url: str) -> None: + """A wrapper to handle the InstagramDownload class.""" + downloader = InstagramDownload(client, bot_message, url) + downloader.start() + +DOWNLOADER_MAP: dict[str, Callable[[Any, Any, str], Any]] = { + "pixeldrain.com": pixeldrain_download, + "krakenfiles.com": krakenfiles_download, + "instagram.com": instagram_handler, +} + +def special_download_entrance(client: Any, bot_message: Any, url: str) -> Any: + try: + hostname = urlparse(url).hostname + if not hostname: + raise ValueError(f"Could not parse a valid hostname from URL: {url}") + except (ValueError, TypeError) as e: + raise ValueError(f"Invalid URL format: {url}") from e + + # Handle the special case for YouTube URLs first. + if hostname.endswith("youtube.com") or hostname == "youtu.be": + raise ValueError("ERROR: For YouTube links, just send the link directly.") + + # Iterate through the map to find a matching handler. + for domain_suffix, handler_function in DOWNLOADER_MAP.items(): + if hostname.endswith(domain_suffix): + return handler_function(client, bot_message, url) + + raise ValueError(f"Invalid URL: No specific downloader found for {hostname}") diff --git a/src/engine/base.py b/src/engine/base.py index 4390e64d..58ffdbf2 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -163,15 +163,34 @@ def send_something(self, *, chat_id, files, _type, caption=None, thumb=None, **k inputs = generate_input_media(files, caption) return self._client.send_media_group(chat_id, inputs)[0] else: - return self._methods[_type]( - chat_id, - files[0], - caption=caption, - thumb=thumb, - progress=self.upload_hook, - # progress_args=(self._bot_msg,), + file_arg_name = None + if _type == "photo": + file_arg_name = "photo" + elif _type == "video": + file_arg_name = "video" + elif _type == "animation": + file_arg_name = "animation" + elif _type == "document": + file_arg_name = "document" + elif _type == "audio": + file_arg_name = "audio" + else: + logging.error("Unknown _type encountered: %s", _type) + # You might want to raise an error or return None here + return None + + send_args = { + "chat_id": chat_id, + file_arg_name: files[0], + "caption": caption, + "progress": self.upload_hook, **kwargs, - ) + } + + if _type in ["video", "animation", "document", "audio"] and thumb is not None: + send_args["thumb"] = thumb + + return self._methods[_type](**send_args) def get_metadata(self): video_path = list(Path(self._tempdir.name).glob("*"))[0] @@ -212,9 +231,17 @@ def _upload(self, files=None, meta=None): chat_id=self._chat_id, files=files, _type="document", - thumb=meta["thumb"], + thumb=meta.get("thumb"), force_document=True, - caption=meta["caption"], + caption=meta.get("caption"), + ) + elif self._format == "photo": + logging.info("Sending as photo for %s", self._url) + success = self.send_something( + chat_id=self._chat_id, + files=files, + _type="photo", + caption=meta.get("caption"), ) elif self._format == "audio": logging.info("Sending as audio for %s", self._url) @@ -222,19 +249,54 @@ def _upload(self, files=None, meta=None): chat_id=self._chat_id, files=files, _type="audio", - caption=meta["caption"], + caption=meta.get("caption"), ) elif self._format == "video": logging.info("Sending as video for %s", self._url) - methods = {"video": meta, "animation": {}, "photo": {}} - for method, thumb in methods.items(): + attempt_methods = ["video", "animation", "audio", "photo"] + video_meta = meta.copy() + + upload_successful = False # Flag to track if any method succeeded + for method in attempt_methods: + current_meta = video_meta.copy() + + if method == "photo": + current_meta.pop("thumb", None) + current_meta.pop("duration", None) + current_meta.pop("height", None) + current_meta.pop("width", None) + elif method == "audio": + current_meta.pop("height", None) + current_meta.pop("width", None) + try: - success = self.send_something(chat_id=self._chat_id, files=files, _type=method, **meta) + success_obj = self.send_something( + chat_id=self._chat_id, + files=files, + _type=method, + **current_meta + ) + + if method == "video": + success = success_obj + elif method == "animation": + success = success_obj + elif method == "photo": + success = success_obj + elif method == "audio": + success = success_obj + + upload_successful = True # Set flag to True on success break except Exception as e: - logging.error("Retry to send as %s, error:", method, e) + logging.error("Retry to send as %s, error: %s", method, e) + + # Check the flag after the loop + if not upload_successful: + raise ValueError("ERROR: For direct links, try again with `/direct`.") + else: - logging.error("Unknown upload format settings") + logging.error("Unknown upload format settings for %s", self._format) return video_key = self._calc_video_key() diff --git a/src/engine/instagram.py b/src/engine/instagram.py index a039d4be..216c71e0 100644 --- a/src/engine/instagram.py +++ b/src/engine/instagram.py @@ -52,9 +52,22 @@ def _download(self, formats=None): code = self.extract_code() counter = 1 video_paths = [] + found_media_types = set() if url_results := resp.get("data"): - for link in url_results: + for media in url_results: + link = media["link"] + media_type = media["type"] + + if media_type == "image": + ext = "jpg" + found_media_types.add("photo") + elif media_type == "video": + ext = "mp4" + found_media_types.add("video") + else: + continue + try: req = requests.get(link, stream=True) length = int(req.headers.get("content-length", 0) or req.headers.get("x-full-image-content-length", 0)) @@ -111,15 +124,22 @@ def _download(self, formats=None): save_path.rename(new_path) save_path = new_path - video_paths.append(save_path) + video_paths.append(str(save_path)) counter += 1 except Exception as e: self._bot_msg.edit_text(f"Download failed!โŒ\n\n`{e}`") return [] + if "video" in found_media_types: + self._format = "video" + elif "photo" in found_media_types: + self._format = "photo" + else: + self._format = "document" + return video_paths def _start(self): - self._download() - self._upload() + downloaded_files = self._download() + self._upload(files=downloaded_files) diff --git a/src/main.py b/src/main.py index 44c5fd6e..67759d0b 100644 --- a/src/main.py +++ b/src/main.py @@ -291,7 +291,12 @@ def direct_download(client: Client, message: types.Message): message.reply_text("Send me a correct LINK.", quote=True) return bot_msg = message.reply_text("Direct download request received.", quote=True) - direct_entrance(client, bot_msg, url) + try: + direct_entrance(client, bot_msg, url) + except ValueError as e: + message.reply_text(e.__str__(), quote=True) + bot_msg.delete() + return @app.on_message(filters.command(["spdl"])) @@ -306,7 +311,12 @@ def spdl_handler(client: Client, message: types.Message): message.reply_text("Something wrong ๐Ÿค”.\nCheck your URL and send me again.", quote=True) return bot_msg = message.reply_text("SPDL request received.", quote=True) - special_download_entrance(client, bot_msg, url) + try: + special_download_entrance(client, bot_msg, url) + except ValueError as e: + message.reply_text(e.__str__(), quote=True) + bot_msg.delete() + return @app.on_message(filters.command(["ytdl"]) & filters.group) @@ -322,8 +332,12 @@ def ytdl_handler(client: Client, message: types.Message): return bot_msg = message.reply_text("Group download request received.", quote=True) - - youtube_entrance(client, bot_msg, url) + try: + youtube_entrance(client, bot_msg, url) + except ValueError as e: + message.reply_text(e.__str__(), quote=True) + bot_msg.delete() + return def check_link(url: str): From 28d342d0ef2ad4590c07e3f1ee87652ab6037237 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 10 Jul 2025 05:03:46 +0530 Subject: [PATCH 284/297] Bump yt-dlp[curl-cffi,default] from 2025.6.25 to 2025.6.30 (#529) --- pdm.lock | 16 ++++++++-------- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pdm.lock b/pdm.lock index 71c61702..9a61df57 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:1f8790b2678afab570955b745664ddc1840854dbf1d89d9a510c6083520eab21" +content_hash = "sha256:1e2987926c27301d6aa30c1a34c4b48a1b53fdbe16d2990bf0f312980bfa09fd" [[metadata.targets]] requires_python = ">3.9" @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.6.25" +version = "2025.6.30" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.6.25-py3-none-any.whl", hash = "sha256:1eb31c9a47d56c7433be23a6ae084c640bd4e14961ad43076927ef05280871ea"}, - {file = "yt_dlp-2025.6.25.tar.gz", hash = "sha256:242b648e1a18ab04bdd4cc175a317fe8ec3ad7d0175eee9f981912624b3d6c8b"}, + {file = "yt_dlp-2025.6.30-py3-none-any.whl", hash = "sha256:541becc29ed7b7b3a08751c0a66da4b7f8ee95cb81066221c78e83598bc3d1f3"}, + {file = "yt_dlp-2025.6.30.tar.gz", hash = "sha256:6d0ae855c0a55bfcc28dffba804ec8525b9b955d34a41191a1561a4cec03d8bd"}, ] [[package]] name = "yt-dlp" -version = "2025.6.25" +version = "2025.6.30" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.6.25", + "yt-dlp==2025.6.30", ] files = [ - {file = "yt_dlp-2025.6.25-py3-none-any.whl", hash = "sha256:1eb31c9a47d56c7433be23a6ae084c640bd4e14961ad43076927ef05280871ea"}, - {file = "yt_dlp-2025.6.25.tar.gz", hash = "sha256:242b648e1a18ab04bdd4cc175a317fe8ec3ad7d0175eee9f981912624b3d6c8b"}, + {file = "yt_dlp-2025.6.30-py3-none-any.whl", hash = "sha256:541becc29ed7b7b3a08751c0a66da4b7f8ee95cb81066221c78e83598bc3d1f3"}, + {file = "yt_dlp-2025.6.30.tar.gz", hash = "sha256:6d0ae855c0a55bfcc28dffba804ec8525b9b955d34a41191a1561a4cec03d8bd"}, ] diff --git a/pyproject.toml b/pyproject.toml index bbb645f0..fa66f780 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.25", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 39ef1c87..9e762236 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.6.25 +yt-dlp[default,curl-cffi]==2025.6.30 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From e5a0a5cc3c724c27f512b68bae9a04a5b742f9a8 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:23:09 +0530 Subject: [PATCH 285/297] Bump kurigram from 2.2.6 to 2.2.7 (#532) * Bump kurigram from 2.2.6 to 2.2.7 * Bump yt-dlp[curl-cffi,default] from 2025.6.30 to 2025.7.21 --- pdm.lock | 22 +++++++++++----------- pyproject.toml | 2 +- requirements.txt | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pdm.lock b/pdm.lock index 9a61df57..1de6e242 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:1e2987926c27301d6aa30c1a34c4b48a1b53fdbe16d2990bf0f312980bfa09fd" +content_hash = "sha256:36d63a2911bbdb136b211ef5a958417d685cdbcabc7bb9a8cb7782e05f8db37d" [[metadata.targets]] requires_python = ">3.9" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.6" +version = "2.2.7" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.6-py3-none-any.whl", hash = "sha256:0fd245a7117e2b6479149995857c81b93cb19979ddadcf3fc8063dca05465847"}, - {file = "kurigram-2.2.6.tar.gz", hash = "sha256:15b64199a076c12b2c73915f66fb580294ea4ca6c1cba05f33e19a364a569d3d"}, + {file = "kurigram-2.2.7-py3-none-any.whl", hash = "sha256:3ebcfd4f4e7e6e9a2547997b8915ed323f679d9e99fde70a1f30f4c72aee0a97"}, + {file = "kurigram-2.2.7.tar.gz", hash = "sha256:61f3529a0cdc0a0f10f8b98103c6509317d90ebc0ae69959f38a4b684ad244f8"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.6.30" +version = "2025.7.21" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.6.30-py3-none-any.whl", hash = "sha256:541becc29ed7b7b3a08751c0a66da4b7f8ee95cb81066221c78e83598bc3d1f3"}, - {file = "yt_dlp-2025.6.30.tar.gz", hash = "sha256:6d0ae855c0a55bfcc28dffba804ec8525b9b955d34a41191a1561a4cec03d8bd"}, + {file = "yt_dlp-2025.7.21-py3-none-any.whl", hash = "sha256:d7aa2b53f9b2f35453346360f41811a0dad1e956e70b35a4ae95039d4d815d15"}, + {file = "yt_dlp-2025.7.21.tar.gz", hash = "sha256:46fbb53eab1afbe184c45b4c17e9a6eba614be680e4c09de58b782629d0d7f43"}, ] [[package]] name = "yt-dlp" -version = "2025.6.30" +version = "2025.7.21" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=1.26.17", "websockets>=13.0", - "yt-dlp==2025.6.30", + "yt-dlp==2025.7.21", ] files = [ - {file = "yt_dlp-2025.6.30-py3-none-any.whl", hash = "sha256:541becc29ed7b7b3a08751c0a66da4b7f8ee95cb81066221c78e83598bc3d1f3"}, - {file = "yt_dlp-2025.6.30.tar.gz", hash = "sha256:6d0ae855c0a55bfcc28dffba804ec8525b9b955d34a41191a1561a4cec03d8bd"}, + {file = "yt_dlp-2025.7.21-py3-none-any.whl", hash = "sha256:d7aa2b53f9b2f35453346360f41811a0dad1e956e70b35a4ae95039d4d815d15"}, + {file = "yt_dlp-2025.7.21.tar.gz", hash = "sha256:46fbb53eab1afbe184c45b4c17e9a6eba614be680e4c09de58b782629d0d7f43"}, ] diff --git a/pyproject.toml b/pyproject.toml index fa66f780..60bca1fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.6.30", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.6", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.7.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 9e762236..61ac20e7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.6.30 +yt-dlp[default,curl-cffi]==2025.7.21 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.6 +kurigram==2.2.7 cryptography>=43.0.3 From 9b7d3d674c6f2f51a0ba7b06aea85f481a91c5ba Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:11:37 +0530 Subject: [PATCH 286/297] Improve Download Handling in `/direct` and Performance with Metadata Embedding in `yt-dlp` (#534) * Fix some files not returning properly in `/direct` command & cleanup unnecessary steps. * Add new `ydl_opts` to improve dl speed and embed metadata --- src/engine/base.py | 1 - src/engine/direct.py | 53 +++++++++++++++++++------------------------ src/engine/generic.py | 8 +++++++ 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/engine/base.py b/src/engine/base.py index 58ffdbf2..5ba232cd 100644 --- a/src/engine/base.py +++ b/src/engine/base.py @@ -176,7 +176,6 @@ def send_something(self, *, chat_id, files, _type, caption=None, thumb=None, **k file_arg_name = "audio" else: logging.error("Unknown _type encountered: %s", _type) - # You might want to raise an error or return None here return None send_args = { diff --git a/src/engine/direct.py b/src/engine/direct.py index 06db3d38..0388ad45 100644 --- a/src/engine/direct.py +++ b/src/engine/direct.py @@ -25,18 +25,18 @@ def _setup_formats(self) -> list | None: # direct download doesn't need to setup formats pass - def _get_aria2_name(self): - try: - cmd = f"aria2c --truncate-console-readout=true -x10 --dry-run --file-allocation=none {self._url}" - result = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) - stdout_str = result.stdout.decode("utf-8") - name = os.path.basename(stdout_str).split("\n")[0] - if len(name) == 0: - name = os.path.basename(self._url) - return name - except Exception: - name = os.path.basename(self._url) - return name + # def _get_aria2_name(self): + # try: + # cmd = f"aria2c --truncate-console-readout=true -x10 --dry-run --file-allocation=none {self._url}" + # result = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) + # stdout_str = result.stdout.decode("utf-8") + # name = os.path.basename(stdout_str).split("\n")[0] + # if len(name) == 0: + # name = os.path.basename(self._url) + # return name + # except Exception: + # name = os.path.basename(self._url) + # return name def _requests_download(self): logging.info("Requests download with url %s", self._url) @@ -54,8 +54,8 @@ def _requests_download(self): return [file.as_posix()] def _aria2_download(self): - ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.4472.124 Safari/537.36" - filename = self._get_aria2_name() + ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" + # filename = self._get_aria2_name() self._process = None try: self._bot_msg.edit_text("Aria2 download starting...") @@ -73,7 +73,6 @@ def _aria2_download(self): "--human-readable=true", f"--user-agent={ua}", "-d", temp_dir, - "-o", filename, self._url, ] @@ -113,21 +112,15 @@ def _aria2_download(self): stderr ) - files = [f for f in Path(temp_dir).glob("*") if f.is_file()] - if not files: + # This will get [Path_object] if a file is found, or None if no files are found. + files = [f] if (f := next((item for item in Path(temp_dir).glob("*") if item.is_file()), None)) is not None else None + if files is None: + logging.error(f"No files found in {temp_dir}") raise FileNotFoundError(f"No files found in {temp_dir}") + else: + logging.info("Successfully downloaded file: %s", files[0]) - file = files[0] - # Handle file extension - if not file.suffix: - if ext := filetype.guess_extension(file): - new_file = file.with_suffix(f".{ext}") - file.rename(new_file) - file = new_file - - logging.info("Successfully downloaded file: %s", file) - - return [file.as_posix()] + return files except subprocess.TimeoutExpired: error_msg = "Download timed out after 5 minutes." @@ -187,5 +180,5 @@ def _download(self, formats=None) -> list: return self._requests_download() def _start(self): - self._download() - self._upload() + downloaded_files = self._download() + self._upload(files=downloaded_files) \ No newline at end of file diff --git a/src/engine/generic.py b/src/engine/generic.py index 3516e554..bbcf3d7b 100644 --- a/src/engine/generic.py +++ b/src/engine/generic.py @@ -95,6 +95,14 @@ def _download(self, formats) -> list: "restrictfilenames": False, "quiet": True, "match_filter": match_filter, + "concurrent_fragments": 16, + "buffersize": 4194304, + "retries": 6, + "fragment_retries": 6, + "skip_unavailable_fragments": True, + "embed_metadata": True, + "embed_thumbnail": True, + "writethumbnail": False, } # setup cookies for youtube only if is_youtube(self._url): From 7279fc6ee5d415ecbcc69f079f5aec7819410a9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 08:36:17 +0800 Subject: [PATCH 287/297] Bump redis from 6.2.0 to 6.3.0 (#535) Bumps [redis](https://github.com/redis/redis-py) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v6.2.0...v6.3.0) --- updated-dependencies: - dependency-name: redis dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 61ac20e7..c6a0dbcc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ PyMySQL>=1.1.1 filetype>=1.2.0 beautifulsoup4>=4.12.3 fakeredis>=2.26.2 -redis==6.2.0 +redis==6.3.0 requests>=2.32.3 tqdm>=4.67.1 token-bucket>=0.3.0 From 933eb4c994932821aaffb566c6b26795222bafe0 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:49:20 +0530 Subject: [PATCH 288/297] Bump redis from 6.2.0 to 6.3.0 (PDM) (#536) --- pdm.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index 1de6e242..e9fa227a 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:36d63a2911bbdb136b211ef5a958417d685cdbcabc7bb9a8cb7782e05f8db37d" +content_hash = "sha256:ccf9ba0ca0c00ffdfc237d096da38a3e58ef321b35e261a062476f705f3de081" [[metadata.targets]] requires_python = ">3.9" @@ -779,7 +779,7 @@ files = [ [[package]] name = "redis" -version = "6.2.0" +version = "6.3.0" requires_python = ">=3.9" summary = "Python client for Redis database and key-value store" groups = ["default"] @@ -787,8 +787,8 @@ dependencies = [ "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", ] files = [ - {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, - {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, + {file = "redis-6.3.0-py3-none-any.whl", hash = "sha256:92f079d656ded871535e099080f70fab8e75273c0236797126ac60242d638e9b"}, + {file = "redis-6.3.0.tar.gz", hash = "sha256:3000dbe532babfb0999cdab7b3e5744bcb23e51923febcfaeb52c8cfb29632ef"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 60bca1fc..23823ae5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.7.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.2.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.7.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.3.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} From 35aaf26a975bdd085ecb3d43fdd061228184d0d6 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:37:35 +0530 Subject: [PATCH 289/297] Bump yt-dlp and redis to the latest versions (#539) --- pdm.lock | 26 +++++++++++++------------- pyproject.toml | 2 +- requirements.txt | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pdm.lock b/pdm.lock index e9fa227a..f8a76c58 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:ccf9ba0ca0c00ffdfc237d096da38a3e58ef321b35e261a062476f705f3de081" +content_hash = "sha256:abb5216ce445b61a4a487cc4d5468443847414d233c67b0414f50416489f2fb5" [[metadata.targets]] requires_python = ">3.9" @@ -779,7 +779,7 @@ files = [ [[package]] name = "redis" -version = "6.3.0" +version = "6.4.0" requires_python = ">=3.9" summary = "Python client for Redis database and key-value store" groups = ["default"] @@ -787,8 +787,8 @@ dependencies = [ "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", ] files = [ - {file = "redis-6.3.0-py3-none-any.whl", hash = "sha256:92f079d656ded871535e099080f70fab8e75273c0236797126ac60242d638e9b"}, - {file = "redis-6.3.0.tar.gz", hash = "sha256:3000dbe532babfb0999cdab7b3e5744bcb23e51923febcfaeb52c8cfb29632ef"}, + {file = "redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f"}, + {file = "redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.7.21" +version = "2025.8.11" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.7.21-py3-none-any.whl", hash = "sha256:d7aa2b53f9b2f35453346360f41811a0dad1e956e70b35a4ae95039d4d815d15"}, - {file = "yt_dlp-2025.7.21.tar.gz", hash = "sha256:46fbb53eab1afbe184c45b4c17e9a6eba614be680e4c09de58b782629d0d7f43"}, + {file = "yt_dlp-2025.8.11-py3-none-any.whl", hash = "sha256:f115d2246c1ab5737772bd4845be057eebb91c0d95125a7577d92288351df7d0"}, + {file = "yt_dlp-2025.8.11.tar.gz", hash = "sha256:dc7c120a367fe55e0f711613dc80ea29d3a4e0ed8d66104cebfbe3d36e81fdfc"}, ] [[package]] name = "yt-dlp" -version = "2025.7.21" +version = "2025.8.11" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1112,15 +1112,15 @@ dependencies = [ "brotli; implementation_name == \"cpython\"", "brotlicffi; implementation_name != \"cpython\"", "certifi", - "curl-cffi!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.11,>=0.5.10; implementation_name == \"cpython\"", + "curl-cffi!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.14,>=0.5.10; implementation_name == \"cpython\"", "mutagen", "pycryptodomex", "requests<3,>=2.32.2", - "urllib3<3,>=1.26.17", + "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.7.21", + "yt-dlp==2025.8.11", ] files = [ - {file = "yt_dlp-2025.7.21-py3-none-any.whl", hash = "sha256:d7aa2b53f9b2f35453346360f41811a0dad1e956e70b35a4ae95039d4d815d15"}, - {file = "yt_dlp-2025.7.21.tar.gz", hash = "sha256:46fbb53eab1afbe184c45b4c17e9a6eba614be680e4c09de58b782629d0d7f43"}, + {file = "yt_dlp-2025.8.11-py3-none-any.whl", hash = "sha256:f115d2246c1ab5737772bd4845be057eebb91c0d95125a7577d92288351df7d0"}, + {file = "yt_dlp-2025.8.11.tar.gz", hash = "sha256:dc7c120a367fe55e0f711613dc80ea29d3a4e0ed8d66104cebfbe3d36e81fdfc"}, ] diff --git a/pyproject.toml b/pyproject.toml index 23823ae5..126497d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.7.21", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.3.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.11", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index c6a0dbcc..768e3c92 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.7.21 +yt-dlp[default,curl-cffi]==2025.8.11 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 filetype>=1.2.0 beautifulsoup4>=4.12.3 fakeredis>=2.26.2 -redis==6.3.0 +redis==6.4.0 requests>=2.32.3 tqdm>=4.67.1 token-bucket>=0.3.0 From 44f1a4b383138d3432a908163564b1d25ce28c79 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:11:07 +0530 Subject: [PATCH 290/297] Bump yt-dlp[curl-cffi,default] from 2025.8.11 to 2025.8.20 (#541) --- pdm.lock | 16 ++++++++-------- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pdm.lock b/pdm.lock index f8a76c58..22f852f4 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:abb5216ce445b61a4a487cc4d5468443847414d233c67b0414f50416489f2fb5" +content_hash = "sha256:82ff13577e18a1a872d4d35655a184e8e3fc6a120f3590f6918b465dc536de14" [[metadata.targets]] requires_python = ">3.9" @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.8.11" +version = "2025.8.20" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.8.11-py3-none-any.whl", hash = "sha256:f115d2246c1ab5737772bd4845be057eebb91c0d95125a7577d92288351df7d0"}, - {file = "yt_dlp-2025.8.11.tar.gz", hash = "sha256:dc7c120a367fe55e0f711613dc80ea29d3a4e0ed8d66104cebfbe3d36e81fdfc"}, + {file = "yt_dlp-2025.8.20-py3-none-any.whl", hash = "sha256:073c97e2a3f9cd0fa6a76142c4ef46ca62b2575c37eaf80d8c3718fd6f3277eb"}, + {file = "yt_dlp-2025.8.20.tar.gz", hash = "sha256:da873bcf424177ab5c3b701fa94ea4cdac17bf3aec5ef37b91f530c90def7bcf"}, ] [[package]] name = "yt-dlp" -version = "2025.8.11" +version = "2025.8.20" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.8.11", + "yt-dlp==2025.8.20", ] files = [ - {file = "yt_dlp-2025.8.11-py3-none-any.whl", hash = "sha256:f115d2246c1ab5737772bd4845be057eebb91c0d95125a7577d92288351df7d0"}, - {file = "yt_dlp-2025.8.11.tar.gz", hash = "sha256:dc7c120a367fe55e0f711613dc80ea29d3a4e0ed8d66104cebfbe3d36e81fdfc"}, + {file = "yt_dlp-2025.8.20-py3-none-any.whl", hash = "sha256:073c97e2a3f9cd0fa6a76142c4ef46ca62b2575c37eaf80d8c3718fd6f3277eb"}, + {file = "yt_dlp-2025.8.20.tar.gz", hash = "sha256:da873bcf424177ab5c3b701fa94ea4cdac17bf3aec5ef37b91f530c90def7bcf"}, ] diff --git a/pyproject.toml b/pyproject.toml index 126497d6..f8da3a97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.11", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.20", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 768e3c92..23d44f2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.8.11 +yt-dlp[default,curl-cffi]==2025.8.20 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From c29c5d8205e880e19707b32b36e41b76b4199eda Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Wed, 27 Aug 2025 06:16:37 +0530 Subject: [PATCH 291/297] Bump yt-dlp[curl-cffi,default] from 2025.8.20 to 2025.8.22 (#543) --- pdm.lock | 16 ++++++++-------- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pdm.lock b/pdm.lock index 22f852f4..868069b1 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:82ff13577e18a1a872d4d35655a184e8e3fc6a120f3590f6918b465dc536de14" +content_hash = "sha256:d48fa6f11cc17fdc1405b0b5c16f7aea64d07c8007275ad9d488e175f4a629ba" [[metadata.targets]] requires_python = ">3.9" @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.8.20" +version = "2025.8.22" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.8.20-py3-none-any.whl", hash = "sha256:073c97e2a3f9cd0fa6a76142c4ef46ca62b2575c37eaf80d8c3718fd6f3277eb"}, - {file = "yt_dlp-2025.8.20.tar.gz", hash = "sha256:da873bcf424177ab5c3b701fa94ea4cdac17bf3aec5ef37b91f530c90def7bcf"}, + {file = "yt_dlp-2025.8.22-py3-none-any.whl", hash = "sha256:b8c71fe4516170dea60c6e5c54e2d45654693b8dc273cad060f22199476ec979"}, + {file = "yt_dlp-2025.8.22.tar.gz", hash = "sha256:d1846bbb7edbcd2a0d4a2d76c7a2124868de9ea3b3959a8cb8219e3f7cb5c335"}, ] [[package]] name = "yt-dlp" -version = "2025.8.20" +version = "2025.8.22" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.8.20", + "yt-dlp==2025.8.22", ] files = [ - {file = "yt_dlp-2025.8.20-py3-none-any.whl", hash = "sha256:073c97e2a3f9cd0fa6a76142c4ef46ca62b2575c37eaf80d8c3718fd6f3277eb"}, - {file = "yt_dlp-2025.8.20.tar.gz", hash = "sha256:da873bcf424177ab5c3b701fa94ea4cdac17bf3aec5ef37b91f530c90def7bcf"}, + {file = "yt_dlp-2025.8.22-py3-none-any.whl", hash = "sha256:b8c71fe4516170dea60c6e5c54e2d45654693b8dc273cad060f22199476ec979"}, + {file = "yt_dlp-2025.8.22.tar.gz", hash = "sha256:d1846bbb7edbcd2a0d4a2d76c7a2124868de9ea3b3959a8cb8219e3f7cb5c335"}, ] diff --git a/pyproject.toml b/pyproject.toml index f8da3a97..5e66c055 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.20", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 23d44f2f..56f3e171 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.8.20 +yt-dlp[default,curl-cffi]==2025.8.22 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From b73784e0213f7da8dbbf5fc275998a8f2fdea0b8 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 31 Aug 2025 04:38:06 +0530 Subject: [PATCH 292/297] Bump kurigram and yt-dlp to the latest versions (#547) --- pdm.lock | 22 +++++++++++----------- pyproject.toml | 2 +- requirements.txt | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pdm.lock b/pdm.lock index 868069b1..309e8cf1 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:d48fa6f11cc17fdc1405b0b5c16f7aea64d07c8007275ad9d488e175f4a629ba" +content_hash = "sha256:0b96ce3a0e855f43924d5ce60567bb494a49f07e5e0ea12809c5d7b6a5e2c2d5" [[metadata.targets]] requires_python = ">3.9" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.7" +version = "2.2.9" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.7-py3-none-any.whl", hash = "sha256:3ebcfd4f4e7e6e9a2547997b8915ed323f679d9e99fde70a1f30f4c72aee0a97"}, - {file = "kurigram-2.2.7.tar.gz", hash = "sha256:61f3529a0cdc0a0f10f8b98103c6509317d90ebc0ae69959f38a4b684ad244f8"}, + {file = "kurigram-2.2.9-py3-none-any.whl", hash = "sha256:2e7834de8289b360362e4702747caa2ee6000ddf4cdb8780ba2057b618f7b773"}, + {file = "kurigram-2.2.9.tar.gz", hash = "sha256:2b2bc468c8953edb496115d41954d17209574913c91fd269c14cf7efb3e8138b"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.8.22" +version = "2025.8.27" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.8.22-py3-none-any.whl", hash = "sha256:b8c71fe4516170dea60c6e5c54e2d45654693b8dc273cad060f22199476ec979"}, - {file = "yt_dlp-2025.8.22.tar.gz", hash = "sha256:d1846bbb7edbcd2a0d4a2d76c7a2124868de9ea3b3959a8cb8219e3f7cb5c335"}, + {file = "yt_dlp-2025.8.27-py3-none-any.whl", hash = "sha256:0b8fd3bb7c54bc2e7ecb5cdac7d64c30e2503ea4d3dd9ae24d4f09e22aaa95f4"}, + {file = "yt_dlp-2025.8.27.tar.gz", hash = "sha256:ed74768d2a93b29933ab14099da19497ef571637f7aa375140dd3d882b9c1854"}, ] [[package]] name = "yt-dlp" -version = "2025.8.22" +version = "2025.8.27" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.8.22", + "yt-dlp==2025.8.27", ] files = [ - {file = "yt_dlp-2025.8.22-py3-none-any.whl", hash = "sha256:b8c71fe4516170dea60c6e5c54e2d45654693b8dc273cad060f22199476ec979"}, - {file = "yt_dlp-2025.8.22.tar.gz", hash = "sha256:d1846bbb7edbcd2a0d4a2d76c7a2124868de9ea3b3959a8cb8219e3f7cb5c335"}, + {file = "yt_dlp-2025.8.27-py3-none-any.whl", hash = "sha256:0b8fd3bb7c54bc2e7ecb5cdac7d64c30e2503ea4d3dd9ae24d4f09e22aaa95f4"}, + {file = "yt_dlp-2025.8.27.tar.gz", hash = "sha256:ed74768d2a93b29933ab14099da19497ef571637f7aa375140dd3d882b9c1854"}, ] diff --git a/pyproject.toml b/pyproject.toml index 5e66c055..199f69e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.7", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.27", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.9", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 56f3e171..a833722f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.8.22 +yt-dlp[default,curl-cffi]==2025.8.27 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.7 +kurigram==2.2.9 cryptography>=43.0.3 From be5334b1a35972afe74e1be96c9e12b1f4164c5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 19:37:12 +0800 Subject: [PATCH 293/297] Bump yt-dlp[curl-cffi,default] from 2025.8.27 to 2025.9.5 (#549) Bumps [yt-dlp[curl-cffi,default]](https://github.com/yt-dlp/yt-dlp) from 2025.8.27 to 2025.9.5. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.08.27...2025.09.05) --- updated-dependencies: - dependency-name: yt-dlp[curl-cffi,default] dependency-version: 2025.9.5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a833722f..8910d45a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.8.27 +yt-dlp[default,curl-cffi]==2025.9.5 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 281623609a5aa52bfbef272216be9aa9c9d7808c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:43:58 +0800 Subject: [PATCH 294/297] Bump yt-dlp[curl-cffi,default] from 2025.9.5 to 2025.9.26 (#554) Bumps [yt-dlp[curl-cffi,default]](https://github.com/yt-dlp/yt-dlp) from 2025.9.5 to 2025.9.26. - [Release notes](https://github.com/yt-dlp/yt-dlp/releases) - [Changelog](https://github.com/yt-dlp/yt-dlp/blob/master/Changelog.md) - [Commits](https://github.com/yt-dlp/yt-dlp/compare/2025.09.05...2025.09.26) --- updated-dependencies: - dependency-name: yt-dlp[curl-cffi,default] dependency-version: 2025.9.26 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8910d45a..c07ab461 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.9.5 +yt-dlp[default,curl-cffi]==2025.9.26 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 From 24b578363de8dfd0b6aca97a56dc13154f002100 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:53:11 +0530 Subject: [PATCH 295/297] Bump kurigram and yt-dlp to the latest versions (#555) --- pdm.lock | 22 +++++++++++----------- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pdm.lock b/pdm.lock index 309e8cf1..79bb5a54 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:0b96ce3a0e855f43924d5ce60567bb494a49f07e5e0ea12809c5d7b6a5e2c2d5" +content_hash = "sha256:3ecb8e13b620daf0169a2a7b8800d9a36fd92fdcc0b8c6e132371c43d8ba3e96" [[metadata.targets]] requires_python = ">3.9" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.9" +version = "2.2.12" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.9-py3-none-any.whl", hash = "sha256:2e7834de8289b360362e4702747caa2ee6000ddf4cdb8780ba2057b618f7b773"}, - {file = "kurigram-2.2.9.tar.gz", hash = "sha256:2b2bc468c8953edb496115d41954d17209574913c91fd269c14cf7efb3e8138b"}, + {file = "kurigram-2.2.12-py3-none-any.whl", hash = "sha256:f9dcedff87160cf48a911ee9b92b2e956b700c306e2bc10f008684962aed5eac"}, + {file = "kurigram-2.2.12.tar.gz", hash = "sha256:0c40b8881bda9f4a5ceed5d8fdf97811eecf0e76ea34e529d8181925450a4335"}, ] [[package]] @@ -1092,18 +1092,18 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.8.27" +version = "2025.9.26" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.8.27-py3-none-any.whl", hash = "sha256:0b8fd3bb7c54bc2e7ecb5cdac7d64c30e2503ea4d3dd9ae24d4f09e22aaa95f4"}, - {file = "yt_dlp-2025.8.27.tar.gz", hash = "sha256:ed74768d2a93b29933ab14099da19497ef571637f7aa375140dd3d882b9c1854"}, + {file = "yt_dlp-2025.9.26-py3-none-any.whl", hash = "sha256:36f5fbc153600f759abd48d257231f0e0a547a115ac7ffb05d5b64e5c7fdf8a2"}, + {file = "yt_dlp-2025.9.26.tar.gz", hash = "sha256:c148ae8233ac4ce6c5fbf6f70fcc390f13a00f59da3776d373cf88c5370bda86"}, ] [[package]] name = "yt-dlp" -version = "2025.8.27" +version = "2025.9.26" extras = ["curl-cffi", "default"] requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.8.27", + "yt-dlp==2025.9.26", ] files = [ - {file = "yt_dlp-2025.8.27-py3-none-any.whl", hash = "sha256:0b8fd3bb7c54bc2e7ecb5cdac7d64c30e2503ea4d3dd9ae24d4f09e22aaa95f4"}, - {file = "yt_dlp-2025.8.27.tar.gz", hash = "sha256:ed74768d2a93b29933ab14099da19497ef571637f7aa375140dd3d882b9c1854"}, + {file = "yt_dlp-2025.9.26-py3-none-any.whl", hash = "sha256:36f5fbc153600f759abd48d257231f0e0a547a115ac7ffb05d5b64e5c7fdf8a2"}, + {file = "yt_dlp-2025.9.26.tar.gz", hash = "sha256:c148ae8233ac4ce6c5fbf6f70fcc390f13a00f59da3776d373cf88c5370bda86"}, ] diff --git a/pyproject.toml b/pyproject.toml index 199f69e3..b57e4fdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.8.27", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.9", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.9.26", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.12", "cryptography>=43.0.3"] requires-python = ">3.9" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index c07ab461..99df4e07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.9 +kurigram==2.2.12 cryptography>=43.0.3 From 979e95bf01a598851f75873dda3ba74a21d13d36 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:31:53 +0530 Subject: [PATCH 296/297] Bump kurigram and yt-dlp to the latest versions (#558) * Bump kurigram and yt-dlp to the latest versions * Bump yt-dlp[curl-cffi,default] to 2025.10.22 --- pdm.lock | 28 ++++++++++++++-------------- pyproject.toml | 4 ++-- requirements.txt | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pdm.lock b/pdm.lock index 79bb5a54..ac09efb3 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,10 +5,10 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:3ecb8e13b620daf0169a2a7b8800d9a36fd92fdcc0b8c6e132371c43d8ba3e96" +content_hash = "sha256:8b5547b4831bb7c5b449506f131f5d842237771721abdf5db6a8e5b6db9e17cb" [[metadata.targets]] -requires_python = ">3.9" +requires_python = ">=3.10" [[package]] name = "apscheduler" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.12" +version = "2.2.13" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.12-py3-none-any.whl", hash = "sha256:f9dcedff87160cf48a911ee9b92b2e956b700c306e2bc10f008684962aed5eac"}, - {file = "kurigram-2.2.12.tar.gz", hash = "sha256:0c40b8881bda9f4a5ceed5d8fdf97811eecf0e76ea34e529d8181925450a4335"}, + {file = "kurigram-2.2.13-py3-none-any.whl", hash = "sha256:656ef2377431d6054c23cb852b3b9efed46c0349658fc7d2e2cb05834263f0a9"}, + {file = "kurigram-2.2.13.tar.gz", hash = "sha256:71c054821937d7238eee966be318d3fd9a2150a6769468e83db31933ccc2c9af"}, ] [[package]] @@ -1092,20 +1092,20 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.9.26" -requires_python = ">=3.9" +version = "2025.10.22" +requires_python = ">=3.10" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.9.26-py3-none-any.whl", hash = "sha256:36f5fbc153600f759abd48d257231f0e0a547a115ac7ffb05d5b64e5c7fdf8a2"}, - {file = "yt_dlp-2025.9.26.tar.gz", hash = "sha256:c148ae8233ac4ce6c5fbf6f70fcc390f13a00f59da3776d373cf88c5370bda86"}, + {file = "yt_dlp-2025.10.22-py3-none-any.whl", hash = "sha256:9c803a9598859f91d0d5bd3337f1506ecb40bbe97f6efbe93bc4461fed344fb2"}, + {file = "yt_dlp-2025.10.22.tar.gz", hash = "sha256:db2d48133222b1d9508c6de757859c24b5cefb9568cf68ccad85dac20b07f77b"}, ] [[package]] name = "yt-dlp" -version = "2025.9.26" +version = "2025.10.22" extras = ["curl-cffi", "default"] -requires_python = ">=3.9" +requires_python = ">=3.10" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] dependencies = [ @@ -1118,9 +1118,9 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.9.26", + "yt-dlp==2025.10.22", ] files = [ - {file = "yt_dlp-2025.9.26-py3-none-any.whl", hash = "sha256:36f5fbc153600f759abd48d257231f0e0a547a115ac7ffb05d5b64e5c7fdf8a2"}, - {file = "yt_dlp-2025.9.26.tar.gz", hash = "sha256:c148ae8233ac4ce6c5fbf6f70fcc390f13a00f59da3776d373cf88c5370bda86"}, + {file = "yt_dlp-2025.10.22-py3-none-any.whl", hash = "sha256:9c803a9598859f91d0d5bd3337f1506ecb40bbe97f6efbe93bc4461fed344fb2"}, + {file = "yt_dlp-2025.10.22.tar.gz", hash = "sha256:db2d48133222b1d9508c6de757859c24b5cefb9568cf68ccad85dac20b07f77b"}, ] diff --git a/pyproject.toml b/pyproject.toml index b57e4fdc..bf730a2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,8 +5,8 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.9.26", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.12", "cryptography>=43.0.3"] -requires-python = ">3.9" +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.10.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.13", "cryptography>=43.0.3"] +requires-python = ">=3.10" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 99df4e07..95b96286 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.9.26 +yt-dlp[default,curl-cffi]==2025.10.22 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +15,5 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.12 +kurigram==2.2.13 cryptography>=43.0.3 From f5687a4710c2ff76687acdc9885cb8f0ff8fca09 Mon Sep 17 00:00:00 2001 From: Sanuja Seneviratne <66342986+SanujaNS@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:40:02 +0530 Subject: [PATCH 297/297] Bump yt-dlp to latest with support for external JavaScript runtime (#566) * Bump yt-dlp to latest with support for external JavaScript runtime * Bump kurigram from 2.2.13 to 2.2.15 * Install deno JS runtime for yt-dlp in docker image --- Dockerfile | 2 +- README.md | 4 ++++ pdm.lock | 34 +++++++++++++++++++++++----------- pyproject.toml | 2 +- requirements.txt | 5 +++-- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index ca689d2d..d28fb5d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN pdm install FROM python:3.12-alpine AS runner WORKDIR /app -RUN apk update && apk add --no-cache ffmpeg aria2 +RUN apk update && apk add --no-cache ffmpeg aria2 deno COPY --from=pybuilder /build/.venv/lib/ /usr/local/lib/ COPY src /app WORKDIR /app diff --git a/README.md b/README.md index 56e5e3e4..82211b21 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ This bot can be deployed on any platform that supports Python. 2. Install modules using PDM: `pdm install`, or the old way use `pip install -r requirements.txt` + +> [!IMPORTANT] +> All users who intend to download from YouTube are strongly encouraged to install one of the JS runtimes (like deno) supported by yt-dlp. + 3.
Setting up config file diff --git a/pdm.lock b/pdm.lock index ac09efb3..ad00d10a 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:8b5547b4831bb7c5b449506f131f5d842237771721abdf5db6a8e5b6db9e17cb" +content_hash = "sha256:d7917c1051f4b5ff27b1dd3bf9fc03daa8a1c6178a848d93eaae8e1d92e15600" [[metadata.targets]] requires_python = ">=3.10" @@ -609,7 +609,7 @@ files = [ [[package]] name = "kurigram" -version = "2.2.13" +version = "2.2.15" requires_python = ">=3.8" summary = "Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots" groups = ["default"] @@ -618,8 +618,8 @@ dependencies = [ "pysocks<=1.7.1", ] files = [ - {file = "kurigram-2.2.13-py3-none-any.whl", hash = "sha256:656ef2377431d6054c23cb852b3b9efed46c0349658fc7d2e2cb05834263f0a9"}, - {file = "kurigram-2.2.13.tar.gz", hash = "sha256:71c054821937d7238eee966be318d3fd9a2150a6769468e83db31933ccc2c9af"}, + {file = "kurigram-2.2.15-py3-none-any.whl", hash = "sha256:9622af086988d0df783257b34e0ab423f7d33f1412b563e0d4f3df58d57d8f65"}, + {file = "kurigram-2.2.15.tar.gz", hash = "sha256:4e777ef2809d0883a318299374438a408d71ecb4ddef597b7292b7ffc332955a"}, ] [[package]] @@ -1092,18 +1092,29 @@ files = [ [[package]] name = "yt-dlp" -version = "2025.10.22" +version = "2025.11.12" requires_python = ">=3.10" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] files = [ - {file = "yt_dlp-2025.10.22-py3-none-any.whl", hash = "sha256:9c803a9598859f91d0d5bd3337f1506ecb40bbe97f6efbe93bc4461fed344fb2"}, - {file = "yt_dlp-2025.10.22.tar.gz", hash = "sha256:db2d48133222b1d9508c6de757859c24b5cefb9568cf68ccad85dac20b07f77b"}, + {file = "yt_dlp-2025.11.12-py3-none-any.whl", hash = "sha256:b47af37bbb16b08efebb36825a280ea25a507c051f93bf413a6e4a0e586c6e79"}, + {file = "yt_dlp-2025.11.12.tar.gz", hash = "sha256:5f0795a6b8fc57a5c23332d67d6c6acf819a0b46b91a6324bae29414fa97f052"}, +] + +[[package]] +name = "yt-dlp-ejs" +version = "0.3.1" +requires_python = ">=3.10" +summary = "External JavaScript for yt-dlp supporting many runtimes" +groups = ["default"] +files = [ + {file = "yt_dlp_ejs-0.3.1-py3-none-any.whl", hash = "sha256:a6e3548874db7c774388931752bb46c7f4642c044b2a189e56968f3d5ecab622"}, + {file = "yt_dlp_ejs-0.3.1.tar.gz", hash = "sha256:7f2119eb02864800f651fa33825ddfe13d152a1f730fa103d9864f091df24227"}, ] [[package]] name = "yt-dlp" -version = "2025.10.22" +version = "2025.11.12" extras = ["curl-cffi", "default"] requires_python = ">=3.10" summary = "A feature-rich command-line audio/video downloader" @@ -1118,9 +1129,10 @@ dependencies = [ "requests<3,>=2.32.2", "urllib3<3,>=2.0.2", "websockets>=13.0", - "yt-dlp==2025.10.22", + "yt-dlp-ejs==0.3.1", + "yt-dlp==2025.11.12", ] files = [ - {file = "yt_dlp-2025.10.22-py3-none-any.whl", hash = "sha256:9c803a9598859f91d0d5bd3337f1506ecb40bbe97f6efbe93bc4461fed344fb2"}, - {file = "yt_dlp-2025.10.22.tar.gz", hash = "sha256:db2d48133222b1d9508c6de757859c24b5cefb9568cf68ccad85dac20b07f77b"}, + {file = "yt_dlp-2025.11.12-py3-none-any.whl", hash = "sha256:b47af37bbb16b08efebb36825a280ea25a507c051f93bf413a6e4a0e586c6e79"}, + {file = "yt_dlp-2025.11.12.tar.gz", hash = "sha256:5f0795a6b8fc57a5c23332d67d6c6acf819a0b46b91a6324bae29414fa97f052"}, ] diff --git a/pyproject.toml b/pyproject.toml index bf730a2d..d1f14feb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Default template for PDM package" authors = [ {name = "Benny", email = "benny.think@gmail.com"}, ] -dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.10.22", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.13", "cryptography>=43.0.3"] +dependencies = ["tgcrypto>=1.2.5", "yt-dlp[curl-cffi,default]==2025.11.12", "APScheduler>=3.11.0", "ffmpeg-python>=0.2.0", "PyMySQL>=1.1.1", "filetype>=1.2.0", "beautifulsoup4>=4.12.3", "fakeredis>=2.26.2", "redis==6.4.0", "requests>=2.32.3", "tqdm>=4.67.1", "token-bucket>=0.3.0", "python-dotenv>=1.0.1", "black>=24.10.0", "sqlalchemy>=2.0.36", "psutil>=6.1.0", "ffpb>=0.4.1", "kurigram==2.2.15", "cryptography>=43.0.3", "yt-dlp-ejs>=0.3.1"] requires-python = ">=3.10" readme = "README.md" license = {text = "Apache2.0"} diff --git a/requirements.txt b/requirements.txt index 95b96286..3f711b66 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ tgcrypto>=1.2.5 -yt-dlp[default,curl-cffi]==2025.10.22 APScheduler>=3.11.0 ffmpeg-python>=0.2.0 PyMySQL>=1.1.1 @@ -15,5 +14,7 @@ black>=24.10.0 sqlalchemy>=2.0.36 psutil>=6.1.0 ffpb>=0.4.1 -kurigram==2.2.13 +kurigram==2.2.15 cryptography>=43.0.3 +yt-dlp[default,curl-cffi]==2025.11.12 +yt-dlp-ejs==0.3.1 \ No newline at end of file

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�bVQ{Z3{8%|L3KM@!*}V> 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

Deploy to heroku + +Deploy to Heroku If you are having trouble deploying, you can fork the project to your personal account and deploy it from there. **Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redisยฎ plans will no longer be available.** [Heroku Announcement](https://devcenter.heroku.com/articles/free-dyno-hours) +