From 66a37ab5cb46367c55dfb914a8bd28143a4b3bc9 Mon Sep 17 00:00:00 2001 From: smolkik-code Date: Sat, 27 Dec 2025 01:20:49 +0700 Subject: [PATCH] update v0.1 --- .DS_Store | Bin 8196 -> 10244 bytes .gitignore | 3 +- bot/__pycache__/app.cpython-314.pyc | Bin 0 -> 1627 bytes bot/__pycache__/config.cpython-314.pyc | Bin 0 -> 1031 bytes bot/__pycache__/handlers.cpython-314.pyc | Bin 0 -> 31290 bytes bot/__pycache__/logging_conf.cpython-314.pyc | Bin 0 -> 466 bytes bot/__pycache__/telegram_send.cpython-314.pyc | Bin 0 -> 3374 bytes bot/__pycache__/transcode.cpython-314.pyc | Bin 0 -> 2431 bytes bot/__pycache__/utils.cpython-314.pyc | Bin 0 -> 1777 bytes bot/__pycache__/ytdlp_client.cpython-314.pyc | Bin 0 -> 4688 bytes bot/config.py | 2 +- bot/handlers.py | 165 +++++++++++------- bot/telegram_send.py | 90 ++++++---- bot/utils.py | 4 +- bot/ytdlp_client.py | 62 +++---- requirements.txt | 4 +- 16 files changed, 186 insertions(+), 144 deletions(-) create mode 100644 bot/__pycache__/app.cpython-314.pyc create mode 100644 bot/__pycache__/config.cpython-314.pyc create mode 100644 bot/__pycache__/handlers.cpython-314.pyc create mode 100644 bot/__pycache__/logging_conf.cpython-314.pyc create mode 100644 bot/__pycache__/telegram_send.cpython-314.pyc create mode 100644 bot/__pycache__/transcode.cpython-314.pyc create mode 100644 bot/__pycache__/utils.cpython-314.pyc create mode 100644 bot/__pycache__/ytdlp_client.cpython-314.pyc diff --git a/.DS_Store b/.DS_Store index 24adf06d9ed9a28eb96c533cc23573fa60d87d54..43eff61f3f9efe57799827c9dd8b179e7d8b9b14 100644 GIT binary patch delta 638 zcmZp1XbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~50D9Q!oFar4u#RW+@`AI@F;cft;Cl|={PF^o4zu8&zHRG@d Jh{-$xW&owyhA{vD delta 128 zcmZn(XmOBWU|?W$DortDU;r^WfEYvza8E20o2aKK$^w!H@);O%81fks8B!RElgc+1 zPGg_gz`L29gN1{UW%D`l-|T|SK!rdc!3`u_K?ZCr{LVa?UnP(Os2yZ1!{m6Lsgnx@ P-8L(TzGj^KLGBs=g8~`7 diff --git a/.gitignore b/.gitignore index 97810c7..83d7beb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -cookies.txt \ No newline at end of file +cookies.txt +.venv \ No newline at end of file diff --git a/bot/__pycache__/app.cpython-314.pyc b/bot/__pycache__/app.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ef3c0c681074f6c0589e2a5c847849ba3e98ee5 GIT binary patch literal 1627 zcma)6%WoS+7@xJ*@vgmo1RTR6vI_)+k0c(^ zMeoZAe^4$L!OsyTvr;YBH=9^e%7rzik^Kb!47csS#1m6wCea1QZ;_bpf)>Y&!FyZO?hVHE zKr8ZZt`TKSAX@^%%l^KNIAa3Yk{F(Nr5F>)R+{07*Yk`CWGln)#A}u@ft(lBuP{p> z|M!h@Yz^7x9K(TRFJjVr+3SIwAr?c@58NWig>b}08br0+ETmb72|Cfr!4c%_=r$B7 z7Pd53SNqU}w7GGr?bx>7Mk+Ea-RUDp%L7YL*E$o5RtJu3(YC^ItksqI^0g+8mg?1Np|*x&j;&hbzJs*VahwoszwvM!_HDK2m?rJ75azaVSnKs% zn%ad0d?CN-l1|LKmSgT1JNY%I-M4fbx%rO%0nt9RO-JiosphvFlqc(!dbcreX@(6? zlMU!?+ygT0f+zg+r}Za7`c7=O@T+iYAk2-#r2H_Of4P z2V!m{&g{+Yn*(vt^Sd*9lmHRoC{3o%>z~y3YhSM%6z;Act`9QHL;lU5`RJJbcICU( zZ&trw`Qg&NOFv#HK3*(7C_LJDxG~6F9`cIsHq4won7%uII6ug&4Ec*b`x8HNOzukZ zqpI`?s#?oUV+rY|-i4aAq~AhbF2^zV!cD_Dj(M5{V9EbYsU)E{yaK>6N>kP7db#&9 z?j;y{DV3<*&!syq7aBNv1zWRpRXqfdb`PBQ*j3Bv^iBP3_$pCpH@I(*=_t%`+z8x! s3~v4g&W(UF0LoKv<}W_Uy>PSucO$=$$Efiad#)QkNn*vLQCG|!(rC`}`*=Ctm>l5+K zzLf9+KLOv2FKXh0KLOn)tz%5u_@Zwn{R7TgsHUczNzR#ba=vfAlR1+vryUS`_~Azp z7y$ULCw(*bE{E?Z0P0`~K#B)Qj|QH)XF!HaAPZ9cu0D#y2>^}I6d-)MA=9Z{L(qKO zHIy|>oX%%~)_!MzsQJTd&YDxq(fwZ!ZRFVJdan=A=;$+D@4@ru0Js1K>Tp2%q>hAH zM+ABt$a?Cu&c9e>%i0G_>w?V5SCC^M;tX{EhoB*K6?i>Ab!AB}g-xktRKzZki*t8j zI3bC`0++;g$fe@pC?_R2Oh*J!iiFdgl!5W*<3)tlLM-d^&rMB};|m3+gFZH=AWX-E z#bjIv$D|k!u`RDWt(KMLd`S&gnENHG1pu!T8SRT4ZKnyP{N|$g5Lpo7j4*qU6`hh1=>FVbNEy_MW(3Mv9`o0^2t*w(lNp zjW@@;=O;UlHFv1)+GE_?n_HXhi(PL_W4JnfzzjDcExyUOtDTZI64IEPb$SobyTJ1a zc-n!^wJz}N0Cs;i_*Q-+e{}863ZG|%m*^Ok(?)zdjQ>;SftGnhuy%UeyX9^0ZB?^h xsap;_XB)Ye+?2bQeXnP<(K*c%tTSI2Ps87uZ_c-uI-)l0*O-|Xv^q-E{|4o`)qVf~ literal 0 HcmV?d00001 diff --git a/bot/__pycache__/handlers.cpython-314.pyc b/bot/__pycache__/handlers.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3d61436b0f5719a9bb37ddc911ec1f0b449e5c2 GIT binary patch literal 31290 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 literal 0 HcmV?d00001 diff --git a/bot/__pycache__/logging_conf.cpython-314.pyc b/bot/__pycache__/logging_conf.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a781d96e40b1247326b43036348856aaaf42558d GIT binary patch literal 466 zcmdPq2_yN0aFmD_HIpTUvfmZeod_Ci5-! zoc#3k%)Inl+)0VWnaR%id1;yHw^%&=-27KEdJmt56p~=jJFy1zOb+`LJR=_v(#f# literal 0 HcmV?d00001 diff --git a/bot/__pycache__/telegram_send.cpython-314.pyc b/bot/__pycache__/telegram_send.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f9de0502ebc229db84b4d6489357e937f625c6d GIT binary patch literal 3374 zcmd5;O>7fK6rR~1uh)s!!6D=ao0x=9wgD{Bpwfb>Izk0h5~bVLR3eGx*h^xtz3%Rs z{z!-vs#csRCwkz(p@-(qtyHZ!b0BS`SSukR_0rrDxhQJYL*JYA+9pkDidxiJ?VC4m zes<=~``&)TN-#j6ym$BKxi@@-{Dcj!aCN0N4$3^4CK^3OPEwopqt2b;PVzS2Pc*)p zXhJv1ci4g^g6?Cws7avvneNkM&;v}Dw6;N_wy)sgDs_a3v^@v4G^SftdREU+u;Yux z4=)dI?IehKk|fjIkxvQ9f-OScqH|QElhET9jhpm#411as8g`+n$Av+%Ag7vtp(o1MNQn1!{>@NY}FhM?tifQCX`CGz zGQ3yet=$JPK>A^=B%Sa)3^uQ5WTb9AN>jAVmH8AIsdtuyB$;S<*PB7egxJ(QEh%U; z$t8J>d%3=k6L25r!D}DVdH%E*fIVnDJ|pN!DA*n(5;?8J$S`@qHIiBQUWDYheiDTd z_~59ce3azqVLnGy!QsrAcc2n4=CZosgspO^WSF*v zbcD=Y+I9ly^%R@9AetCxCVHwl!J+3YJl$R>F*AgHJ)XdGS!Y44fH$l1w(HSr(J$;} zIkF^2ZqusLbv=JAzpM-`Dnr$9XSIK@8X2zk4z0>0w6{jYP+*;ia$pt3I!^-8MLDv@ zc@0?RfgJh0b^zAD(wEpnZw)3o#P4WA@mGidYDEZuwW3heJK7Sv<;qSOEKfiO5037w z&474z=)&Q)r3JAj`^) z@d6v$3me~c&(v7}c$&W9uM%1Jdz9dh0-< zPprr^(c`ZK1k{xPm@7RL^}e>muv{6E!Sa6z-G11)6$gQ7G(gAc8k5K)u!TedYh)5w zGb(|Kn?PFw81cur#1UW$PYYm=H*CfkG~vjrz~SD+zdM9yYl4R5v1s&fn8+p}J%Qlb zP?AEOhtzq-)_BMy(gjw|&@+Q0_498wmnG^wTjeakZz7Dhh%4J+c{gM8j#!#AY@-Db zIYM5is?QNDV1kZh*(odcvEBj_m%6yq7!EcA2!^YPg5CaSpddgVNukd#eRApQxNYy1Y^IgDMTKpzglqj z=IBP=mm4-C>`w)D#v`F-{UCQ?+IlIO_la literal 0 HcmV?d00001 diff --git a/bot/__pycache__/transcode.cpython-314.pyc b/bot/__pycache__/transcode.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b7d95fa6783dde6adffc3a2e49c1f25b7fd2d36 GIT binary patch literal 2431 zcma(TOKcNIbk@7JH|rlsfB>n;lJIjNF$rl&(}-UnAr(aKKqC$Yvstg>EwHc647tC}!*Zgh?=%i2$WEuww-dIp75LxdSK`Yv{7& z(iM``(>m1_WZ*DOPf5g}2Q^#Wp-&A(H$p~AOP*EFh7M_RHl?JA5sE2eusfGdYEta@ zvCxP{LPVF+hOEVu_RNIkjYg$(S|busqEX!o+IaYk4KU@=E3}mfyrk1tEW1)aj_)MgsO> zxc9dQu5x6Uh^s=yEaoCLmEAC^Xu(eG!fwpt8Z2NB_F^CQ;{aZTYjGW}$E)9vVJb51 zYcy_{+s)uLaO-@=5vk@)m!Z z4Qv^8JQ`0CaUwg`)wwGJn3OgaPfDYPxFaqZ#2}JR0DIs-pV-maeO55P09)>uw?Nr! z;W8XNSGY)ZPnO)eCz=mzHy(odlOh~9Yu*LBC&lgUWVAh%=?btQ&%(=@w2W!nq_#xP8; z^XO?{{nYs@=V#=vTfYh22>sCdQ|SB9;{g7gD|!M``!4UBksf=Rp4M%=-ZZ=VPUFqS zrH;NrN8fh?=81^e(YM%f%G`C@Y>F<{oiV*NKuHx!{RPZ?6Uhf|kibZda0A z))naWv0x1ZXk3NbvTFN*Hl&e$n*O%`mgk&G5>X=?kE`RB(BFS7e0U&waBv8oBBEPv z$(TsXs%EVkRS0!1t)@q#NlnXG0oX;=bW|BvVq+lPLLrVa>&*Zf zw6kv&MR9*ArValQO+5sB_S5tu0I#3J^YWq&c>_~DmwTpquk=3kH<`^J&bQ33F~xn0 z{{1Gu|C!*M3SAD(1Q&(P`M%%yx+3p|-!*x<1(g}5TWdOBX@>!t^b zeEoEU;+%AHJ>@;0_g&>*toM0b`M$q4vQCNlQ$XJOB~Q!Go|Z+=mi(cj;9n9p7KDv6 z>ICHM!Pj9{-AQ8_dT3MgM@w4-`HA{KqeAkaNf122HccgY|ugd9;=}xR-m> zu%5ylewc&24mE`=cLx4jKQ`TK5F`o}k)izWS-imB`HYMPJL3FJ$^c*T?h1D-K?J GTiZX2v3kw` literal 0 HcmV?d00001 diff --git a/bot/__pycache__/utils.cpython-314.pyc b/bot/__pycache__/utils.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a6856d9584c3b61c0f3fa5588cc9d3a4bdce2b8 GIT binary patch literal 1777 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 O<7B)mL5K@hFaHG=$n+Ti literal 0 HcmV?d00001 diff --git a/bot/config.py b/bot/config.py index a11e3da..6caca0f 100644 --- a/bot/config.py +++ b/bot/config.py @@ -8,7 +8,7 @@ DOWNLOAD_DIR = Path(os.getenv("DOWNLOAD_DIR", "downloads")) DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True) COOKIES_FILE = Path(os.getenv("COOKIES_FILE", "")) if os.getenv("COOKIES_FILE") else Path() -BROWSER = os.getenv("BROWSER", "") +BROWSER = os.getenv("BROWSER", "chrome") CONNECT_TIMEOUT = int(os.getenv("CONNECT_TIMEOUT", 600)) READ_TIMEOUT = int(os.getenv("READ_TIMEOUT", 1800)) diff --git a/bot/handlers.py b/bot/handlers.py index cd392d5..47012dc 100644 --- a/bot/handlers.py +++ b/bot/handlers.py @@ -1,3 +1,4 @@ +from typing import Optional from pathlib import Path import httpx from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup @@ -125,8 +126,10 @@ class Handlers: else: await message.edit_text("ℹ️ Stories не найдены или доступ ограничен.") except Exception as e: - await message.edit_text(f"⚠️ Ошибка Stories: {e}") - + try: + await message.edit_text(f"⚠️ Ошибка Stories: {str(e)[:100]}") + except: + await message.reply_text(f"⚠️ Ошибка Stories: {str(e)[:100]}") # -------- Stories: все активные у автора -------- async def download_all_stories(self, url: str, message, chat_id: int): try: @@ -251,7 +254,7 @@ class Handlers: finally: self.sending_active[chat_id] = False - async def _send_audio_safe(self, message, filepath: Path, title: str | None, chat_id: int): + async def _send_audio_safe(self, message, filepath: Path, title: Optional[str], chat_id: int): self.sending_active[chat_id] = True try: await send_audio(message, filepath, title, CONNECT_TIMEOUT, READ_TIMEOUT) @@ -273,29 +276,35 @@ class Handlers: self.sending_active[chat_id] = False async def _download_file(self, url: str, into: Path) -> Path: - into.parent.mkdir(parents=True, exist_ok=True) - timeout = httpx.Timeout(30.0) - async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client: - r = await client.get(url) - r.raise_for_status() - ctype = r.headers.get('content-type', '') - ext = '' - if 'image/jpeg' in ctype: - ext = '.jpg' - elif 'image/png' in ctype: - ext = '.png' - elif 'image/webp' in ctype: - ext = '.webp' - elif 'image/avif' in ctype: - ext = '.avif' - elif 'image/heic' in ctype or 'image/heif' in ctype: - ext = '.heic' - if not into.suffix: - into = into.with_suffix(ext or '.jpg') - into.write_bytes(r.content) + print(f"[DBG] _download_file: url={url}, into={into}") # DBG + into.parent.mkdir(parents=True, exist_ok=True) + timeout = httpx.Timeout(30.0) + async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client: + r = await client.get(url) + r.raise_for_status() + ctype = r.headers.get('content-type', '') + print(f"[DBG] _download_file: content-type={ctype}") # DBG + + ext = '' + if 'image/jpeg' in ctype: + ext = '.jpg' + elif 'image/png' in ctype: + ext = '.png' + elif 'image/webp' in ctype: + ext = '.webp' + elif 'image/avif' in ctype: + ext = '.avif' + elif 'image/heic' in ctype or 'image/heif' in ctype: + ext = '.heic' + + if not into.suffix: + into = into.with_suffix(ext or '.jpg') + + into.write_bytes(r.content) + print(f"[DBG] _download_file: saved {into}") # DBG return into - def _pick_path(self, entry) -> Path | None: + def _pick_path(self, entry) -> Optional[Path]: if isinstance(entry, dict): rd = entry.get('requested_downloads') or [] for it in rd[::-1]: @@ -318,7 +327,7 @@ class Handlers: sent = 0 for idx, entry in enumerate(entries): fp = self._pick_path(entry) - temp_file: Path | None = None + temp_file: Optional[Path] = None try: if fp and fp.exists(): suffix = (fp.suffix or '').lower() @@ -350,47 +359,69 @@ class Handlers: return sent async def _send_playlist_mixed_with_images(self, info: dict | None, message, chat_id: int) -> int: - if not isinstance(info, dict): - return 0 - entries = info.get('entries') or [] - sent = 0 - for idx, entry in enumerate(entries): - fp = self._pick_path(entry) - temp_file: Path | None = None - try: - if fp and fp.exists(): - suffix = (fp.suffix or '').lower() - if suffix in IMAGE_SUFFIXES: - await self._send_photo_safe(message, fp, chat_id) - sent += 1 - elif suffix in VIDEO_SUFFIXES: - out_mp4 = await transcode_to_mobile_mp4(fp) - try: - await self._send_video_safe(message, out_mp4, chat_id) - sent += 1 - finally: - out_mp4.unlink(missing_ok=True) - else: - await self._send_document_safe(message, fp, chat_id) + if not isinstance(info, dict): + print("[DBG] _send_playlist_mixed_with_images: info is not dict") # DBG + return 0 + + entries = info.get('entries') or [] + print(f"[DBG] playlist entries={len(entries)}") # DBG + + sent = 0 + for idx, entry in enumerate(entries): + print(f"[DBG] entry[{idx}] keys={list(entry.keys()) if isinstance(entry, dict) else type(entry)}") # DBG + fp = self._pick_path(entry) + temp_file: Path | None = None + + try: + if fp and fp.exists(): + suffix = (fp.suffix or '').lower() + print(f"[DBG] entry[{idx}] local file={fp}, suffix={suffix}") # DBG + + if suffix in IMAGE_SUFFIXES: + print(f"[DBG] entry[{idx}] send as photo") # DBG + await self._send_photo_safe(message, fp, chat_id) + sent += 1 + elif suffix in VIDEO_SUFFIXES: + print(f"[DBG] entry[{idx}] transcode/send video") # DBG + out_mp4 = await transcode_to_mobile_mp4(fp) + try: + await self._send_video_safe(message, out_mp4, chat_id) sent += 1 + finally: + out_mp4.unlink(missing_ok=True) else: - image_url = None - thumbs = entry.get('thumbnails') if isinstance(entry, dict) else None - if isinstance(thumbs, list) and thumbs: - urls = [t.get('url') for t in thumbs if isinstance(t, dict) and t.get('url')] - if urls: - image_url = max(urls, key=len) - if not image_url and isinstance(entry, dict): - cand = entry.get('url') or entry.get('thumbnail') - if isinstance(cand, str) and cand.startswith(('http://', 'https://')): - image_url = cand - if image_url: - temp_file = await self._download_file(image_url, DOWNLOAD_DIR / f"story_img_{idx}") - await self._send_photo_safe(message, temp_file, chat_id) - sent += 1 - finally: - if temp_file: - temp_file.unlink(missing_ok=True) - if fp: - fp.unlink(missing_ok=True) - return sent + print(f"[DBG] entry[{idx}] send as document") # DBG + await self._send_document_safe(message, fp, chat_id) + sent += 1 + else: + print(f"[DBG] entry[{idx}] no local file, try thumbnails/url") # DBG + image_url = None + thumbs = entry.get('thumbnails') if isinstance(entry, dict) else None + + if isinstance(thumbs, list) and thumbs: + urls = [t.get('url') for t in thumbs if isinstance(t, dict) and t.get('url')] + if urls: + image_url = max(urls, key=len) + print(f"[DBG] entry[{idx}] thumbnail url={image_url}") # DBG + + if not image_url and isinstance(entry, dict): + cand = entry.get('url') or entry.get('thumbnail') + if isinstance(cand, str) and cand.startswith(('http://', 'https://')): + image_url = cand + print(f"[DBG] entry[{idx}] direct url={image_url}") # DBG + + if image_url: + temp_file = await self._download_file(image_url, DOWNLOAD_DIR / f"story_img_{idx}") + await self._send_photo_safe(message, temp_file, chat_id) + sent += 1 + else: + print(f"[DBG] entry[{idx}] no image url found") # DBG + + finally: + if temp_file: + temp_file.unlink(missing_ok=True) + if fp: + fp.unlink(missing_ok=True) + + print(f"[DBG] _send_playlist_mixed_with_images: sent={sent}") # DBG + return sent diff --git a/bot/telegram_send.py b/bot/telegram_send.py index b581fe5..0b32b44 100644 --- a/bot/telegram_send.py +++ b/bot/telegram_send.py @@ -1,41 +1,59 @@ +import asyncio from pathlib import Path +from typing import Optional from telegram import Message +from telegram.constants import ParseMode +from telegram.error import TelegramError +import logging -async def send_video(message: Message, filepath: Path, connect_timeout: int, read_timeout: int): - with filepath.open('rb') as f: - await message.chat.send_video( - video=f, - supports_streaming=True, - connect_timeout=connect_timeout, - read_timeout=read_timeout - ) +logger = logging.getLogger(__name__) -async def send_audio(message: Message, filepath: Path, title: str | None, connect_timeout: int, read_timeout: int): - with filepath.open('rb') as f: - await message.chat.send_audio( - audio=f, - title=title, - connect_timeout=connect_timeout, - read_timeout=read_timeout - ) - -async def send_document(message: Message, filepath: Path, connect_timeout: int, read_timeout: int): - with filepath.open('rb') as f: - await message.chat.send_document( - document=f, - filename=filepath.name, - connect_timeout=connect_timeout, - read_timeout=read_timeout - ) - -async def send_photo(message: Message, filepath: Path, connect_timeout: int, read_timeout: int): - # Фото до ~20MB лучше отправлять как фото, большие — как документ - if filepath.stat().st_size <= 20 * 1024 * 1024: - with filepath.open('rb') as f: - await message.chat.send_photo( - photo=f, - connect_timeout=connect_timeout, - read_timeout=read_timeout +async def send_video(message: Message, filepath: Path, title: Optional[str] = None, *args, **kwargs): + """Универсальная функция - принимает любые аргументы""" + try: + with open(filepath, 'rb') as video_file: + await message.reply_video( + video=video_file, + caption=title or "Видео", + supports_streaming=True, + parse_mode=ParseMode.HTML, + **kwargs # ✅ принимает любые доп. аргументы ) - else: - await send_document(message, filepath, connect_timeout, read_timeout) + except TelegramError as e: + logger.error(f"Ошибка отправки видео: {e}") + await message.reply_text("❌ Ошибка при отправке видео") + +async def send_audio(message: Message, filepath: Path, title: Optional[str] = None, *args, **kwargs): + try: + with open(filepath, 'rb') as audio_file: + await message.reply_audio( + audio=audio_file, + title=title or "Аудио", + parse_mode=ParseMode.HTML, + **kwargs + ) + except TelegramError as e: + logger.error(f"Ошибка отправки аудио: {e}") + await message.reply_text("❌ Ошибка при отправке аудио") + +async def send_document(message: Message, filepath: Path, *args, **kwargs): + try: + with open(filepath, 'rb') as doc_file: + await message.reply_document( + document=doc_file, + **kwargs + ) + except TelegramError as e: + logger.error(f"Ошибка отправки документа: {e}") + await message.reply_text("❌ Ошибка при отправке документа") + +async def send_photo(message: Message, filepath: Path, *args, **kwargs): + try: + with open(filepath, 'rb') as photo_file: + await message.reply_photo( + photo=photo_file, + **kwargs + ) + except TelegramError as e: + logger.error(f"Ошибка отправки фото: {e}") + await message.reply_text("❌ Ошибка при отправке фото") diff --git a/bot/utils.py b/bot/utils.py index d1c027d..994aff3 100644 --- a/bot/utils.py +++ b/bot/utils.py @@ -4,7 +4,9 @@ def is_instagram_story_url(url: str) -> bool: VIDEO_SUFFIXES = ('.mp4', '.mov', '.mkv') IMAGE_SUFFIXES = ('.jpg', '.jpeg', '.png', '.webp') -def extract_instagram_username(url: str) -> str | None: +from typing import Optional + +def extract_instagram_username(url: Optional[str]) -> Optional[str]: # Поддержка: # https://www.instagram.com/stories/USERNAME/... # https://www.instagram.com/USERNAME/ diff --git a/bot/ytdlp_client.py b/bot/ytdlp_client.py index 59cad1e..cb70989 100644 --- a/bot/ytdlp_client.py +++ b/bot/ytdlp_client.py @@ -1,18 +1,18 @@ import asyncio from functools import partial from pathlib import Path +from typing import Optional import os import yt_dlp -def build_opts(for_video: bool, outdir: Path, cookiefile: Path | None, browser: str | None, message=None, out_for_playlist: bool = True) -> dict: + +def build_opts(for_video: bool, outdir: Path, cookiefile: Optional[Path], browser: Optional[str], message=None, out_for_playlist: bool = True) -> dict: postprocessors = [] if for_video: - # На этапе постпроцессора делаем простую конвертацию в mp4 (дальше будет наша принудительная перекодировка) postprocessors.append({'key': 'FFmpegVideoConvertor', 'preferedformat': 'mp4'}) - - # Для плейлистов (stories/галереи) — уникальные имена, чтобы не затирать + outtmpl = '%(uploader)s_%(id)s.%(ext)s' if out_for_playlist else '%(title)s.%(ext)s' - + opts: dict = { 'outtmpl': str(outdir / outtmpl), 'quiet': True, @@ -21,53 +21,43 @@ def build_opts(for_video: bool, outdir: Path, cookiefile: Path | None, browser: 'retries': 5, 'fragment_retries': 5, 'concurrent_fragment_downloads': 5, - 'noprogress': True, - 'source_address': '0.0.0.0', # форс IPv4 + 'noprogress': True, # ✅ Отключаем прогресс-бар + 'source_address': '0.0.0.0', } - + proxy = os.getenv('HTTPS_PROXY') or os.getenv('HTTP_PROXY') or '' if proxy: opts['proxy'] = proxy - + if cookiefile and cookiefile.exists(): opts['cookiefile'] = str(cookiefile) elif browser: opts['cookiesfrombrowser'] = (browser,) - - if message: - opts['progress_hooks'] = [partial(progress_hook_async_bridge, message=message)] - + + # ✅ УБРАЛИ progress_hooks - они ломают event loop + return opts -async def progress_hook_async_bridge(d, message): - try: - if d.get('status') == 'downloading': - percent = d.get('_percent_str', '').strip() - speed = d.get('_speed_str', '').strip() - eta = d.get('_eta_str', '').strip() - text = f"⬇️ Загрузка: {percent or '?'}\n🚀 Скорость: {speed or '?'}\n⏱ Осталось: {eta or '?'}" - await message.edit_text(text) - except Exception: - pass -async def run(url: str, opts: dict) -> tuple[Path | None, dict | None]: +async def run(url: str, opts: dict) -> tuple[Optional[Path], Optional[dict]]: loop = asyncio.get_running_loop() info, filepath = None, None - + def _do(): nonlocal info, filepath with yt_dlp.YoutubeDL(opts) as ydl: info = ydl.extract_info(url, download=True) - out = None - if isinstance(info, dict): - out = info.get('_filename') - if not out: - rd = info.get('requested_downloads') or [] - for it in rd[::-1]: - out = it.get('filepath') or it.get('filename') - if out: - break - filepath = Path(out) if out else None - + + out = None + if isinstance(info, dict): + out = info.get('_filename') + if not out: + rd = info.get('requested_downloads') or [] + for it in rd[::-1]: + out = it.get('filepath') or it.get('filename') + if out: + break + filepath = Path(out) if out else None + await loop.run_in_executor(None, _do) return filepath, info diff --git a/requirements.txt b/requirements.txt index 1f79462..ebea43f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -python-telegram-bot==20.7 +python-telegram-bot>=22.0 yt-dlp>=2025.1.1 -httpx>=0.25.2 +httpx>=0.27 instaloader>=4.13.1