update_0.2 #1

Merged
smolkik_adm merged 6 commits from update_0.2 into main 2026-02-19 03:22:57 +00:00
2 changed files with 205 additions and 124 deletions
Showing only changes of commit 5b0152e47c - Show all commits
+192 -88
View File
@@ -3,8 +3,8 @@ import yt_dlp
import threading import threading
import os import os
import logging import logging
import shutil
import subprocess import subprocess
import shutil
from typing import List from typing import List
from config import ( from config import (
@@ -26,7 +26,134 @@ def _progress_hook(cancel_event, progress_cb):
progress_cb(d) progress_cb(d)
return hook return hook
# ---------------- TIKTOK: Видео + Аудио (автоматически) -------------- def optimize_for_telegram(input_path: str, output_path: str) -> None:
"""
Конвертирует видео в Telegram-safe формат:
- mp4 + h264 + aac
- четные размеры
- crf: 23 (или 28 >50MB)
- faststart
"""
file_size_mb = os.path.getsize(input_path) / (1024 * 1024)
crf = 28 if file_size_mb > 50 else 23
cmd = [
'ffmpeg',
'-i', input_path,
'-c:v', 'libx264',
'-preset', 'fast',
'-crf', str(crf),
'-c:a', 'aac',
'-b:a', '128k',
'-movflags', '+faststart',
'-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
'-y',
output_path
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
if result.returncode != 0:
logger.error(f"FFmpeg error: {result.stderr}")
shutil.copy2(input_path, output_path)
# ---------------- YOUTUBE/VK: Video (с выбором качества) ------
def download_video(
url: str,
quality: str,
out_path: str,
cookies: str | None,
cancel_event: threading.Event,
progress_cb,
):
# 1. Скачиваем в любой формат
ydl_opts = {
"format": f"best[height<={quality}]/best",
"outtmpl": out_path, # ← без .mp4!
"cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"quiet": True,
"no_warnings": True,
"concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# 2. Ищем реальный файл (без расширения, .mp4, .mov, .webm)
base = out_path.rsplit('.', 1)[0]
actual_video = None
for ext in ['', '.mp4', '.mov', '.webm', '.mkv']:
candidate = base + ext
if os.path.exists(candidate):
actual_video = candidate
break
if not actual_video:
raise Exception(f"Video file not created for {url}")
# 3. Конвертируем в Telegram-safe mp4
optimized_path = base + "_telegram.mp4"
await asyncio.to_thread(optimize_for_telegram, actual_video, optimized_path)
# 4. Удаляем оригинальный файл и ставим конвертированный
for path in [out_path, actual_video]:
if os.path.exists(path): os.remove(path)
os.rename(optimized_path, out_path)
# ---------------- AUDIO (YouTube/VK) ------
def download_audio(
url: str,
out_path: str,
cookies: str | None,
cancel_event: threading.Event,
progress_cb,
):
ydl_opts = {
"format": "bestaudio/best",
"outtmpl": out_path.replace('.mp3', ''), # ← без .mp3!
"cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"postprocessors": [{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
"preferredquality": "192",
}],
"quiet": True,
"no_warnings": True,
"concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# Ищем файл (может быть .m4a, .opus, .weba)
base = out_path.rsplit('.', 1)[0]
actual_audio = None
for ext in ['', '.mp3', '.m4a', '.opus', '.weba']:
candidate = base + ext
if os.path.exists(candidate):
actual_audio = candidate
break
if not actual_audio:
raise Exception(f"Audio file not created for {url}")
# Конвертируем в mp3 (если нужно)
if actual_audio != out_path:
optimized = base + "_telegram.mp3"
subprocess.run([
'ffmpeg', '-i', actual_audio, '-c:a', 'aac', '-b:a', '192k',
'-loglevel', 'error', '-y', optimized
])
if os.path.exists(out_path): os.remove(out_path)
os.rename(optimized, out_path)
# ---------------- TIKTOK: Video + Audio (автоматически) ------
def download_tiktok_video_and_audio( def download_tiktok_video_and_audio(
url: str, url: str,
video_path: str, video_path: str,
@@ -35,15 +162,14 @@ def download_tiktok_video_and_audio(
cancel_event: threading.Event, cancel_event: threading.Event,
progress_cb, progress_cb,
): ):
# 1. Скачиваем видео (1080p) — УБРАН skip_impersonation # 1. Скачиваем видео
video_opts = { video_opts = {
"format": f"bestvideo[height<={DEFAULT_TIKTOK_VIDEO_QUALITY}]+bestaudio/best", "format": f"bestvideo[height<={DEFAULT_TIKTOK_VIDEO_QUALITY}]+bestaudio/best",
"outtmpl": video_path.replace('.mp4', ''), "outtmpl": video_path, # ← без .mp4!
"merge_output_format": "mp4",
"cookiefile": cookies if cookies and os.path.exists(cookies) else None, "cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)], "progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"quiet": True, "quiet": True,
"no_warnings": False, # ← ВКЛЮЧИЛ предупреждения для отладки "no_warnings": False,
"concurrent_fragment_downloads": 4, "concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760, "http_chunk_size": 10485760,
} }
@@ -51,10 +177,24 @@ def download_tiktok_video_and_audio(
with yt_dlp.YoutubeDL(video_opts) as ydl: with yt_dlp.YoutubeDL(video_opts) as ydl:
ydl.download([url]) ydl.download([url])
if not os.path.exists(video_path): base_video = video_path.rsplit('.', 1)[0]
actual_video = None
for ext in ['', '.mp4', '.mov', '.webm']:
candidate = base_video + ext
if os.path.exists(candidate):
actual_video = candidate
break
if not actual_video:
raise Exception("TikTok video file not created") raise Exception("TikTok video file not created")
# 2. Извлекаем аудио (MP3 192kbps) — УБРАН skip_impersonation # Конвертируем в Telegram-safe
optimized_video = base_video + "_telegram.mp4"
await asyncio.to_thread(optimize_for_telegram, actual_video, optimized_video)
for path in [video_path, actual_video]:
if os.path.exists(path): os.remove(path)
os.rename(optimized_video, video_path)
# 2. Аудио
audio_opts = { audio_opts = {
"format": "bestaudio/best", "format": "bestaudio/best",
"outtmpl": audio_path.replace('.mp3', ''), "outtmpl": audio_path.replace('.mp3', ''),
@@ -65,7 +205,7 @@ def download_tiktok_video_and_audio(
"preferredquality": DEFAULT_TIKTOK_AUDIO_QUALITY, "preferredquality": DEFAULT_TIKTOK_AUDIO_QUALITY,
}], }],
"quiet": True, "quiet": True,
"no_warnings": False, # ← ВКЛЮЧИЛ для отладки "no_warnings": False,
"concurrent_fragment_downloads": 4, "concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760, "http_chunk_size": 10485760,
} }
@@ -73,10 +213,27 @@ def download_tiktok_video_and_audio(
with yt_dlp.YoutubeDL(audio_opts) as ydl: with yt_dlp.YoutubeDL(audio_opts) as ydl:
ydl.download([url]) ydl.download([url])
if not os.path.exists(audio_path): base_audio = audio_path.rsplit('.', 1)[0]
actual_audio = None
for ext in ['', '.mp3', '.m4a', '.opus']:
candidate = base_audio + ext
if os.path.exists(candidate):
actual_audio = candidate
break
if not actual_audio:
raise Exception("TikTok audio file not created") raise Exception("TikTok audio file not created")
# ---------------- INSTAGRAM: Видео в 1080p (автоматически) -------------- if actual_audio != audio_path:
optimized_audio = base_audio + "_telegram.mp3"
subprocess.run([
'ffmpeg', '-i', actual_audio, '-c:a', 'aac', '-b:a', '192k',
'-loglevel', 'error', '-y', optimized_audio
])
if os.path.exists(audio_path): os.remove(audio_path)
os.rename(optimized_audio, audio_path)
# ---------------- INSTAGRAM: Video (1080p) ------
def download_instagram_video( def download_instagram_video(
url: str, url: str,
out_path: str, out_path: str,
@@ -84,68 +241,37 @@ def download_instagram_video(
cancel_event: threading.Event, cancel_event: threading.Event,
progress_cb, progress_cb,
): ):
# УБРАН skip_impersonation
ydl_opts = { ydl_opts = {
"format": f"bestvideo[height<={DEFAULT_INSTAGRAM_VIDEO_QUALITY}]+bestaudio/best", "format": f"bestvideo[height<={DEFAULT_INSTAGRAM_VIDEO_QUALITY}]+bestaudio/best",
"outtmpl": out_path.replace('.mp4', ''),
"merge_output_format": "mp4",
"cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"quiet": True,
"no_warnings": False, # ← ВКЛЮЧИЛ для отладки
"concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# ---------------- AUDIO (YouTube/VK) --------------
def download_audio(
url: str,
out_path: str,
cookies: str | None,
cancel_event: threading.Event,
progress_cb,
):
ydl_opts = {
"format": "bestaudio/best",
"outtmpl": out_path.replace('.mp3', ''),
"cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"postprocessors": [{"key": "FFmpegExtractAudio", "preferredcodec": "mp3", "preferredquality": "192"}],
"quiet": True,
"no_warnings": True,
"concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# ---------------- VIDEO (YouTube/VK — с выбором качества) --------------
def download_video(
url: str,
quality: str,
out_path: str,
cookies: str | None,
cancel_event: threading.Event,
progress_cb,
):
ydl_opts = {
"format": f"best[height<={quality}]/best",
"outtmpl": out_path, "outtmpl": out_path,
"merge_output_format": "mp4",
"cookiefile": cookies if cookies and os.path.exists(cookies) else None, "cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)], "progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"quiet": True, "quiet": True,
"no_warnings": True, "no_warnings": False,
"concurrent_fragment_downloads": 4, "concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760, "http_chunk_size": 10485760,
} }
with yt_dlp.YoutubeDL(ydl_opts) as ydl: with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url]) ydl.download([url])
# ---------------- PLAYLIST (осталось без изменений) -------------- base = out_path.rsplit('.', 1)[0]
actual = None
for ext in ['', '.mp4', '.mov', '.webm']:
candidate = base + ext
if os.path.exists(candidate):
actual = candidate
break
if not actual:
raise Exception("Instagram video not created")
optimized = base + "_telegram.mp4"
await asyncio.to_thread(optimize_for_telegram, actual, optimized)
if os.path.exists(out_path): os.remove(out_path)
os.rename(optimized, out_path)
# ---------------- PLAYLIST ------
def download_playlist_videos( def download_playlist_videos(
playlist_info: dict, playlist_info: dict,
output_dir: str, output_dir: str,
@@ -157,7 +283,6 @@ def download_playlist_videos(
ydl_opts = { ydl_opts = {
"format": "best[height<=1080]/best", "format": "best[height<=1080]/best",
"outtmpl": os.path.join(output_dir, "%(title)s [%(id)s].%(ext)s"), "outtmpl": os.path.join(output_dir, "%(title)s [%(id)s].%(ext)s"),
"merge_output_format": "mp4",
"cookiefile": cookies if cookies and os.path.exists(cookies) else None, "cookiefile": cookies if cookies and os.path.exists(cookies) else None,
"progress_hooks": [_progress_hook(cancel_event, progress_cb)], "progress_hooks": [_progress_hook(cancel_event, progress_cb)],
"quiet": True, "quiet": True,
@@ -168,45 +293,24 @@ def download_playlist_videos(
"concurrent_fragment_downloads": 4, "concurrent_fragment_downloads": 4,
"http_chunk_size": 10485760, "http_chunk_size": 10485760,
} }
try: try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl: with yt_dlp.YoutubeDL(ydl_opts) as ydl:
entries = playlist_info.get('entries', []) entries = playlist_info.get('entries', [])
total = len(entries)
logger.info(f"Downloading playlist: {total} videos")
for i, entry in enumerate(entries, 1): for i, entry in enumerate(entries, 1):
if cancel_event.is_set(): if cancel_event.is_set():
raise DownloadCancelled("Cancelled by user") raise DownloadCancelled("Cancelled by user")
if not entry.get('url'): if not entry.get('url'):
logger.warning(f"Entry {i} has no URL")
continue continue
try: try:
info = ydl.extract_info(entry['url'], download=True) info = ydl.extract_info(entry['url'], download=True)
if info and os.path.exists(ydl.prepare_filename(info)): if info:
downloaded_files.append(ydl.prepare_filename(info)) filename = ydl.prepare_filename(info)
if os.path.exists(filename):
downloaded_files.append(filename)
except Exception as e: except Exception as e:
logger.error(f"Error downloading video {i}: {e}") logger.error(f"Error downloading video {i}: {e}")
continue
return downloaded_files return downloaded_files
except Exception as e: except Exception as e:
logger.error(f"Playlist download error: {e}") logger.error(f"Playlist download error: {e}")
raise raise
# ---------------- METADATA (осталось без изменений) --------------
def add_metadata_to_audio(input_path: str, output_path: str, metadata: dict):
if metadata is None:
metadata = {}
if not input_path.lower().endswith('.mp3'):
shutil.copy2(input_path, output_path)
return
metadata_args = []
for key in ['title', 'artist', 'album']:
if metadata.get(key):
metadata_args.extend(['-metadata', f'{key}={str(metadata[key])[:100]}'])
cmd = [
'ffmpeg', '-i', input_path, '-c', 'copy',
'-id3v2_version', '3', '-loglevel', 'error', '-y',
*metadata_args, output_path
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
shutil.copy2(input_path, output_path)
+12 -35
View File
@@ -1,4 +1,4 @@
# main.py — ИСПРАВЛЕНО: добавлен import cache_key # main.py
import os import os
import asyncio import asyncio
import threading import threading
@@ -17,8 +17,6 @@ from config import (
CACHE_DIR, CACHE_DIR,
TMP_DIR, TMP_DIR,
COOKIES_FILE, COOKIES_FILE,
CACHE_MAX_AGE_DAYS,
CACHE_MAX_SIZE_MB,
) )
from keyboards import youtube_quality_keyboard, cancel_keyboard, playlist_keyboard from keyboards import youtube_quality_keyboard, cancel_keyboard, playlist_keyboard
from downloader import ( from downloader import (
@@ -27,18 +25,17 @@ from downloader import (
download_video, download_video,
download_audio, download_audio,
download_playlist_videos, download_playlist_videos,
optimize_for_telegram,
DownloadCancelled, DownloadCancelled,
) )
from middleware import PrivateMiddleware from middleware import PrivateMiddleware
from rate_limit import check_rate_limit
from info import extract_info, is_playlist, get_platform_info from info import extract_info, is_playlist, get_platform_info
from cache import cache_key, cache_path # ← ИСПРАВЛЕНО: добавлен cache_key from cache import cache_key, cache_path
from cleanup import cleanup_tmp from cleanup import cleanup_tmp
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Инициализация
if LOCAL_API_URL: if LOCAL_API_URL:
api_server = TelegramAPIServer.from_base(LOCAL_API_URL) api_server = TelegramAPIServer.from_base(LOCAL_API_URL)
session = AiohttpSession(api=api_server) session = AiohttpSession(api=api_server)
@@ -87,8 +84,7 @@ def make_progress_cb(loop, message):
asyncio.run_coroutine_threadsafe(update(d), loop) asyncio.run_coroutine_threadsafe(update(d), loop)
return cb return cb
# ---------------------- AUTO DOWNLOADS (TikTok/Instagram) ------------------ # ---------------- TikTok + Instagram (автозагрузка) ----------------
async def process_tiktok_auto(message: Message, user_id: int, url: str): async def process_tiktok_auto(message: Message, user_id: int, url: str):
status = await message.answer("🎵 <b>Загружаю TikTok (видео + аудио)...</b>", parse_mode="HTML") status = await message.answer("🎵 <b>Загружаю TikTok (видео + аудио)...</b>", parse_mode="HTML")
@@ -103,7 +99,6 @@ async def process_tiktok_auto(message: Message, user_id: int, url: str):
tmp_audio = cache_path(TMP_DIR, key_audio, "mp3") tmp_audio = cache_path(TMP_DIR, key_audio, "mp3")
os.makedirs(os.path.dirname(tmp_audio), exist_ok=True) os.makedirs(os.path.dirname(tmp_audio), exist_ok=True)
# Проверка кэша
if os.path.exists(video_cache) and os.path.exists(audio_cache): if os.path.exists(video_cache) and os.path.exists(audio_cache):
await status.edit_text("📤 <b>Отправляю из кэша...</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю из кэша...</b>", parse_mode="HTML")
try: try:
@@ -118,7 +113,6 @@ async def process_tiktok_auto(message: Message, user_id: int, url: str):
cleanup_tmp(TMP_DIR) cleanup_tmp(TMP_DIR)
return return
# Загрузка
cancel_event = threading.Event() cancel_event = threading.Event()
ACTIVE_DOWNLOADS[user_id] = {"cancel": cancel_event} ACTIVE_DOWNLOADS[user_id] = {"cancel": cancel_event}
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
@@ -134,19 +128,16 @@ async def process_tiktok_auto(message: Message, user_id: int, url: str):
cancel_event, cancel_event,
progress_cb, progress_cb,
) )
if cancel_event.is_set(): if cancel_event.is_set():
await status.edit_text("⛔ Загрузка отменена") await status.edit_text("⛔ Загрузка отменена")
for p in [tmp_video, tmp_audio]: for p in [tmp_video, tmp_audio]:
if os.path.exists(p): os.remove(p) if os.path.exists(p): os.remove(p)
return return
# Перемещение в кэш # Уже готовы Telegram-safe mp4/m3, просто перемещаем
for path in [video_cache, audio_cache]: if os.path.exists(video_cache): os.remove(video_cache)
if os.path.exists(path): os.remove(path)
os.makedirs(os.path.dirname(video_cache), exist_ok=True)
os.rename(tmp_video, video_cache) os.rename(tmp_video, video_cache)
if os.path.exists(audio_cache): os.remove(audio_cache)
os.rename(tmp_audio, audio_cache) os.rename(tmp_audio, audio_cache)
await status.edit_text("📤 <b>Отправляю видео + аудио...</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю видео + аудио...</b>", parse_mode="HTML")
@@ -170,7 +161,6 @@ async def process_instagram_auto(message: Message, user_id: int, url: str):
tmp_path = cache_path(TMP_DIR, key, "mp4") tmp_path = cache_path(TMP_DIR, key, "mp4")
os.makedirs(os.path.dirname(tmp_path), exist_ok=True) os.makedirs(os.path.dirname(tmp_path), exist_ok=True)
# Проверка кэша
if os.path.exists(final_cache): if os.path.exists(final_cache):
await status.edit_text("📤 <b>Отправляю из кэша...</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю из кэша...</b>", parse_mode="HTML")
try: try:
@@ -198,15 +188,12 @@ async def process_instagram_auto(message: Message, user_id: int, url: str):
cancel_event, cancel_event,
progress_cb, progress_cb,
) )
if cancel_event.is_set(): if cancel_event.is_set():
await status.edit_text("⛔ Загрузка отменена") await status.edit_text("⛔ Загрузка отменена")
if os.path.exists(tmp_path): os.remove(tmp_path) if os.path.exists(tmp_path): os.remove(tmp_path)
return return
# Перемещение в кэш
if os.path.exists(final_cache): os.remove(final_cache) if os.path.exists(final_cache): os.remove(final_cache)
os.makedirs(os.path.dirname(final_cache), exist_ok=True)
os.rename(tmp_path, final_cache) os.rename(tmp_path, final_cache)
await status.edit_text("📤 <b>Отправляю видео...</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю видео...</b>", parse_mode="HTML")
@@ -221,16 +208,15 @@ async def process_instagram_auto(message: Message, user_id: int, url: str):
ACTIVE_DOWNLOADS.pop(user_id, None) ACTIVE_DOWNLOADS.pop(user_id, None)
cleanup_tmp(TMP_DIR) cleanup_tmp(TMP_DIR)
# ---------------------- HANDLERS ------------------ # ---------------- Handlers ----------------
@dp.message(F.text == "/start") @dp.message(F.text == "/start")
async def start(message: Message): async def start(message: Message):
await message.answer( await message.answer(
"👋 <b>Привет!</b>\n\n" "👋 <b>Привет!</b>\n\n"
"📥 Скачиваю видео и аудио по ссылке.\n\n" "📥 Скачиваю видео и аудио по ссылке.\n\n"
"✨ <b>Новые возможности:</b>\n" "✨ <b>Новые возможности:</b>\n"
"• 🎬 TikTok: автоматически видео + аудио\n" "• 🎬 TikTok: автоматически видео + аудио (Telegram-safe)\n"
"• 📸 Instagram: автоматически видео в 1080p\n" "• 📸 Instagram: автоматически видео в 1080p (Telegram-safe)\n"
"• 🎵 Аудио из любого видео\n" "• 🎵 Аудио из любого видео\n"
"• 📁 Плейлисты YouTube\n" "• 📁 Плейлисты YouTube\n"
"👉 Просто отправь ссылку.", "👉 Просто отправь ссылку.",
@@ -245,17 +231,14 @@ async def handle_link(message: Message):
platform_info = await asyncio.to_thread(get_platform_info, url) platform_info = await asyncio.to_thread(get_platform_info, url)
# TikTok — автоматически
if platform_info == "tiktok": if platform_info == "tiktok":
await process_tiktok_auto(message, user_id, url) await process_tiktok_auto(message, user_id, url)
return return
# Instagram — автоматически
if platform_info == "instagram": if platform_info == "instagram":
await process_instagram_auto(message, user_id, url) await process_instagram_auto(message, user_id, url)
return return
# Плейлисты
if await asyncio.to_thread(is_playlist, url): if await asyncio.to_thread(is_playlist, url):
await message.answer( await message.answer(
"📁 <b>Обнаружен плейлист!</b>\n\n" "📁 <b>Обнаружен плейлист!</b>\n\n"
@@ -265,7 +248,6 @@ async def handle_link(message: Message):
) )
return return
# YouTube/VK — выбор качества
await message.answer( await message.answer(
"🔽 <b>Выбери качество:</b>", "🔽 <b>Выбери качество:</b>",
reply_markup=youtube_quality_keyboard(), reply_markup=youtube_quality_keyboard(),
@@ -291,7 +273,6 @@ async def handle_video(callback: CallbackQuery):
tmp_path = cache_path(TMP_DIR, key, "mp4") tmp_path = cache_path(TMP_DIR, key, "mp4")
os.makedirs(os.path.dirname(tmp_path), exist_ok=True) os.makedirs(os.path.dirname(tmp_path), exist_ok=True)
# Проверка кэша
if os.path.exists(final_path): if os.path.exists(final_path):
await status.edit_text("📤 <b>Отправляю файл из кэша…</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю файл из кэша…</b>", parse_mode="HTML")
try: try:
@@ -318,14 +299,12 @@ async def handle_video(callback: CallbackQuery):
cancel_event, cancel_event,
progress_cb, progress_cb,
) )
if cancel_event.is_set(): if cancel_event.is_set():
if os.path.exists(tmp_path): os.remove(tmp_path) if os.path.exists(tmp_path): os.remove(tmp_path)
await status.edit_text("⛔ Загрузка отменена") await status.edit_text("⛔ Загрузка отменена")
return return
if os.path.exists(final_path): os.remove(final_path) if os.path.exists(final_path): os.remove(final_path)
os.makedirs(os.path.dirname(final_path), exist_ok=True)
os.rename(tmp_path, final_path) os.rename(tmp_path, final_path)
except DownloadCancelled: except DownloadCancelled:
@@ -372,7 +351,6 @@ async def handle_audio(callback: CallbackQuery):
tmp_path = cache_path(TMP_DIR, key, "mp3") tmp_path = cache_path(TMP_DIR, key, "mp3")
os.makedirs(os.path.dirname(tmp_path), exist_ok=True) os.makedirs(os.path.dirname(tmp_path), exist_ok=True)
# Проверка кэша
if os.path.exists(final_path): if os.path.exists(final_path):
await status.edit_text("📤 <b>Отправляю аудио из кэша…</b>", parse_mode="HTML") await status.edit_text("📤 <b>Отправляю аудио из кэша…</b>", parse_mode="HTML")
try: try:
@@ -398,7 +376,6 @@ async def handle_audio(callback: CallbackQuery):
cancel_event, cancel_event,
progress_cb, progress_cb,
) )
if cancel_event.is_set(): if cancel_event.is_set():
await status.edit_text("⛔ Загрузка отменена") await status.edit_text("⛔ Загрузка отменена")
if os.path.exists(tmp_path): os.remove(tmp_path) if os.path.exists(tmp_path): os.remove(tmp_path)
@@ -410,6 +387,7 @@ async def handle_audio(callback: CallbackQuery):
os.remove(tmp_path) os.remove(tmp_path)
raise Exception("Создан пустой аудио файл") raise Exception("Создан пустой аудио файл")
if os.path.exists(final_path): os.remove(final_path)
os.rename(tmp_path, final_path) os.rename(tmp_path, final_path)
except DownloadCancelled: except DownloadCancelled:
@@ -433,7 +411,7 @@ async def handle_audio(callback: CallbackQuery):
logger.error(f"Error sending audio: {e}") logger.error(f"Error sending audio: {e}")
await status.edit_text("❌ Ошибка при отправке аудио") await status.edit_text("❌ Ошибка при отправке аудио")
# ---------------------- PLAYLIST HANDLERS ------------------ # ---------------- Playlist Handlers ----------------
@dp.callback_query(F.data == "playlist_all") @dp.callback_query(F.data == "playlist_all")
async def handle_playlist_all(callback: CallbackQuery): async def handle_playlist_all(callback: CallbackQuery):
await callback.answer() await callback.answer()
@@ -596,7 +574,6 @@ async def cancel_download(callback: CallbackQuery):
else: else:
await callback.answer("❌ Нет активной загрузки", show_alert=True) await callback.answer("❌ Нет активной загрузки", show_alert=True)
# ---------------------- ENTRYPOINT ------------------
async def main(): async def main():
cleanup_tmp(TMP_DIR) cleanup_tmp(TMP_DIR)
try: try: