Files
youtube_downloader/utils.py
2025-09-28 09:18:03 +09:00

211 lines
8.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Утилиты для YouTube Downloader
"""
import os
import json
import argparse
from datetime import datetime
from config import Config
from downloader import YouTubeDownloader
class DownloadLogger:
"""Класс для логирования загрузок"""
def __init__(self, log_file="download_history.json"):
self.log_file = log_file
self.history = self.load_history()
def load_history(self):
"""Загружает историю загрузок"""
if os.path.exists(self.log_file):
try:
with open(self.log_file, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return []
return []
def save_history(self):
"""Сохраняет историю загрузок"""
try:
with open(self.log_file, 'w', encoding='utf-8') as f:
json.dump(self.history, f, indent=2, ensure_ascii=False)
except IOError as e:
print(f"Ошибка сохранения истории: {e}")
def add_download(self, url, title, success=True, error=None):
"""Добавляет запись о загрузке"""
record = {
'timestamp': datetime.now().isoformat(),
'url': url,
'title': title,
'success': success,
'error': error
}
self.history.append(record)
self.save_history()
def get_recent_downloads(self, count=10):
"""Возвращает последние загрузки"""
return self.history[-count:] if len(self.history) >= count else self.history
def get_failed_downloads(self):
"""Возвращает неудачные загрузки"""
return [record for record in self.history if not record['success']]
class BatchDownloader:
"""Класс для пакетной загрузки из файла со ссылками"""
def __init__(self, config=None):
self.config = config or Config()
self.downloader = YouTubeDownloader(self.config)
self.logger = DownloadLogger()
def download_from_file(self, file_path, quality='best', audio_only=False):
"""Загружает все URL из файла"""
if not os.path.exists(file_path):
print(f"Файл {file_path} не найден")
return False
with open(file_path, 'r', encoding='utf-8') as f:
urls = [line.strip() for line in f if line.strip() and not line.startswith('#')]
print(f"Найдено {len(urls)} URL для загрузки")
successful = 0
failed = 0
for i, url in enumerate(urls, 1):
print(f"\n[{i}/{len(urls)}] Загрузка: {url}")
try:
# Получаем информацию о видео
info = self.downloader.get_video_info(url)
title = info['title'] if info else 'Неизвестное название'
# Загружаем
success = self.downloader.download_video(url, quality, audio_only)
if success:
successful += 1
self.logger.add_download(url, title, True)
print(f"✓ Успешно загружено: {title}")
else:
failed += 1
self.logger.add_download(url, title, False, "Ошибка загрузки")
print(f"✗ Ошибка загрузки: {title}")
except Exception as e:
failed += 1
self.logger.add_download(url, "Неизвестное название", False, str(e))
print(f"✗ Исключение при загрузке {url}: {str(e)}")
print(f"\nРезультаты пакетной загрузки:")
print(f"Успешно: {successful}")
print(f"Ошибок: {failed}")
print(f"Всего: {len(urls)}")
return failed == 0
def create_url_list_template(filename="batch_urls.txt"):
"""Создает шаблон файла со ссылками"""
template = """# Пример файла со списком URL для пакетной загрузки YouTube Downloader
# Каждый URL должен быть на отдельной строке
# Строки, начинающиеся с #, являются комментариями и игнорируются
# Примеры видео для тестирования:
# https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Плейлисты тоже поддерживаются:
# https://www.youtube.com/playlist?list=PLAYLIST_ID
# Добавьте ваши ссылки ниже:
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(template)
print(f"Создан файл {filename} с шаблоном ссылок")
def show_download_history():
"""Показывает историю загрузок"""
logger = DownloadLogger()
recent = logger.get_recent_downloads(20)
if not recent:
print("История загрузок пуста")
return
print("Последние загрузки:")
print("-" * 80)
for record in recent:
timestamp = record['timestamp'][:19] # Убираем микросекунды
status = "" if record['success'] else ""
title = record['title'][:50] + "..." if len(record['title']) > 50 else record['title']
print(f"{status} {timestamp} | {title}")
if not record['success'] and record.get('error'):
print(f" Ошибка: {record['error']}")
def clean_downloads_folder():
"""Очищает папку загрузок"""
config = Config()
downloads_dir = config.get('output_directory', 'downloads')
if not downloads_dir or not os.path.exists(downloads_dir):
print(f"Папка {downloads_dir} не существует")
return
files = os.listdir(downloads_dir)
if not files:
print(f"Папка {downloads_dir} уже пуста")
return
print(f"Найдено {len(files)} файлов в {downloads_dir}")
confirm = input("Удалить все файлы? (yes/no): ")
if confirm.lower() in ['yes', 'y', 'да', 'д']:
for file in files:
file_path = os.path.join(str(downloads_dir), file)
try:
if os.path.isfile(file_path):
os.remove(file_path)
print(f"Удален: {file}")
except Exception as e:
print(f"Ошибка удаления {file}: {e}")
print("Очистка завершена")
else:
print("Очистка отменена")
def main():
parser = argparse.ArgumentParser(description='YouTube Downloader - Утилиты')
parser.add_argument('action', choices=[
'batch', 'history', 'clean', 'template'
], help='Действие для выполнения')
parser.add_argument('--file', '-f', help='Файл со ссылками для пакетной загрузки')
parser.add_argument('--quality', '-q', default='best', help='Качество видео')
parser.add_argument('--audio-only', '-a', action='store_true', help='Только аудио')
args = parser.parse_args()
if args.action == 'batch':
if not args.file:
print("Для пакетной загрузки необходимо указать файл со ссылками (--file)")
return
batch = BatchDownloader()
batch.download_from_file(args.file, args.quality, args.audio_only)
elif args.action == 'history':
show_download_history()
elif args.action == 'clean':
clean_downloads_folder()
elif args.action == 'template':
create_url_list_template()
if __name__ == '__main__':
main()