init commit

This commit is contained in:
2025-09-28 09:18:03 +09:00
commit a8076bc9d0
78 changed files with 11035 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
"""
Утилиты для 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():
"""Создает шаблон файла со ссылками"""
template = """# YouTube URL List Template
# Добавьте по одной ссылке YouTube на каждой строке
# Строки, начинающиеся с #, игнорируются (комментарии)
# Пример видео:
# https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Пример плейлиста:
# https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMt6VEY
# Добавьте ваши ссылки ниже:
"""
with open('urls.txt', 'w', encoding='utf-8') as f:
f.write(template)
print("Создан файл urls.txt с шаблоном ссылок")
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()