211 lines
8.1 KiB
Python
211 lines
8.1 KiB
Python
"""
|
||
Утилиты для 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() |