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

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
.venv/
.env
downloads/
__pycache__/
*.pyc
*.pyo
*.pyd
*.sqlite3
instance/
*.log
*.pot
*.mo
*.swp
.DS_Store
.idea/
.vscode/

View File

View File

@@ -0,0 +1,16 @@
.venv/
.env
downloads/
__pycache__/
*.pyc
*.pyo
*.pyd
*.sqlite3
instance/
*.log
*.pot
*.mo
*.swp
.DS_Store
.idea/
.vscode/

View File

@@ -0,0 +1,125 @@
# 📋 Руководство по пакетной загрузке YouTube Downloader
## 🎯 Способы пакетной загрузки
### 1. Загрузка из файла со списком URL
Создайте текстовый файл с URL (один на строку):
```bash
# Создать шаблон
python3 utils.py template batch_urls.txt
# Отредактировать файл и добавить URL
nano batch_urls.txt
# Загрузить все URL из файла
python3 main.py --batch batch_urls.txt --audio-only
```
**Пример содержимого файла:**
```
# Мои любимые видео
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=VIDEO_ID_2
https://www.youtube.com/watch?v=VIDEO_ID_3
# Плейлист (используйте с --playlist)
https://www.youtube.com/playlist?list=PLAYLIST_ID
```
### 2. Загрузка нескольких URL через командную строку
```bash
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 🛠 Опции для пакетной загрузки
### Основные параметры:
- `--batch FILE` - Файл со списком URL
- `--urls URL1 URL2...` - Несколько URL через пробел
- `--continue-on-error` - Продолжать при ошибках
- `--quality QUALITY` - Качество для всех видео
- `--audio-only` - Только аудио для всех
- `--output DIR` - Папка сохранения
### Примеры команд:
```bash
# Скачать аудио из файла, продолжать при ошибках
python3 main.py --batch my_urls.txt --audio-only --continue-on-error
# Скачать несколько видео в 720p
python3 main.py --urls URL1 URL2 URL3 --quality 720p
# Скачать плейлист из файла
python3 main.py --batch playlists.txt --playlist
# Показать информацию о первом URL из файла
python3 main.py --batch urls.txt --info
# Сохранить в определенную папку
python3 main.py --batch urls.txt --output /path/to/folder
```
## 📊 Отчеты о загрузке
После пакетной загрузки отображается:
- ✅ Количество успешных загрузок
- ❌ Количество ошибок
- 📋 Список неудачных URL (до 5 первых)
- 📁 Путь к сохраненным файлам
## 🔧 Make команды для тестирования
```bash
make create-batch-template # Создать шаблон
make test-batch # Тест с показом info
make demo-batch # Демо загрузки аудио
```
## 💡 Советы и рекомендации
### Формат файла со списком URL:
- Один URL на строку
- Строки с `#` - комментарии (игнорируются)
- Пустые строки игнорируются
- UTF-8 кодировка
### Обработка ошибок:
- По умолчанию остановка на первой ошибке
- `--continue-on-error` - продолжить загрузку других URL
- Подробные сообщения об ошибках в выводе
### Производительность:
- Загрузка по одному URL (последовательно)
- Автоматические задержки между запросами
- Fallback стратегии при блокировках
## ❗ Важные моменты
1. **Лимиты YouTube**: Не злоупотребляйте пакетной загрузкой
2. **Права авторов**: Соблюдайте авторские права
3. **Дисковое пространство**: Контролируйте объем загрузок
4. **Сетевая нагрузка**: Учитывайте трафик интернета
## 🐛 Troubleshooting
**Проблема**: Некоторые URL не загружаются
```bash
# Решение: включить продолжение при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
**Проблема**: Медленная загрузка
```bash
# Решение: попробовать более низкое качество
python3 main.py --batch urls.txt --quality 480p
```
**Проблема**: Ошибки 403
```bash
# Решение: приложение автоматически применит fallback стратегии
# Просто дождитесь повторных попыток
```

View File

@@ -0,0 +1,125 @@
# 📋 Руководство по пакетной загрузке YouTube Downloader
## 🎯 Способы пакетной загрузки
### 1. Загрузка из файла со списком URL
Создайте текстовый файл с URL (один на строку):
```bash
# Создать шаблон
python3 utils.py template batch_urls.txt
# Отредактировать файл и добавить URL
nano batch_urls.txt
# Загрузить все URL из файла
python3 main.py --batch batch_urls.txt --audio-only
```
**Пример содержимого файла:**
```
# Мои любимые видео
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=VIDEO_ID_2
https://www.youtube.com/watch?v=VIDEO_ID_3
# Плейлист (используйте с --playlist)
https://www.youtube.com/playlist?list=PLAYLIST_ID
```
### 2. Загрузка нескольких URL через командную строку
```bash
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 🛠 Опции для пакетной загрузки
### Основные параметры:
- `--batch FILE` - Файл со списком URL
- `--urls URL1 URL2...` - Несколько URL через пробел
- `--continue-on-error` - Продолжать при ошибках
- `--quality QUALITY` - Качество для всех видео
- `--audio-only` - Только аудио для всех
- `--output DIR` - Папка сохранения
### Примеры команд:
```bash
# Скачать аудио из файла, продолжать при ошибках
python3 main.py --batch my_urls.txt --audio-only --continue-on-error
# Скачать несколько видео в 720p
python3 main.py --urls URL1 URL2 URL3 --quality 720p
# Скачать плейлист из файла
python3 main.py --batch playlists.txt --playlist
# Показать информацию о первом URL из файла
python3 main.py --batch urls.txt --info
# Сохранить в определенную папку
python3 main.py --batch urls.txt --output /path/to/folder
```
## 📊 Отчеты о загрузке
После пакетной загрузки отображается:
- ✅ Количество успешных загрузок
- ❌ Количество ошибок
- 📋 Список неудачных URL (до 5 первых)
- 📁 Путь к сохраненным файлам
## 🔧 Make команды для тестирования
```bash
make create-batch-template # Создать шаблон
make test-batch # Тест с показом info
make demo-batch # Демо загрузки аудио
```
## 💡 Советы и рекомендации
### Формат файла со списком URL:
- Один URL на строку
- Строки с `#` - комментарии (игнорируются)
- Пустые строки игнорируются
- UTF-8 кодировка
### Обработка ошибок:
- По умолчанию остановка на первой ошибке
- `--continue-on-error` - продолжить загрузку других URL
- Подробные сообщения об ошибках в выводе
### Производительность:
- Загрузка по одному URL (последовательно)
- Автоматические задержки между запросами
- Fallback стратегии при блокировках
## ❗ Важные моменты
1. **Лимиты YouTube**: Не злоупотребляйте пакетной загрузкой
2. **Права авторов**: Соблюдайте авторские права
3. **Дисковое пространство**: Контролируйте объем загрузок
4. **Сетевая нагрузка**: Учитывайте трафик интернета
## 🐛 Troubleshooting
**Проблема**: Некоторые URL не загружаются
```bash
# Решение: включить продолжение при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
**Проблема**: Медленная загрузка
```bash
# Решение: попробовать более низкое качество
python3 main.py --batch urls.txt --quality 480p
```
**Проблема**: Ошибки 403
```bash
# Решение: приложение автоматически применит fallback стратегии
# Просто дождитесь повторных попыток
```

View File

@@ -0,0 +1,58 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Скачать тестовое видео (информация)
demo-info:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info
# Показать форматы тестового видео
demo-formats:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --formats
# Все команды
all: install config run-examples
# Справка по Makefile
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " all - Установить все и запустить примеры"

View File

@@ -0,0 +1,58 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Скачать тестовое видео (информация)
demo-info:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info
# Показать форматы тестового видео
demo-formats:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --formats
# Все команды
all: install config run-examples
# Справка по Makefile
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " all - Установить все и запустить примеры"

View File

@@ -0,0 +1,65 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Скачать тестовое видео (информация)
demo-info:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info
# Показать форматы тестового видео
demo-formats:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --formats
# Все команды
all: install config run-examples
# Справка по Makefile
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " all - Установить все и запустить примеры"

View File

@@ -0,0 +1,72 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Скачать тестовое видео (информация)
demo-info:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info
# Показать форматы тестового видео
demo-formats:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --formats
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

View File

@@ -0,0 +1,72 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Скачать тестовое видео (информация)
demo-info:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info
# Показать форматы тестового видео
demo-formats:
python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --formats
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

View File

@@ -0,0 +1,76 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Тестирование пакетной загрузки
test-batch:
python3 main.py --batch batch_urls.txt --info
# Демо пакетной загрузки
demo-batch:
python3 main.py --batch batch_urls.txt --audio-only --continue-on-error
# Создать шаблон для пакетной загрузки
create-batch-template:
python3 utils.py template batch_template.txt
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " demo-info - Показать информацию о тестовом видео"
@echo " demo-formats - Показать форматы тестового видео"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

View File

@@ -0,0 +1,77 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Тестирование пакетной загрузки
test-batch:
python3 main.py --batch batch_urls.txt --info
# Демо пакетной загрузки
demo-batch:
python3 main.py --batch batch_urls.txt --audio-only --continue-on-error
# Создать шаблон для пакетной загрузки
create-batch-template:
python3 utils.py template batch_template.txt
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " test-batch - Тест пакетной загрузки (только info)"
@echo " demo-batch - Демо пакетной загрузки аудио"
@echo " create-batch-template - Создать шаблон для пакетной загрузки"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

View File

@@ -0,0 +1,77 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Тестирование пакетной загрузки
test-batch:
python3 main.py --batch batch_urls.txt --info
# Демо пакетной загрузки
demo-batch:
python3 main.py --batch batch_urls.txt --audio-only --continue-on-error
# Создать шаблон для пакетной загрузки
create-batch-template:
python3 utils.py template batch_template.txt
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " test-batch - Тест пакетной загрузки (только info)"
@echo " demo-batch - Демо пакетной загрузки аудио"
@echo " create-batch-template - Создать шаблон для пакетной загрузки"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

View File

@@ -0,0 +1,64 @@
# YouTube Downloader
Приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
pip install -r requirements.txt
```
## Использование
### Скачать одно видео:
```bash
python main.py https://www.youtube.com/watch?v=VIDEO_ID
```
### Скачать с определенным качеством:
```bash
python main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
```
### Скачать только аудио:
```bash
python main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
```
### Указать папку загрузки:
```bash
python main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
```
## Возможности
- ✅ Скачивание видео в различных разрешениях
- ✅ Скачивание только аудио
- ✅ Прогресс-бар загрузки
- ✅ Настраиваемая папка загрузки
- ✅ Поддержка плейлистов
- ✅ Валидация URL
## Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## Структура проекта
```
youtube_downloader/
├── main.py # Точка входа в приложение
├── downloader.py # Основной класс загрузчика
├── config.py # Конфигурация приложения
├── requirements.txt # Зависимости Python
└── README.md # Документация
```

View File

@@ -0,0 +1,215 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install
# или
pip3 install -r requirements.txt
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install # Установить зависимости
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
pip3 install --upgrade yt-dlp
```
### Проблемы с ffmpeg
Установите ffmpeg:
```bash
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
# Скачайте с https://ffmpeg.org/
```
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,215 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install
# или
pip3 install -r requirements.txt
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install # Установить зависимости
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
pip3 install --upgrade yt-dlp
```
### Проблемы с ffmpeg
Установите ffmpeg:
```bash
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
# Скачайте с https://ffmpeg.org/
```
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,224 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install # Установить зависимости
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
pip3 install --upgrade yt-dlp
```
### Проблемы с ffmpeg
Установите ffmpeg:
```bash
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
# Скачайте с https://ffmpeg.org/
```
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,226 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
pip3 install --upgrade yt-dlp
```
### Проблемы с ffmpeg
Установите ffmpeg:
```bash
# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
# Скачайте с https://ffmpeg.org/
```
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,237 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,237 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,243 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Пакетная загрузка из файла
python3 main.py --batch batch_urls.txt --audio-only
# Загрузка нескольких URL
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,258 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ Пакетная загрузка из файла
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Пакетная загрузка из файла
python3 main.py --batch batch_urls.txt --audio-only
# Загрузка нескольких URL
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Пакетная загрузка:
```bash
# Создать шаблон файла со списком URL
make create-batch-template
# Загрузка из файла со списком URL
python3 main.py --batch urls.txt --quality 720p --continue-on-error
# Загрузка нескольких URL через командную строку
python3 main.py --urls URL1 URL2 URL3 --audio-only
# Продолжить загрузку при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,261 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
-**Пакетная загрузка из файла со списком URL**
-**Загрузка нескольких URL через командную строку**
-**Продолжение загрузки при ошибках (--continue-on-error)**
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
- ✅ Автоматическое исправление ошибок 403/блокировок
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Пакетная загрузка из файла
python3 main.py --batch batch_urls.txt --audio-only
# Загрузка нескольких URL
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Пакетная загрузка:
```bash
# Создать шаблон файла со списком URL
make create-batch-template
# Загрузка из файла со списком URL
python3 main.py --batch urls.txt --quality 720p --continue-on-error
# Загрузка нескольких URL через командную строку
python3 main.py --urls URL1 URL2 URL3 --audio-only
# Продолжить загрузку при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,261 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
-**Пакетная загрузка из файла со списком URL**
-**Загрузка нескольких URL через командную строку**
-**Продолжение загрузки при ошибках (--continue-on-error)**
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
- ✅ Автоматическое исправление ошибок 403/блокировок
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Пакетная загрузка из файла
python3 main.py --batch batch_urls.txt --audio-only
# Загрузка нескольких URL
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Пакетная загрузка:
```bash
# Создать шаблон файла со списком URL
make create-batch-template
# Загрузка из файла со списком URL
python3 main.py --batch urls.txt --quality 720p --continue-on-error
# Загрузка нескольких URL через командную строку
python3 main.py --urls URL1 URL2 URL3 --audio-only
# Продолжить загрузку при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

View File

@@ -0,0 +1,14 @@
# Пример файла со списком URL для пакетной загрузки YouTube Downloader
# Каждый URL должен быть на отдельной строке
# Строки, начинающиеся с #, являются комментариями и игнорируются
# Примеры видео для тестирования:
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=etuo7AA6rmY
# Можно добавить больше URL:
# https://www.youtube.com/watch?v=ANOTHER_VIDEO_ID
# https://www.youtube.com/watch?v=YET_ANOTHER_VIDEO_ID
# Плейлисты тоже поддерживаются (используйте --playlist):
# https://www.youtube.com/playlist?list=PLAYLIST_ID

View File

@@ -0,0 +1,14 @@
# Пример файла со списком URL для пакетной загрузки YouTube Downloader
# Каждый URL должен быть на отдельной строке
# Строки, начинающиеся с #, являются комментариями и игнорируются
# Примеры видео для тестирования:
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=etuo7AA6rmY
# Можно добавить больше URL:
# https://www.youtube.com/watch?v=ANOTHER_VIDEO_ID
# https://www.youtube.com/watch?v=YET_ANOTHER_VIDEO_ID
# Плейлисты тоже поддерживаются (используйте --playlist):
# https://www.youtube.com/playlist?list=PLAYLIST_ID

View File

@@ -0,0 +1,55 @@
import os
import json
class Config:
"""Класс для управления конфигурацией приложения"""
DEFAULT_CONFIG = {
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": True,
"add_metadata": True,
"subtitle_languages": ["ru", "en"],
"download_subtitles": False
}
def __init__(self, config_file="config.json"):
self.config_file = config_file
self.config = self.load_config()
def load_config(self):
"""Загружает конфигурацию из файла или создает стандартную"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return {**self.DEFAULT_CONFIG, **json.load(f)}
except (json.JSONDecodeError, IOError):
print(f"Ошибка чтения конфигурации из {self.config_file}. Используется стандартная конфигурация.")
return self.DEFAULT_CONFIG.copy()
def save_config(self):
"""Сохраняет текущую конфигурацию в файл"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=4, ensure_ascii=False)
except IOError as e:
print(f"Ошибка сохранения конфигурации: {e}")
def get(self, key, default=None):
"""Получает значение конфигурации"""
return self.config.get(key, default)
def set(self, key, value):
"""Устанавливает значение конфигурации"""
self.config[key] = value
def create_output_directory(self):
"""Создает папку для загрузки, если она не существует"""
output_dir = self.get("output_directory")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка для загрузок: {output_dir}")
return output_dir

View File

@@ -0,0 +1,55 @@
import os
import json
class Config:
"""Класс для управления конфигурацией приложения"""
DEFAULT_CONFIG = {
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": True,
"add_metadata": True,
"subtitle_languages": ["ru", "en"],
"download_subtitles": False
}
def __init__(self, config_file="config.json"):
self.config_file = config_file
self.config = self.load_config()
def load_config(self):
"""Загружает конфигурацию из файла или создает стандартную"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return {**self.DEFAULT_CONFIG, **json.load(f)}
except (json.JSONDecodeError, IOError):
print(f"Ошибка чтения конфигурации из {self.config_file}. Используется стандартная конфигурация.")
return self.DEFAULT_CONFIG.copy()
def save_config(self):
"""Сохраняет текущую конфигурацию в файл"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=4, ensure_ascii=False)
except IOError as e:
print(f"Ошибка сохранения конфигурации: {e}")
def get(self, key, default=None):
"""Получает значение конфигурации"""
return self.config.get(key, default)
def set(self, key, value):
"""Устанавливает значение конфигурации"""
self.config[key] = value
def create_output_directory(self):
"""Создает папку для загрузки, если она не существует"""
output_dir = self.get("output_directory", "downloads")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка для загрузок: {output_dir}")
return output_dir

View File

@@ -0,0 +1,55 @@
import os
import json
class Config:
"""Класс для управления конфигурацией приложения"""
DEFAULT_CONFIG = {
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": True,
"add_metadata": True,
"subtitle_languages": ["ru", "en"],
"download_subtitles": False
}
def __init__(self, config_file="config.json"):
self.config_file = config_file
self.config = self.load_config()
def load_config(self):
"""Загружает конфигурацию из файла или создает стандартную"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return {**self.DEFAULT_CONFIG, **json.load(f)}
except (json.JSONDecodeError, IOError):
print(f"Ошибка чтения конфигурации из {self.config_file}. Используется стандартная конфигурация.")
return self.DEFAULT_CONFIG.copy()
def save_config(self):
"""Сохраняет текущую конфигурацию в файл"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=4, ensure_ascii=False)
except IOError as e:
print(f"Ошибка сохранения конфигурации: {e}")
def get(self, key, default=None):
"""Получает значение конфигурации"""
return self.config.get(key, default)
def set(self, key, value):
"""Устанавливает значение конфигурации"""
self.config[key] = value
def create_output_directory(self):
"""Создает папку для загрузки, если она не существует"""
output_dir = self.get("output_directory", "downloads")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка для загрузок: {output_dir}")
return output_dir

View File

@@ -0,0 +1,213 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - self.progress_bar.last_downloaded)
else:
self.progress_bar.update(downloaded)
self.progress_bar.last_downloaded = downloaded
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(output_path, '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
for f in info.get('formats', []):
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,215 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(output_path, '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
for f in info.get('formats', []):
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,215 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(output_path, '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
for f in info.get('formats', []):
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,215 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
for f in info.get('formats', []):
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,216 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and 'formats' in info:
for f in info.get('formats', []):
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,218 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,218 @@
import os
import re
import yt_dlp
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,243 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки: {str(e)}{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,303 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except yt_dlp.DownloadError as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}Неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,303 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except yt_dlp.utils.DownloadError as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}Неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,303 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg or "HTTP Error" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}Неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,299 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg or "HTTP Error" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,299 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg or "HTTP Error" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python3
"""
Примеры использования YouTube Downloader
"""
from downloader import YouTubeDownloader
from config import Config
from colorama import Fore, Style
def example_download_video():
"""Пример загрузки одного видео"""
print(f"{Fore.CYAN}=== Пример загрузки видео ==={Style.RESET_ALL}")
# URL тестового видео (короткое видео с YouTube)
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # Rick Roll для теста
config = Config()
downloader = YouTubeDownloader(config)
# Получение информации о видео
print(f"{Fore.YELLOW}Получение информации о видео...{Style.RESET_ALL}")
info = downloader.get_video_info(url)
if info:
print(f"Название: {info['title']}")
print(f"Автор: {info['uploader']}")
print(f"Длительность: {info['duration']} сек")
# Показать доступные форматы
print(f"\n{Fore.YELLOW}Доступные форматы:{Style.RESET_ALL}")
formats = downloader.get_available_formats(url)
for fmt in formats[:5]: # Показываем только первые 5 форматов
print(f"{fmt['quality']} ({fmt['ext']})")
return url
def example_download_audio():
"""Пример загрузки только аудио"""
print(f"\n{Fore.CYAN}=== Пример загрузки аудио ==={Style.RESET_ALL}")
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
config = Config()
downloader = YouTubeDownloader(config)
print("Для загрузки только аудио используйте параметр audio_only=True")
print("Пример: downloader.download_video(url, audio_only=True)")
def show_cli_examples():
"""Показать примеры использования CLI"""
print(f"\n{Fore.CYAN}=== Примеры использования CLI ==={Style.RESET_ALL}")
examples = [
"# Скачать видео в лучшем качестве",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID",
"",
"# Скачать видео в 720p",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p",
"",
"# Скачать только аудио",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only",
"",
"# Скачать в определенную папку",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --output /path/to/downloads/",
"",
"# Скачать плейлист",
"python main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist",
"",
"# Показать информацию о видео",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --info",
"",
"# Показать доступные форматы",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --formats",
"",
"# Настроить конфигурацию по умолчанию",
"python main.py configure --output-dir downloads --video-quality 720p",
"",
"# Показать текущие настройки",
"python main.py show-config"
]
for example in examples:
if example.startswith("#"):
print(f"{Fore.GREEN}{example}{Style.RESET_ALL}")
elif example == "":
print()
else:
print(f"{Fore.WHITE}{example}{Style.RESET_ALL}")
def test_url_validation():
"""Тест валидации URL"""
print(f"\n{Fore.CYAN}=== Тест валидации URL ==={Style.RESET_ALL}")
downloader = YouTubeDownloader()
test_urls = [
("https://www.youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://youtu.be/dQw4w9WgXcQ", True),
("https://youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMt6VEY", True),
("https://example.com/video", False),
("not_a_url", False),
]
for url, expected in test_urls:
result = downloader.validate_url(url)
status = "" if result == expected else ""
color = Fore.GREEN if result == expected else Fore.RED
print(f"{color}{status} {url} -> {result}{Style.RESET_ALL}")
if __name__ == "__main__":
print(f"{Fore.CYAN}YouTube Downloader - Примеры использования{Style.RESET_ALL}")
print("=" * 60)
# Тест валидации URL
test_url_validation()
# Примеры загрузки
test_url = example_download_video()
example_download_audio()
# Примеры CLI
show_cli_examples()
print(f"\n{Fore.GREEN}Все примеры показаны. Для реальной загрузки используйте main.py{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Для начала работы установите зависимости: pip install -r requirements.txt{Style.RESET_ALL}")

View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python3
"""
Примеры использования YouTube Downloader
"""
from downloader import YouTubeDownloader
from config import Config
from colorama import Fore, Style
def example_download_video():
"""Пример загрузки одного видео"""
print(f"{Fore.CYAN}=== Пример загрузки видео ==={Style.RESET_ALL}")
# URL тестового видео (короткое видео с YouTube)
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # Rick Roll для теста
config = Config()
downloader = YouTubeDownloader(config)
# Получение информации о видео
print(f"{Fore.YELLOW}Получение информации о видео...{Style.RESET_ALL}")
info = downloader.get_video_info(url)
if info:
print(f"Название: {info['title']}")
print(f"Автор: {info['uploader']}")
print(f"Длительность: {info['duration']} сек")
# Показать доступные форматы
print(f"\n{Fore.YELLOW}Доступные форматы:{Style.RESET_ALL}")
formats = downloader.get_available_formats(url)
for fmt in formats[:5]: # Показываем только первые 5 форматов
print(f"{fmt['quality']} ({fmt['ext']})")
return url
def example_download_audio():
"""Пример загрузки только аудио"""
print(f"\n{Fore.CYAN}=== Пример загрузки аудио ==={Style.RESET_ALL}")
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
config = Config()
downloader = YouTubeDownloader(config)
print("Для загрузки только аудио используйте параметр audio_only=True")
print("Пример: downloader.download_video(url, audio_only=True)")
def show_cli_examples():
"""Показать примеры использования CLI"""
print(f"\n{Fore.CYAN}=== Примеры использования CLI ==={Style.RESET_ALL}")
examples = [
"# Скачать видео в лучшем качестве",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID",
"",
"# Скачать видео в 720p",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p",
"",
"# Скачать только аудио",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only",
"",
"# Скачать в определенную папку",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --output /path/to/downloads/",
"",
"# Скачать плейлист",
"python main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist",
"",
"# Показать информацию о видео",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --info",
"",
"# Показать доступные форматы",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --formats",
"",
"# Настроить конфигурацию по умолчанию",
"python main.py configure --output-dir downloads --video-quality 720p",
"",
"# Показать текущие настройки",
"python main.py show-config"
]
for example in examples:
if example.startswith("#"):
print(f"{Fore.GREEN}{example}{Style.RESET_ALL}")
elif example == "":
print()
else:
print(f"{Fore.WHITE}{example}{Style.RESET_ALL}")
def test_url_validation():
"""Тест валидации URL"""
print(f"\n{Fore.CYAN}=== Тест валидации URL ==={Style.RESET_ALL}")
downloader = YouTubeDownloader()
test_urls = [
("https://www.youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://youtu.be/dQw4w9WgXcQ", True),
("https://youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMt6VEY", True),
("https://example.com/video", False),
("not_a_url", False),
]
for url, expected in test_urls:
result = downloader.validate_url(url)
status = "" if result == expected else ""
color = Fore.GREEN if result == expected else Fore.RED
print(f"{color}{status} {url} -> {result}{Style.RESET_ALL}")
if __name__ == "__main__":
print(f"{Fore.CYAN}YouTube Downloader - Примеры использования{Style.RESET_ALL}")
print("=" * 60)
# Тест валидации URL
test_url_validation()
# Примеры загрузки
test_url = example_download_video()
example_download_audio()
# Примеры CLI
show_cli_examples()
print(f"\n{Fore.GREEN}Все примеры показаны. Для реальной загрузки используйте main.py{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Для начала работы установите зависимости: pip install -r requirements.txt{Style.RESET_ALL}")

View File

@@ -0,0 +1,63 @@
#!/bin/bash
# Скрипт установки ffmpeg для YouTube Downloader
echo "🎬 Установка ffmpeg для YouTube Downloader"
echo "=========================================="
# Определение ОС
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "🐧 Linux обнаружен"
# Проверка наличия apt
if command -v apt &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через apt..."
sudo apt update && sudo apt install -y ffmpeg
# Проверка наличия yum
elif command -v yum &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через yum..."
sudo yum install -y ffmpeg
# Проверка наличия dnf
elif command -v dnf &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через dnf..."
sudo dnf install -y ffmpeg
else
echo "❌ Менеджер пакетов не найден. Установите ffmpeg вручную."
exit 1
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "🍎 macOS обнаружен"
if command -v brew &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через Homebrew..."
brew install ffmpeg
else
echo "❌ Homebrew не найден. Установите его сначала:"
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
exit 1
fi
else
echo "❓ Неизвестная ОС: $OSTYPE"
echo "Установите ffmpeg вручную:"
echo " - Windows: Скачайте с https://ffmpeg.org/"
echo " - Linux: sudo apt install ffmpeg"
echo " - macOS: brew install ffmpeg"
exit 1
fi
# Проверка установки
if command -v ffmpeg &> /dev/null; then
echo "✅ ffmpeg успешно установлен!"
ffmpeg -version | head -1
echo ""
echo "🎯 Теперь можно скачивать аудио в MP3:"
echo " python3 main.py URL --audio-only"
else
echo "❌ Ошибка установки ffmpeg"
exit 1
fi

View File

@@ -0,0 +1,63 @@
#!/bin/bash
# Скрипт установки ffmpeg для YouTube Downloader
echo "🎬 Установка ffmpeg для YouTube Downloader"
echo "=========================================="
# Определение ОС
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "🐧 Linux обнаружен"
# Проверка наличия apt
if command -v apt &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через apt..."
sudo apt update && sudo apt install -y ffmpeg
# Проверка наличия yum
elif command -v yum &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через yum..."
sudo yum install -y ffmpeg
# Проверка наличия dnf
elif command -v dnf &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через dnf..."
sudo dnf install -y ffmpeg
else
echo "❌ Менеджер пакетов не найден. Установите ffmpeg вручную."
exit 1
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "🍎 macOS обнаружен"
if command -v brew &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через Homebrew..."
brew install ffmpeg
else
echo "❌ Homebrew не найден. Установите его сначала:"
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
exit 1
fi
else
echo "❓ Неизвестная ОС: $OSTYPE"
echo "Установите ffmpeg вручную:"
echo " - Windows: Скачайте с https://ffmpeg.org/"
echo " - Linux: sudo apt install ffmpeg"
echo " - macOS: brew install ffmpeg"
exit 1
fi
# Проверка установки
if command -v ffmpeg &> /dev/null; then
echo "✅ ffmpeg успешно установлен!"
ffmpeg -version | head -1
echo ""
echo "🎯 Теперь можно скачивать аудио в MP3:"
echo " python3 main.py URL --audio-only"
else
echo "❌ Ошибка установки ffmpeg"
exit 1
fi

View File

@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
@click.command()
@click.argument('url', required=True)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
def main(url, quality, audio_only, output, playlist, info, formats, config):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Проверка URL
if not downloader.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
# Показать информацию о видео
if info:
video_info = downloader.get_video_info(url)
if video_info:
print(f"\n{Fore.GREEN}Информация о видео:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Название:{Style.RESET_ALL} {video_info['title']}")
print(f"{Fore.YELLOW}Автор:{Style.RESET_ALL} {video_info['uploader']}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.YELLOW}Длительность:{Style.RESET_ALL} {duration_str}")
if video_info['view_count']:
print(f"{Fore.YELLOW}Просмотров:{Style.RESET_ALL} {video_info['view_count']:,}")
if video_info['upload_date']:
date_str = video_info['upload_date']
formatted_date = f"{date_str[6:8]}.{date_str[4:6]}.{date_str[:4]}"
print(f"{Fore.YELLOW}Дата загрузки:{Style.RESET_ALL} {formatted_date}")
if video_info['description']:
desc = video_info['description'][:200] + "..." if len(video_info['description']) > 200 else video_info['description']
print(f"{Fore.YELLOW}Описание:{Style.RESET_ALL} {desc}")
return
# Показать доступные форматы
if formats:
print(f"\n{Fore.GREEN}Получение доступных форматов...{Style.RESET_ALL}")
available_formats = downloader.get_available_formats(url)
if available_formats:
print(f"\n{Fore.GREEN}Доступные форматы:{Style.RESET_ALL}")
for fmt in available_formats:
size_str = f" ({fmt['filesize']//1024//1024} MB)" if fmt['filesize'] else ""
print(f"{fmt['quality']} ({fmt['ext']}){size_str}")
else:
print(f"{Fore.RED}Не удалось получить информацию о форматах{Style.RESET_ALL}")
return
# Скачивание
print(f"\n{Fore.GREEN}Начинаю обработку...{Style.RESET_ALL}")
success = False
if playlist:
success = downloader.download_playlist(url, quality, audio_only, output)
else:
success = downloader.download_video(url, quality, audio_only, output)
if success:
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

View File

@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
@click.command()
@click.argument('url', required=True)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
def main(url, quality, audio_only, output, playlist, info, formats, config):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Проверка URL
if not downloader.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
# Показать информацию о видео
if info:
video_info = downloader.get_video_info(url)
if video_info:
print(f"\n{Fore.GREEN}Информация о видео:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Название:{Style.RESET_ALL} {video_info['title']}")
print(f"{Fore.YELLOW}Автор:{Style.RESET_ALL} {video_info['uploader']}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.YELLOW}Длительность:{Style.RESET_ALL} {duration_str}")
if video_info['view_count']:
print(f"{Fore.YELLOW}Просмотров:{Style.RESET_ALL} {video_info['view_count']:,}")
if video_info['upload_date']:
date_str = video_info['upload_date']
formatted_date = f"{date_str[6:8]}.{date_str[4:6]}.{date_str[:4]}"
print(f"{Fore.YELLOW}Дата загрузки:{Style.RESET_ALL} {formatted_date}")
if video_info['description']:
desc = video_info['description'][:200] + "..." if len(video_info['description']) > 200 else video_info['description']
print(f"{Fore.YELLOW}Описание:{Style.RESET_ALL} {desc}")
return
# Показать доступные форматы
if formats:
print(f"\n{Fore.GREEN}Получение доступных форматов...{Style.RESET_ALL}")
available_formats = downloader.get_available_formats(url)
if available_formats:
print(f"\n{Fore.GREEN}Доступные форматы:{Style.RESET_ALL}")
for fmt in available_formats:
size_str = f" ({fmt['filesize']//1024//1024} MB)" if fmt['filesize'] else ""
print(f"{fmt['quality']} ({fmt['ext']}){size_str}")
else:
print(f"{Fore.RED}Не удалось получить информацию о форматах{Style.RESET_ALL}")
return
# Скачивание
print(f"\n{Fore.GREEN}Начинаю обработку...{Style.RESET_ALL}")
success = False
if playlist:
success = downloader.download_playlist(url, quality, audio_only, output)
else:
success = downloader.download_video(url, quality, audio_only, output)
if success:
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
@click.command()
@click.argument('url', required=False)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
@click.option('--batch', '-b', default=None, type=click.Path(exists=True),
help='Файл со списком URL для пакетной загрузки')
@click.option('--urls', multiple=True,
help='Несколько URL через пробел')
@click.option('--continue-on-error', is_flag=True,
help='Продолжать загрузку при ошибках')
def main(url, quality, audio_only, output, playlist, info, formats, config, batch, urls, continue_on_error):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист (опционально при использовании --batch или --urls)
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Определяем список URL для обработки
urls_to_process = []
if batch:
# Пакетная загрузка из файла
print(f"{Fore.GREEN}Загрузка URL из файла: {batch}{Style.RESET_ALL}")
urls_to_process = load_urls_from_file(batch)
elif urls:
# Множественные URL из командной строки
urls_to_process = list(urls)
print(f"{Fore.GREEN}Обработка {len(urls_to_process)} URL из командной строки{Style.RESET_ALL}")
elif url:
# Один URL
urls_to_process = [url]
else:
print(f"{Fore.RED}Ошибка: Необходимо указать URL, --batch файл или --urls{Style.RESET_ALL}")
sys.exit(1)
# Обработка списка URL
if info or formats:
# Для info и formats обрабатываем только первый URL
process_single_url = urls_to_process[0] if urls_to_process else None
if not process_single_url:
print(f"{Fore.RED}Ошибка: Нет URL для обработки{Style.RESET_ALL}")
sys.exit(1)
if not downloader.validate_url(process_single_url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
if info:
show_video_info(downloader, process_single_url)
return
if formats:
show_video_formats(downloader, process_single_url)
return
# Пакетная загрузка
print(f"\n{Fore.GREEN}Начинаю обработку {len(urls_to_process)} URL...{Style.RESET_ALL}")
successful = 0
failed = 0
failed_urls = []
for i, current_url in enumerate(urls_to_process, 1):
print(f"\n{Fore.CYAN}[{i}/{len(urls_to_process)}] Обработка: {current_url}{Style.RESET_ALL}")
# Проверка URL
if not downloader.validate_url(current_url):
print(f"{Fore.RED}✗ Некорректный URL, пропускаем{Style.RESET_ALL}")
failed += 1
failed_urls.append((current_url, "Некорректный URL"))
if not continue_on_error:
break
continue
try:
success = False
if playlist:
success = downloader.download_playlist(current_url, quality, audio_only, output)
else:
success = downloader.download_video(current_url, quality, audio_only, output)
if success:
successful += 1
print(f"{Fore.GREEN}✓ [{i}/{len(urls_to_process)}] Успешно загружено{Style.RESET_ALL}")
else:
failed += 1
failed_urls.append((current_url, "Ошибка загрузки"))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Ошибка загрузки{Style.RESET_ALL}")
if not continue_on_error:
break
except Exception as e:
failed += 1
error_msg = str(e)[:100] + "..." if len(str(e)) > 100 else str(e)
failed_urls.append((current_url, error_msg))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Исключение: {error_msg}{Style.RESET_ALL}")
if not continue_on_error:
break
# Показ результатов
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if len(urls_to_process) > 1:
print(f"{Fore.GREEN}Пакетная загрузка завершена!{Style.RESET_ALL}")
print(f"{Fore.CYAN}Успешно: {successful}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Ошибок: {failed}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Всего: {len(urls_to_process)}{Style.RESET_ALL}")
if failed_urls:
print(f"\n{Fore.YELLOW}Неудачные загрузки:{Style.RESET_ALL}")
for url_item, error in failed_urls[:5]: # Показываем только первые 5
short_url = url_item[:50] + "..." if len(url_item) > 50 else url_item
print(f"{short_url}: {error}")
if len(failed_urls) > 5:
print(f" ... и еще {len(failed_urls) - 5} ошибок")
else:
if successful > 0:
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
else:
print(f"{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if failed > 0 and not continue_on_error:
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

View File

@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
def load_urls_from_file(file_path):
"""Загружает список URL из файла"""
urls = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
# Пропускаем пустые строки и комментарии
if line and not line.startswith('#'):
urls.append(line)
print(f"Загружено {len(urls)} URL из файла {file_path}")
return urls
except Exception as e:
print(f"{Fore.RED}Ошибка чтения файла {file_path}: {str(e)}{Style.RESET_ALL}")
return []
def show_video_info(downloader, url):
"""Показывает информацию о видео"""
video_info = downloader.get_video_info(url)
if video_info:
print(f"\n{Fore.GREEN}Информация о видео:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Название:{Style.RESET_ALL} {video_info['title']}")
print(f"{Fore.YELLOW}Автор:{Style.RESET_ALL} {video_info['uploader']}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.YELLOW}Длительность:{Style.RESET_ALL} {duration_str}")
if video_info['view_count']:
print(f"{Fore.YELLOW}Просмотров:{Style.RESET_ALL} {video_info['view_count']:,}")
if video_info['upload_date']:
date_str = video_info['upload_date']
formatted_date = f"{date_str[6:8]}.{date_str[4:6]}.{date_str[:4]}"
print(f"{Fore.YELLOW}Дата загрузки:{Style.RESET_ALL} {formatted_date}")
if video_info['description']:
desc = video_info['description'][:200] + "..." if len(video_info['description']) > 200 else video_info['description']
print(f"{Fore.YELLOW}Описание:{Style.RESET_ALL} {desc}")
def show_video_formats(downloader, url):
"""Показывает доступные форматы видео"""
print(f"\n{Fore.GREEN}Получение доступных форматов...{Style.RESET_ALL}")
available_formats = downloader.get_available_formats(url)
if available_formats:
print(f"\n{Fore.GREEN}Доступные форматы:{Style.RESET_ALL}")
for fmt in available_formats:
size_str = f" ({fmt['filesize']//1024//1024} MB)" if fmt['filesize'] else ""
print(f"{fmt['quality']} ({fmt['ext']}){size_str}")
else:
print(f"{Fore.RED}Не удалось получить информацию о форматах{Style.RESET_ALL}")
@click.command()
@click.argument('url', required=False)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
@click.option('--batch', '-b', default=None, type=click.Path(exists=True),
help='Файл со списком URL для пакетной загрузки')
@click.option('--urls', multiple=True,
help='Несколько URL через пробел')
@click.option('--continue-on-error', is_flag=True,
help='Продолжать загрузку при ошибках')
def main(url, quality, audio_only, output, playlist, info, formats, config, batch, urls, continue_on_error):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист (опционально при использовании --batch или --urls)
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Определяем список URL для обработки
urls_to_process = []
if batch:
# Пакетная загрузка из файла
print(f"{Fore.GREEN}Загрузка URL из файла: {batch}{Style.RESET_ALL}")
urls_to_process = load_urls_from_file(batch)
elif urls:
# Множественные URL из командной строки
urls_to_process = list(urls)
print(f"{Fore.GREEN}Обработка {len(urls_to_process)} URL из командной строки{Style.RESET_ALL}")
elif url:
# Один URL
urls_to_process = [url]
else:
print(f"{Fore.RED}Ошибка: Необходимо указать URL, --batch файл или --urls{Style.RESET_ALL}")
sys.exit(1)
# Обработка списка URL
if info or formats:
# Для info и formats обрабатываем только первый URL
process_single_url = urls_to_process[0] if urls_to_process else None
if not process_single_url:
print(f"{Fore.RED}Ошибка: Нет URL для обработки{Style.RESET_ALL}")
sys.exit(1)
if not downloader.validate_url(process_single_url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
if info:
show_video_info(downloader, process_single_url)
return
if formats:
show_video_formats(downloader, process_single_url)
return
# Пакетная загрузка
print(f"\n{Fore.GREEN}Начинаю обработку {len(urls_to_process)} URL...{Style.RESET_ALL}")
successful = 0
failed = 0
failed_urls = []
for i, current_url in enumerate(urls_to_process, 1):
print(f"\n{Fore.CYAN}[{i}/{len(urls_to_process)}] Обработка: {current_url}{Style.RESET_ALL}")
# Проверка URL
if not downloader.validate_url(current_url):
print(f"{Fore.RED}✗ Некорректный URL, пропускаем{Style.RESET_ALL}")
failed += 1
failed_urls.append((current_url, "Некорректный URL"))
if not continue_on_error:
break
continue
try:
success = False
if playlist:
success = downloader.download_playlist(current_url, quality, audio_only, output)
else:
success = downloader.download_video(current_url, quality, audio_only, output)
if success:
successful += 1
print(f"{Fore.GREEN}✓ [{i}/{len(urls_to_process)}] Успешно загружено{Style.RESET_ALL}")
else:
failed += 1
failed_urls.append((current_url, "Ошибка загрузки"))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Ошибка загрузки{Style.RESET_ALL}")
if not continue_on_error:
break
except Exception as e:
failed += 1
error_msg = str(e)[:100] + "..." if len(str(e)) > 100 else str(e)
failed_urls.append((current_url, error_msg))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Исключение: {error_msg}{Style.RESET_ALL}")
if not continue_on_error:
break
# Показ результатов
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if len(urls_to_process) > 1:
print(f"{Fore.GREEN}Пакетная загрузка завершена!{Style.RESET_ALL}")
print(f"{Fore.CYAN}Успешно: {successful}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Ошибок: {failed}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Всего: {len(urls_to_process)}{Style.RESET_ALL}")
if failed_urls:
print(f"\n{Fore.YELLOW}Неудачные загрузки:{Style.RESET_ALL}")
for url_item, error in failed_urls[:5]: # Показываем только первые 5
short_url = url_item[:50] + "..." if len(url_item) > 50 else url_item
print(f"{short_url}: {error}")
if len(failed_urls) > 5:
print(f" ... и еще {len(failed_urls) - 5} ошибок")
else:
if successful > 0:
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
else:
print(f"{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if failed > 0 and not continue_on_error:
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

View File

@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
def load_urls_from_file(file_path):
"""Загружает список URL из файла"""
urls = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
# Пропускаем пустые строки и комментарии
if line and not line.startswith('#'):
urls.append(line)
print(f"Загружено {len(urls)} URL из файла {file_path}")
return urls
except Exception as e:
print(f"{Fore.RED}Ошибка чтения файла {file_path}: {str(e)}{Style.RESET_ALL}")
return []
def show_video_info(downloader, url):
"""Показывает информацию о видео"""
video_info = downloader.get_video_info(url)
if video_info:
print(f"\n{Fore.GREEN}Информация о видео:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Название:{Style.RESET_ALL} {video_info['title']}")
print(f"{Fore.YELLOW}Автор:{Style.RESET_ALL} {video_info['uploader']}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.YELLOW}Длительность:{Style.RESET_ALL} {duration_str}")
if video_info['view_count']:
print(f"{Fore.YELLOW}Просмотров:{Style.RESET_ALL} {video_info['view_count']:,}")
if video_info['upload_date']:
date_str = video_info['upload_date']
formatted_date = f"{date_str[6:8]}.{date_str[4:6]}.{date_str[:4]}"
print(f"{Fore.YELLOW}Дата загрузки:{Style.RESET_ALL} {formatted_date}")
if video_info['description']:
desc = video_info['description'][:200] + "..." if len(video_info['description']) > 200 else video_info['description']
print(f"{Fore.YELLOW}Описание:{Style.RESET_ALL} {desc}")
def show_video_formats(downloader, url):
"""Показывает доступные форматы видео"""
print(f"\n{Fore.GREEN}Получение доступных форматов...{Style.RESET_ALL}")
available_formats = downloader.get_available_formats(url)
if available_formats:
print(f"\n{Fore.GREEN}Доступные форматы:{Style.RESET_ALL}")
for fmt in available_formats:
size_str = f" ({fmt['filesize']//1024//1024} MB)" if fmt['filesize'] else ""
print(f"{fmt['quality']} ({fmt['ext']}){size_str}")
else:
print(f"{Fore.RED}Не удалось получить информацию о форматах{Style.RESET_ALL}")
@click.command()
@click.argument('url', required=False)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
@click.option('--batch', '-b', default=None, type=click.Path(exists=True),
help='Файл со списком URL для пакетной загрузки')
@click.option('--urls', multiple=True,
help='Несколько URL через пробел')
@click.option('--continue-on-error', is_flag=True,
help='Продолжать загрузку при ошибках')
def main(url, quality, audio_only, output, playlist, info, formats, config, batch, urls, continue_on_error):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист (опционально при использовании --batch или --urls)
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Определяем список URL для обработки
urls_to_process = []
if batch:
# Пакетная загрузка из файла
print(f"{Fore.GREEN}Загрузка URL из файла: {batch}{Style.RESET_ALL}")
urls_to_process = load_urls_from_file(batch)
elif urls:
# Множественные URL из командной строки
urls_to_process = list(urls)
print(f"{Fore.GREEN}Обработка {len(urls_to_process)} URL из командной строки{Style.RESET_ALL}")
elif url:
# Один URL
urls_to_process = [url]
else:
print(f"{Fore.RED}Ошибка: Необходимо указать URL, --batch файл или --urls{Style.RESET_ALL}")
sys.exit(1)
# Обработка списка URL
if info or formats:
# Для info и formats обрабатываем только первый URL
process_single_url = urls_to_process[0] if urls_to_process else None
if not process_single_url:
print(f"{Fore.RED}Ошибка: Нет URL для обработки{Style.RESET_ALL}")
sys.exit(1)
if not downloader.validate_url(process_single_url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
if info:
show_video_info(downloader, process_single_url)
return
if formats:
show_video_formats(downloader, process_single_url)
return
# Пакетная загрузка
print(f"\n{Fore.GREEN}Начинаю обработку {len(urls_to_process)} URL...{Style.RESET_ALL}")
successful = 0
failed = 0
failed_urls = []
for i, current_url in enumerate(urls_to_process, 1):
print(f"\n{Fore.CYAN}[{i}/{len(urls_to_process)}] Обработка: {current_url}{Style.RESET_ALL}")
# Проверка URL
if not downloader.validate_url(current_url):
print(f"{Fore.RED}✗ Некорректный URL, пропускаем{Style.RESET_ALL}")
failed += 1
failed_urls.append((current_url, "Некорректный URL"))
if not continue_on_error:
break
continue
try:
success = False
if playlist:
success = downloader.download_playlist(current_url, quality, audio_only, output)
else:
success = downloader.download_video(current_url, quality, audio_only, output)
if success:
successful += 1
print(f"{Fore.GREEN}✓ [{i}/{len(urls_to_process)}] Успешно загружено{Style.RESET_ALL}")
else:
failed += 1
failed_urls.append((current_url, "Ошибка загрузки"))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Ошибка загрузки{Style.RESET_ALL}")
if not continue_on_error:
break
except Exception as e:
failed += 1
error_msg = str(e)[:100] + "..." if len(str(e)) > 100 else str(e)
failed_urls.append((current_url, error_msg))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Исключение: {error_msg}{Style.RESET_ALL}")
if not continue_on_error:
break
# Показ результатов
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if len(urls_to_process) > 1:
print(f"{Fore.GREEN}Пакетная загрузка завершена!{Style.RESET_ALL}")
print(f"{Fore.CYAN}Успешно: {successful}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Ошибок: {failed}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Всего: {len(urls_to_process)}{Style.RESET_ALL}")
if failed_urls:
print(f"\n{Fore.YELLOW}Неудачные загрузки:{Style.RESET_ALL}")
for url_item, error in failed_urls[:5]: # Показываем только первые 5
short_url = url_item[:50] + "..." if len(url_item) > 50 else url_item
print(f"{short_url}: {error}")
if len(failed_urls) > 5:
print(f" ... и еще {len(failed_urls) - 5} ошибок")
else:
if successful > 0:
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
else:
print(f"{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if failed > 0 and not continue_on_error:
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

View File

@@ -0,0 +1,4 @@
yt-dlp==2023.12.30
click==8.1.7
tqdm==4.66.1
colorama==0.4.6

View File

@@ -0,0 +1,4 @@
yt-dlp==2023.12.30
click==8.1.7
tqdm==4.66.1
colorama==0.4.6

View File

@@ -0,0 +1,6 @@
yt-dlp>=2024.1.1
click==8.1.7
tqdm==4.66.1
colorama==0.4.6
requests>=2.31.0
fake-useragent>=1.4.0

View File

@@ -0,0 +1,6 @@
yt-dlp>=2024.1.1
click==8.1.7
tqdm==4.66.1
colorama==0.4.6
requests>=2.31.0
fake-useragent>=1.4.0

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# YouTube Downloader Quick Start Script
echo "🎬 YouTube Downloader - Быстрый старт"
echo "========================================"
# Проверка Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python3 не найден. Установите Python 3.7 или новее."
exit 1
fi
# Проверка зависимостей
if ! python3 -c "import yt_dlp" 2>/dev/null; then
echo "📦 Установка зависимостей..."
pip3 install -r requirements.txt
fi
# Создание папки загрузок
mkdir -p downloads
echo ""
echo "✅ Готово! Доступные команды:"
echo ""
echo "📹 Скачать видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID"
echo ""
echo "🎵 Только аудио:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only"
echo ""
echo "📋 Информация о видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info"
echo ""
echo "⚙️ Настройка:"
echo " python3 main.py configure"
echo ""
echo "📚 Справка:"
echo " python3 main.py --help"
echo ""
echo "🛠 Make команды:"
echo " make usage"
echo ""
# Тест функциональности
echo "🧪 Тест системы..."
if python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation:', d.validate_url('https://www.youtube.com/watch?v=test'))" 2>/dev/null; then
echo "✅ Система работает корректно!"
else
echo "❌ Проблемы с системой. Проверьте зависимости."
fi
echo ""
echo "🎯 Пример использования:"
echo " python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info"

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# YouTube Downloader Quick Start Script
echo "🎬 YouTube Downloader - Быстрый старт"
echo "========================================"
# Проверка Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python3 не найден. Установите Python 3.7 или новее."
exit 1
fi
# Проверка зависимостей
if ! python3 -c "import yt_dlp" 2>/dev/null; then
echo "📦 Установка зависимостей..."
pip3 install -r requirements.txt
fi
# Создание папки загрузок
mkdir -p downloads
echo ""
echo "✅ Готово! Доступные команды:"
echo ""
echo "📹 Скачать видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID"
echo ""
echo "🎵 Только аудио:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only"
echo ""
echo "📋 Информация о видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info"
echo ""
echo "⚙️ Настройка:"
echo " python3 main.py configure"
echo ""
echo "📚 Справка:"
echo " python3 main.py --help"
echo ""
echo "🛠 Make команды:"
echo " make usage"
echo ""
# Тест функциональности
echo "🧪 Тест системы..."
if python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation:', d.validate_url('https://www.youtube.com/watch?v=test'))" 2>/dev/null; then
echo "✅ Система работает корректно!"
else
echo "❌ Проблемы с системой. Проверьте зависимости."
fi
echo ""
echo "🎯 Пример использования:"
echo " python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info"

View File

@@ -0,0 +1,60 @@
#!/bin/bash
# YouTube Downloader Quick Start Script
echo "🎬 YouTube Downloader - Быстрый старт"
echo "========================================"
# Проверка Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python3 не найден. Установите Python 3.7 или новее."
exit 1
fi
# Проверка зависимостей
if ! python3 -c "import yt_dlp" 2>/dev/null; then
echo "📦 Установка зависимостей..."
pip3 install -r requirements.txt
fi
# Проверка ffmpeg
if ! command -v ffmpeg &> /dev/null; then
echo "⚠️ ffmpeg не найден. Для конвертации аудио в MP3 установите ffmpeg:"
echo " ./install_ffmpeg.sh"
fi
# Создание папки загрузок
mkdir -p downloads
echo ""
echo "✅ Готово! Доступные команды:"
echo ""
echo "📹 Скачать видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID"
echo ""
echo "🎵 Только аудио:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only"
echo ""
echo "📋 Информация о видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info"
echo ""
echo "⚙️ Настройка:"
echo " python3 main.py configure"
echo ""
echo "📚 Справка:"
echo " python3 main.py --help"
echo ""
echo "🛠 Make команды:"
echo " make usage"
echo ""
# Тест функциональности
echo "🧪 Тест системы..."
if python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation:', d.validate_url('https://www.youtube.com/watch?v=test'))" 2>/dev/null; then
echo "✅ Система работает корректно!"
else
echo "❌ Проблемы с системой. Проверьте зависимости."
fi
echo ""
echo "🎯 Пример использования:"
echo " python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info"

View File

@@ -0,0 +1,60 @@
#!/bin/bash
# YouTube Downloader Quick Start Script
echo "🎬 YouTube Downloader - Быстрый старт"
echo "========================================"
# Проверка Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python3 не найден. Установите Python 3.7 или новее."
exit 1
fi
# Проверка зависимостей
if ! python3 -c "import yt_dlp" 2>/dev/null; then
echo "📦 Установка зависимостей..."
pip3 install -r requirements.txt
fi
# Проверка ffmpeg
if ! command -v ffmpeg &> /dev/null; then
echo "⚠️ ffmpeg не найден. Для конвертации аудио в MP3 установите ffmpeg:"
echo " ./install_ffmpeg.sh"
fi
# Создание папки загрузок
mkdir -p downloads
echo ""
echo "✅ Готово! Доступные команды:"
echo ""
echo "📹 Скачать видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID"
echo ""
echo "🎵 Только аудио:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only"
echo ""
echo "📋 Информация о видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info"
echo ""
echo "⚙️ Настройка:"
echo " python3 main.py configure"
echo ""
echo "📚 Справка:"
echo " python3 main.py --help"
echo ""
echo "🛠 Make команды:"
echo " make usage"
echo ""
# Тест функциональности
echo "🧪 Тест системы..."
if python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation:', d.validate_url('https://www.youtube.com/watch?v=test'))" 2>/dev/null; then
echo "✅ Система работает корректно!"
else
echo "❌ Проблемы с системой. Проверьте зависимости."
fi
echo ""
echo "🎯 Пример использования:"
echo " python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info"

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 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(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()

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()

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()

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(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()

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(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()

125
BATCH_GUIDE.md Normal file
View File

@@ -0,0 +1,125 @@
# 📋 Руководство по пакетной загрузке YouTube Downloader
## 🎯 Способы пакетной загрузки
### 1. Загрузка из файла со списком URL
Создайте текстовый файл с URL (один на строку):
```bash
# Создать шаблон
python3 utils.py template batch_urls.txt
# Отредактировать файл и добавить URL
nano batch_urls.txt
# Загрузить все URL из файла
python3 main.py --batch batch_urls.txt --audio-only
```
**Пример содержимого файла:**
```
# Мои любимые видео
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=VIDEO_ID_2
https://www.youtube.com/watch?v=VIDEO_ID_3
# Плейлист (используйте с --playlist)
https://www.youtube.com/playlist?list=PLAYLIST_ID
```
### 2. Загрузка нескольких URL через командную строку
```bash
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 🛠 Опции для пакетной загрузки
### Основные параметры:
- `--batch FILE` - Файл со списком URL
- `--urls URL1 URL2...` - Несколько URL через пробел
- `--continue-on-error` - Продолжать при ошибках
- `--quality QUALITY` - Качество для всех видео
- `--audio-only` - Только аудио для всех
- `--output DIR` - Папка сохранения
### Примеры команд:
```bash
# Скачать аудио из файла, продолжать при ошибках
python3 main.py --batch my_urls.txt --audio-only --continue-on-error
# Скачать несколько видео в 720p
python3 main.py --urls URL1 URL2 URL3 --quality 720p
# Скачать плейлист из файла
python3 main.py --batch playlists.txt --playlist
# Показать информацию о первом URL из файла
python3 main.py --batch urls.txt --info
# Сохранить в определенную папку
python3 main.py --batch urls.txt --output /path/to/folder
```
## 📊 Отчеты о загрузке
После пакетной загрузки отображается:
- ✅ Количество успешных загрузок
- ❌ Количество ошибок
- 📋 Список неудачных URL (до 5 первых)
- 📁 Путь к сохраненным файлам
## 🔧 Make команды для тестирования
```bash
make create-batch-template # Создать шаблон
make test-batch # Тест с показом info
make demo-batch # Демо загрузки аудио
```
## 💡 Советы и рекомендации
### Формат файла со списком URL:
- Один URL на строку
- Строки с `#` - комментарии (игнорируются)
- Пустые строки игнорируются
- UTF-8 кодировка
### Обработка ошибок:
- По умолчанию остановка на первой ошибке
- `--continue-on-error` - продолжить загрузку других URL
- Подробные сообщения об ошибках в выводе
### Производительность:
- Загрузка по одному URL (последовательно)
- Автоматические задержки между запросами
- Fallback стратегии при блокировках
## ❗ Важные моменты
1. **Лимиты YouTube**: Не злоупотребляйте пакетной загрузкой
2. **Права авторов**: Соблюдайте авторские права
3. **Дисковое пространство**: Контролируйте объем загрузок
4. **Сетевая нагрузка**: Учитывайте трафик интернета
## 🐛 Troubleshooting
**Проблема**: Некоторые URL не загружаются
```bash
# Решение: включить продолжение при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
**Проблема**: Медленная загрузка
```bash
# Решение: попробовать более низкое качество
python3 main.py --batch urls.txt --quality 480p
```
**Проблема**: Ошибки 403
```bash
# Решение: приложение автоматически применит fallback стратегии
# Просто дождитесь повторных попыток
```

77
Makefile Normal file
View File

@@ -0,0 +1,77 @@
# Makefile for YouTube Downloader
.PHONY: install run-examples help clean test config update-ytdlp
# Установка зависимостей
install:
pip3 install -r requirements.txt
# Обновление yt-dlp до последней версии
update-ytdlp:
pip3 install --upgrade yt-dlp
# Установка с обновлением
install-fresh: update-ytdlp install
# Запуск примеров
run-examples:
python3 examples.py
# Показать справку
help:
python3 main.py --help
# Очистка временных файлов
clean:
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
rm -f config.json
# Тестирование URL валидации
test:
python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation test:', d.validate_url('https://www.youtube.com/watch?v=dQw4w9WgXcQ'))"
# Настройка конфигурации по умолчанию
config:
python3 main.py configure --output-dir downloads --video-quality best --audio-format mp3
# Показать текущую конфигурацию
show-config:
python3 main.py show-config
# Тестирование пакетной загрузки
test-batch:
python3 main.py --batch batch_urls.txt --info
# Демо пакетной загрузки
demo-batch:
python3 main.py --batch batch_urls.txt --audio-only --continue-on-error
# Создать шаблон для пакетной загрузки
create-batch-template:
python3 utils.py template batch_template.txt
# Все команды
all: install config run-examples
# Все команды
usage:
@echo "Доступные команды:"
@echo " install - Установить зависимости"
@echo " install-fresh - Установить с обновлением yt-dlp"
@echo " update-ytdlp - Обновить yt-dlp до последней версии"
@echo " run-examples - Запустить примеры"
@echo " help - Показать справку по программе"
@echo " config - Настроить конфигурацию по умолчанию"
@echo " show-config - Показать текущую конфигурацию"
@echo " test - Простой тест валидации URL"
@echo " test-batch - Тест пакетной загрузки (только info)"
@echo " demo-batch - Демо пакетной загрузки аудио"
@echo " create-batch-template - Создать шаблон для пакетной загрузки"
@echo " clean - Очистить временные файлы"
@echo " install-ffmpeg - Установить ffmpeg для аудио конвертации"
@echo " all - Установить все и запустить примеры"
# Установка ffmpeg
install-ffmpeg:
./install_ffmpeg.sh

261
README.md Normal file
View File

@@ -0,0 +1,261 @@
# YouTube Downloader
Мощное приложение для скачивания видео с YouTube с использованием Python и yt-dlp.
## 🚀 Возможности
- ✅ Скачивание видео в различных разрешениях (360p, 480p, 720p, 1080p, best)
- ✅ Скачивание только аудио в формате MP3/AAC
- ✅ Поддержка плейлистов
-**Пакетная загрузка из файла со списком URL**
-**Загрузка нескольких URL через командную строку**
-**Продолжение загрузки при ошибках (--continue-on-error)**
- ✅ Прогресс-бар загрузки с цветным выводом
- ✅ Настраиваемая конфигурация
- ✅ Валидация URL
- ✅ История загрузок
- ✅ CLI интерфейс с множеством опций
- ✅ Поддержка субтитров
- ✅ Автоматическое исправление ошибок 403/блокировок
## 📦 Установка
1. Клонируйте репозиторий:
```bash
git clone <your-repo-url>
cd youtube_downloader
```
2. Установите зависимости:
```bash
make install-fresh
# или
pip3 install -r requirements.txt
```
3. Установите ffmpeg (для конвертации аудио):
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: скачайте с https://ffmpeg.org/
```
## 🎯 Быстрый старт
```bash
# Настройка конфигурации
make config
# Скачать видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Пакетная загрузка из файла
python3 main.py --batch batch_urls.txt --audio-only
# Загрузка нескольких URL
python3 main.py --urls URL1 URL2 URL3 --quality 720p
```
## 📋 Использование CLI
### Основные команды:
```bash
# Скачать видео в лучшем качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID
# Скачать в определенном качестве
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p
# Скачать только аудио
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only
# Указать папку для загрузки
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --output downloads/
# Скачать плейлист
python3 main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist
# Показать информацию о видео
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info
# Показать доступные форматы
python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --formats
```
### Пакетная загрузка:
```bash
# Создать шаблон файла со списком URL
make create-batch-template
# Загрузка из файла со списком URL
python3 main.py --batch urls.txt --quality 720p --continue-on-error
# Загрузка нескольких URL через командную строку
python3 main.py --urls URL1 URL2 URL3 --audio-only
# Продолжить загрузку при ошибках
python3 main.py --batch urls.txt --continue-on-error
```
### Конфигурация:
```bash
# Настроить параметры по умолчанию
python3 main.py configure --output-dir downloads --video-quality 720p
# Показать текущие настройки
python3 main.py show-config
```
## 🔧 Дополнительные утилиты
```bash
# Создать шаблон для пакетной загрузки
python3 utils.py template
# Пакетная загрузка из файла
python3 utils.py batch --file urls.txt --quality 720p
# Показать историю загрузок
python3 utils.py history
# Очистить папку загрузок
python3 utils.py clean
```
## 🛠 Make команды
```bash
make install-fresh # Установить с обновлением yt-dlp
make install-ffmpeg # Установить ffmpeg для аудио
make config # Настроить конфигурацию по умолчанию
make show-config # Показать текущие настройки
make update-ytdlp # Обновить yt-dlp до последней версии
make test # Простой тест валидации URL
make demo-info # Информация о тестовом видео
make demo-formats # Форматы тестового видео
make run-examples # Запустить примеры
make clean # Очистить временные файлы
make usage # Показать справку по Makefile
```
## 📁 Структура проекта
```
youtube_downloader/
├── main.py # Основное CLI приложение
├── downloader.py # Класс загрузчика YouTube
├── config.py # Управление конфигурацией
├── utils.py # Дополнительные утилиты
├── examples.py # Примеры использования
├── requirements.txt # Зависимости Python
├── Makefile # Автоматизация задач
├── README.md # Документация
├── config.json # Файл конфигурации (создается автоматически)
└── urls.txt # Шаблон для пакетной загрузки
```
## ⚙ Конфигурация
Настройки сохраняются в `config.json`:
```json
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": ["ru", "en"],
"download_subtitles": false
}
```
## 📝 Примеры
### Базовое использование в коде:
```python
from downloader import YouTubeDownloader
from config import Config
# Создание загрузчика
config = Config()
downloader = YouTubeDownloader(config)
# Загрузка видео
success = downloader.download_video(
url="https://www.youtube.com/watch?v=VIDEO_ID",
quality="720p",
audio_only=False
)
```
### Пакетная загрузка:
```python
from utils import BatchDownloader
batch = BatchDownloader()
batch.download_from_file("urls.txt", quality="best", audio_only=False)
```
## 🚨 Требования
- Python 3.7+
- Интернет соединение
- ffmpeg (для конвертации аудио)
## 🔍 Troubleshooting
### Ошибки yt-dlp
Если возникают ошибки с yt-dlp, обновите до последней версии:
```bash
make update-ytdlp
# или
pip3 install --upgrade yt-dlp
```
### HTTP Error 403: Forbidden
Приложение автоматически использует несколько стратегий обхода блокировок:
- Альтернативные User-Agent заголовки
- Повторные попытки с задержками
- Различные форматы загрузки
- Fallback настройки
### Проблемы с ffmpeg
Установите ffmpeg для конвертации аудио:
```bash
make install-ffmpeg
# или вручную:
# Ubuntu/Debian: sudo apt install ffmpeg
# macOS: brew install ffmpeg
# Windows: Скачайте с https://ffmpeg.org/
```
### Медленная загрузка
- Попробуйте скачать в более низком качестве: `--quality 480p`
- Используйте только аудио: `--audio-only`
- Проверьте интернет соединение
## 📄 Лицензия
Проект создан для образовательных целей. Соблюдайте условия использования YouTube.
## 🤝 Вклад в проект
1. Форкните репозиторий
2. Создайте ветку для новой функции
3. Сделайте изменения
4. Отправьте Pull Request
---
**Автор:** GitHub Copilot
**Версия:** 1.0.0

14
batch_urls.txt Normal file
View File

@@ -0,0 +1,14 @@
# Пример файла со списком URL для пакетной загрузки YouTube Downloader
# Каждый URL должен быть на отдельной строке
# Строки, начинающиеся с #, являются комментариями и игнорируются
# Примеры видео для тестирования:
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://www.youtube.com/watch?v=etuo7AA6rmY
# Можно добавить больше URL:
# https://www.youtube.com/watch?v=ANOTHER_VIDEO_ID
# https://www.youtube.com/watch?v=YET_ANOTHER_VIDEO_ID
# Плейлисты тоже поддерживаются (используйте --playlist):
# https://www.youtube.com/playlist?list=PLAYLIST_ID

13
config.json Normal file
View File

@@ -0,0 +1,13 @@
{
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": true,
"add_metadata": true,
"subtitle_languages": [
"ru",
"en"
],
"download_subtitles": false
}

55
config.py Normal file
View File

@@ -0,0 +1,55 @@
import os
import json
class Config:
"""Класс для управления конфигурацией приложения"""
DEFAULT_CONFIG = {
"output_directory": "downloads",
"video_quality": "best",
"audio_format": "mp3",
"video_format": "mp4",
"create_subdirs": True,
"add_metadata": True,
"subtitle_languages": ["ru", "en"],
"download_subtitles": False
}
def __init__(self, config_file="config.json"):
self.config_file = config_file
self.config = self.load_config()
def load_config(self):
"""Загружает конфигурацию из файла или создает стандартную"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return {**self.DEFAULT_CONFIG, **json.load(f)}
except (json.JSONDecodeError, IOError):
print(f"Ошибка чтения конфигурации из {self.config_file}. Используется стандартная конфигурация.")
return self.DEFAULT_CONFIG.copy()
def save_config(self):
"""Сохраняет текущую конфигурацию в файл"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=4, ensure_ascii=False)
except IOError as e:
print(f"Ошибка сохранения конфигурации: {e}")
def get(self, key, default=None):
"""Получает значение конфигурации"""
return self.config.get(key, default)
def set(self, key, value):
"""Устанавливает значение конфигурации"""
self.config[key] = value
def create_output_directory(self):
"""Создает папку для загрузки, если она не существует"""
output_dir = self.get("output_directory", "downloads")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка для загрузок: {output_dir}")
return output_dir

299
downloader.py Normal file
View File

@@ -0,0 +1,299 @@
import os
import re
import yt_dlp
import time
import random
from tqdm import tqdm
from colorama import Fore, Style, init
from config import Config
from fake_useragent import UserAgent
# Инициализируем colorama для кроссплатформенной работы с цветами
init(autoreset=True)
class YouTubeDownloader:
"""Основной класс для скачивания видео с YouTube"""
def __init__(self, config=None):
self.config = config or Config()
self.progress_bar = None
def validate_url(self, url):
"""Проверяет корректность YouTube URL"""
youtube_regex = re.compile(
r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/'
r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})'
)
return youtube_regex.match(url) is not None
def progress_hook(self, d):
"""Хук для отображения прогресса загрузки"""
if d['status'] == 'downloading':
if self.progress_bar is None:
total_bytes = d.get('total_bytes') or d.get('total_bytes_estimate')
if total_bytes:
self.progress_bar = tqdm(
total=total_bytes,
unit='B',
unit_scale=True,
desc=f"{Fore.BLUE}Скачивание{Style.RESET_ALL}"
)
if self.progress_bar and 'downloaded_bytes' in d:
downloaded = d['downloaded_bytes']
# Используем hasattr для проверки custom атрибута
if hasattr(self.progress_bar, 'last_downloaded'):
self.progress_bar.update(downloaded - getattr(self.progress_bar, 'last_downloaded', 0))
else:
self.progress_bar.update(downloaded)
# Устанавливаем custom атрибут
setattr(self.progress_bar, 'last_downloaded', downloaded)
elif d['status'] == 'finished':
if self.progress_bar:
self.progress_bar.close()
self.progress_bar = None
print(f"{Fore.GREEN}✓ Загрузка завершена: {d['filename']}{Style.RESET_ALL}")
def get_video_info(self, url):
"""Получает информацию о видео без загрузки"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
return {
'title': info.get('title', 'Неизвестное название'),
'duration': info.get('duration', 0),
'uploader': info.get('uploader', 'Неизвестный автор'),
'view_count': info.get('view_count', 0),
'upload_date': info.get('upload_date', ''),
'description': info.get('description', ''),
'formats': info.get('formats', [])
}
except Exception as e:
print(f"{Fore.RED}Ошибка получения информации о видео: {str(e)}{Style.RESET_ALL}")
return None
def download_video(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает видео по URL"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
return False
# Получаем информацию о видео
video_info = self.get_video_info(url)
if not video_info:
return False
print(f"{Fore.CYAN}Название: {video_info['title']}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Автор: {video_info['uploader']}{Style.RESET_ALL}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.CYAN}Длительность: {duration_str}{Style.RESET_ALL}")
# Определяем папку для загрузки
output_path = output_dir or self.config.create_output_directory()
# Настройки для yt-dlp с улучшенным обходом блокировок
ua = UserAgent()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'writesubtitles': self.config.get('download_subtitles', False),
'writeautomaticsub': self.config.get('download_subtitles', False),
# Настройки для обхода блокировок
'http_headers': {
'User-Agent': ua.random,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
},
'extractor_retries': 3,
'file_access_retries': 3,
'fragment_retries': 3,
'retry_sleep_functions': {
'http': lambda n: min(4 * 2**n, 30),
'fragment': lambda n: min(4 * 2**n, 30),
'file_access': lambda n: min(4 * 2**n, 30),
},
'sleep_interval_requests': 1,
'sleep_interval': 0,
'max_sleep_interval': 5,
}
# Настройки качества и формата
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
print(f"{Fore.YELLOW}Режим: только аудио ({self.config.get('audio_format', 'mp3')}){Style.RESET_ALL}")
else:
if quality == 'best':
format_string = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
format_string = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
format_string = 'best[ext=mp4]/best'
ydl_opts['format'] = format_string
print(f"{Fore.YELLOW}Качество: {quality}{Style.RESET_ALL}")
# Загрузка субтитров
if self.config.get('download_subtitles', False):
ydl_opts['subtitleslangs'] = self.config.get('subtitle_languages', ['ru', 'en'])
try:
# Первая попытка с обычными настройками
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
print(f"{Fore.BLUE}Начинаю загрузку...{Style.RESET_ALL}")
ydl.download([url])
return True
except Exception as e:
error_msg = str(e)
print(f"{Fore.YELLOW}Первая попытка неудачна: {error_msg}{Style.RESET_ALL}")
# Если ошибка 403 или блокировка, пробуем альтернативные настройки
if "403" in error_msg or "Forbidden" in error_msg or "throttl" in error_msg or "HTTP Error" in error_msg:
return self._retry_download_with_fallback(url, ydl_opts)
else:
print(f"{Fore.RED}Ошибка загрузки: {error_msg}{Style.RESET_ALL}")
return False
def _retry_download_with_fallback(self, url, base_opts):
"""Повторная попытка загрузки с альтернативными настройками"""
fallback_strategies = [
{
'name': 'Альтернативный User-Agent',
'opts': {
**base_opts,
'http_headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
}
},
{
'name': 'Без метаданных',
'opts': {
**{k: v for k, v in base_opts.items() if k not in ['writeinfojson', 'writesubtitles', 'writeautomaticsub']},
'writeinfojson': False,
'writesubtitles': False,
'writeautomaticsub': False
}
},
{
'name': 'Только аудио формат',
'opts': {
**base_opts,
'format': 'bestaudio[ext=m4a]/bestaudio/best[height<=480]'
}
}
]
for i, strategy in enumerate(fallback_strategies, 1):
print(f"{Fore.CYAN}Попытка {i+1}: {strategy['name']}{Style.RESET_ALL}")
try:
# Добавляем задержку между попытками
time.sleep(random.uniform(2, 5))
with yt_dlp.YoutubeDL(strategy['opts']) as ydl:
ydl.download([url])
print(f"{Fore.GREEN}✓ Успешно загружено с настройкой: {strategy['name']}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{strategy['name']}: {str(e)}{Style.RESET_ALL}")
continue
print(f"{Fore.RED}Все попытки загрузки неудачны{Style.RESET_ALL}")
return False
def download_playlist(self, url, quality='best', audio_only=False, output_dir=None):
"""Скачивает плейлист"""
if not self.validate_url(url):
print(f"{Fore.RED}Ошибка: Некорректный URL{Style.RESET_ALL}")
return False
output_path = output_dir or self.config.create_output_directory()
ydl_opts = {
'outtmpl': os.path.join(str(output_path), '%(playlist_title)s/%(playlist_index)02d - %(title)s.%(ext)s'),
'progress_hooks': [self.progress_hook],
'writeinfojson': self.config.get('add_metadata', True),
'noplaylist': False, # Включаем загрузку плейлиста
}
# Настройки для аудио или видео
if audio_only:
ydl_opts.update({
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': self.config.get('audio_format', 'mp3'),
'preferredquality': '192',
}],
})
else:
if quality == 'best':
ydl_opts['format'] = 'best[ext=mp4]/best'
elif quality.endswith('p'):
height = quality[:-1]
ydl_opts['format'] = f'best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best'
else:
ydl_opts['format'] = 'best[ext=mp4]/best'
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
# Получаем информацию о плейлисте
playlist_info = ydl.extract_info(url, download=False)
if 'entries' in playlist_info:
print(f"{Fore.CYAN}Плейлист: {playlist_info.get('title', 'Неизвестный плейлист')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Количество видео: {len(playlist_info['entries'])}{Style.RESET_ALL}")
print(f"{Fore.BLUE}Начинаю загрузку плейлиста...{Style.RESET_ALL}")
ydl.download([url])
return True
else:
# Это одиночное видео, а не плейлист
return self.download_video(url, quality, audio_only, output_dir)
except Exception as e:
print(f"{Fore.RED}Ошибка загрузки плейлиста: {str(e)}{Style.RESET_ALL}")
return False
def get_available_formats(self, url):
"""Получает список доступных форматов для видео"""
try:
with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
info = ydl.extract_info(url, download=False)
formats = []
if info and isinstance(info, dict) and 'formats' in info:
formats_list = info.get('formats')
if formats_list:
for f in formats_list:
if f.get('height'): # Только видео форматы
formats.append({
'quality': f"{f.get('height')}p",
'ext': f.get('ext', ''),
'filesize': f.get('filesize', 0),
'format_id': f.get('format_id', '')
})
return formats
except Exception as e:
print(f"{Fore.RED}Ошибка получения форматов: {str(e)}{Style.RESET_ALL}")
return []

126
examples.py Normal file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python3
"""
Примеры использования YouTube Downloader
"""
from downloader import YouTubeDownloader
from config import Config
from colorama import Fore, Style
def example_download_video():
"""Пример загрузки одного видео"""
print(f"{Fore.CYAN}=== Пример загрузки видео ==={Style.RESET_ALL}")
# URL тестового видео (короткое видео с YouTube)
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # Rick Roll для теста
config = Config()
downloader = YouTubeDownloader(config)
# Получение информации о видео
print(f"{Fore.YELLOW}Получение информации о видео...{Style.RESET_ALL}")
info = downloader.get_video_info(url)
if info:
print(f"Название: {info['title']}")
print(f"Автор: {info['uploader']}")
print(f"Длительность: {info['duration']} сек")
# Показать доступные форматы
print(f"\n{Fore.YELLOW}Доступные форматы:{Style.RESET_ALL}")
formats = downloader.get_available_formats(url)
for fmt in formats[:5]: # Показываем только первые 5 форматов
print(f"{fmt['quality']} ({fmt['ext']})")
return url
def example_download_audio():
"""Пример загрузки только аудио"""
print(f"\n{Fore.CYAN}=== Пример загрузки аудио ==={Style.RESET_ALL}")
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
config = Config()
downloader = YouTubeDownloader(config)
print("Для загрузки только аудио используйте параметр audio_only=True")
print("Пример: downloader.download_video(url, audio_only=True)")
def show_cli_examples():
"""Показать примеры использования CLI"""
print(f"\n{Fore.CYAN}=== Примеры использования CLI ==={Style.RESET_ALL}")
examples = [
"# Скачать видео в лучшем качестве",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID",
"",
"# Скачать видео в 720p",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --quality 720p",
"",
"# Скачать только аудио",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only",
"",
"# Скачать в определенную папку",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --output /path/to/downloads/",
"",
"# Скачать плейлист",
"python main.py https://www.youtube.com/playlist?list=PLAYLIST_ID --playlist",
"",
"# Показать информацию о видео",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --info",
"",
"# Показать доступные форматы",
"python main.py https://www.youtube.com/watch?v=VIDEO_ID --formats",
"",
"# Настроить конфигурацию по умолчанию",
"python main.py configure --output-dir downloads --video-quality 720p",
"",
"# Показать текущие настройки",
"python main.py show-config"
]
for example in examples:
if example.startswith("#"):
print(f"{Fore.GREEN}{example}{Style.RESET_ALL}")
elif example == "":
print()
else:
print(f"{Fore.WHITE}{example}{Style.RESET_ALL}")
def test_url_validation():
"""Тест валидации URL"""
print(f"\n{Fore.CYAN}=== Тест валидации URL ==={Style.RESET_ALL}")
downloader = YouTubeDownloader()
test_urls = [
("https://www.youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://youtu.be/dQw4w9WgXcQ", True),
("https://youtube.com/watch?v=dQw4w9WgXcQ", True),
("https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMt6VEY", True),
("https://example.com/video", False),
("not_a_url", False),
]
for url, expected in test_urls:
result = downloader.validate_url(url)
status = "" if result == expected else ""
color = Fore.GREEN if result == expected else Fore.RED
print(f"{color}{status} {url} -> {result}{Style.RESET_ALL}")
if __name__ == "__main__":
print(f"{Fore.CYAN}YouTube Downloader - Примеры использования{Style.RESET_ALL}")
print("=" * 60)
# Тест валидации URL
test_url_validation()
# Примеры загрузки
test_url = example_download_video()
example_download_audio()
# Примеры CLI
show_cli_examples()
print(f"\n{Fore.GREEN}Все примеры показаны. Для реальной загрузки используйте main.py{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Для начала работы установите зависимости: pip install -r requirements.txt{Style.RESET_ALL}")

63
install_ffmpeg.sh Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
# Скрипт установки ffmpeg для YouTube Downloader
echo "🎬 Установка ffmpeg для YouTube Downloader"
echo "=========================================="
# Определение ОС
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "🐧 Linux обнаружен"
# Проверка наличия apt
if command -v apt &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через apt..."
sudo apt update && sudo apt install -y ffmpeg
# Проверка наличия yum
elif command -v yum &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через yum..."
sudo yum install -y ffmpeg
# Проверка наличия dnf
elif command -v dnf &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через dnf..."
sudo dnf install -y ffmpeg
else
echo "❌ Менеджер пакетов не найден. Установите ffmpeg вручную."
exit 1
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "🍎 macOS обнаружен"
if command -v brew &> /dev/null; then
echo "📦 Устанавливаю ffmpeg через Homebrew..."
brew install ffmpeg
else
echo "❌ Homebrew не найден. Установите его сначала:"
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
exit 1
fi
else
echo "❓ Неизвестная ОС: $OSTYPE"
echo "Установите ffmpeg вручную:"
echo " - Windows: Скачайте с https://ffmpeg.org/"
echo " - Linux: sudo apt install ffmpeg"
echo " - macOS: brew install ffmpeg"
exit 1
fi
# Проверка установки
if command -v ffmpeg &> /dev/null; then
echo "✅ ffmpeg успешно установлен!"
ffmpeg -version | head -1
echo ""
echo "🎯 Теперь можно скачивать аудио в MP3:"
echo " python3 main.py URL --audio-only"
else
echo "❌ Ошибка установки ffmpeg"
exit 1
fi

261
main.py Normal file
View File

@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""
YouTube Downloader - Приложение для скачивания видео с YouTube
Автор: GitHub Copilot
"""
import sys
import click
from colorama import Fore, Style
from downloader import YouTubeDownloader
from config import Config
def load_urls_from_file(file_path):
"""Загружает список URL из файла"""
urls = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
line = line.strip()
# Пропускаем пустые строки и комментарии
if line and not line.startswith('#'):
urls.append(line)
print(f"Загружено {len(urls)} URL из файла {file_path}")
return urls
except Exception as e:
print(f"{Fore.RED}Ошибка чтения файла {file_path}: {str(e)}{Style.RESET_ALL}")
return []
def show_video_info(downloader, url):
"""Показывает информацию о видео"""
video_info = downloader.get_video_info(url)
if video_info:
print(f"\n{Fore.GREEN}Информация о видео:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}Название:{Style.RESET_ALL} {video_info['title']}")
print(f"{Fore.YELLOW}Автор:{Style.RESET_ALL} {video_info['uploader']}")
if video_info['duration']:
duration_str = f"{video_info['duration']//60}:{video_info['duration']%60:02d}"
print(f"{Fore.YELLOW}Длительность:{Style.RESET_ALL} {duration_str}")
if video_info['view_count']:
print(f"{Fore.YELLOW}Просмотров:{Style.RESET_ALL} {video_info['view_count']:,}")
if video_info['upload_date']:
date_str = video_info['upload_date']
formatted_date = f"{date_str[6:8]}.{date_str[4:6]}.{date_str[:4]}"
print(f"{Fore.YELLOW}Дата загрузки:{Style.RESET_ALL} {formatted_date}")
if video_info['description']:
desc = video_info['description'][:200] + "..." if len(video_info['description']) > 200 else video_info['description']
print(f"{Fore.YELLOW}Описание:{Style.RESET_ALL} {desc}")
def show_video_formats(downloader, url):
"""Показывает доступные форматы видео"""
print(f"\n{Fore.GREEN}Получение доступных форматов...{Style.RESET_ALL}")
available_formats = downloader.get_available_formats(url)
if available_formats:
print(f"\n{Fore.GREEN}Доступные форматы:{Style.RESET_ALL}")
for fmt in available_formats:
size_str = f" ({fmt['filesize']//1024//1024} MB)" if fmt['filesize'] else ""
print(f"{fmt['quality']} ({fmt['ext']}){size_str}")
else:
print(f"{Fore.RED}Не удалось получить информацию о форматах{Style.RESET_ALL}")
@click.command()
@click.argument('url', required=False)
@click.option('--quality', '-q', default='best',
help='Качество видео (best, 1080p, 720p, 480p, 360p)')
@click.option('--audio-only', '-a', is_flag=True,
help='Скачать только аудио')
@click.option('--output', '-o', default=None,
help='Папка для сохранения файлов')
@click.option('--playlist', '-p', is_flag=True,
help='Скачать весь плейлист')
@click.option('--info', '-i', is_flag=True,
help='Показать информацию о видео без загрузки')
@click.option('--formats', '-f', is_flag=True,
help='Показать доступные форматы')
@click.option('--config', '-c', default='config.json',
help='Путь к файлу конфигурации')
@click.option('--batch', '-b', default=None, type=click.Path(exists=True),
help='Файл со списком URL для пакетной загрузки')
@click.option('--urls', multiple=True,
help='Несколько URL через пробел')
@click.option('--continue-on-error', is_flag=True,
help='Продолжать загрузку при ошибках')
def main(url, quality, audio_only, output, playlist, info, formats, config, batch, urls, continue_on_error):
"""
YouTube Downloader - скачивание видео с YouTube
URL: Ссылка на YouTube видео или плейлист (опционально при использовании --batch или --urls)
"""
# Вывод заголовка
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'YouTube Downloader':^60}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
try:
# Инициализация конфигурации и загрузчика
app_config = Config(config)
downloader = YouTubeDownloader(app_config)
# Определяем список URL для обработки
urls_to_process = []
if batch:
# Пакетная загрузка из файла
print(f"{Fore.GREEN}Загрузка URL из файла: {batch}{Style.RESET_ALL}")
urls_to_process = load_urls_from_file(batch)
elif urls:
# Множественные URL из командной строки
urls_to_process = list(urls)
print(f"{Fore.GREEN}Обработка {len(urls_to_process)} URL из командной строки{Style.RESET_ALL}")
elif url:
# Один URL
urls_to_process = [url]
else:
print(f"{Fore.RED}Ошибка: Необходимо указать URL, --batch файл или --urls{Style.RESET_ALL}")
sys.exit(1)
# Обработка списка URL
if info or formats:
# Для info и formats обрабатываем только первый URL
process_single_url = urls_to_process[0] if urls_to_process else None
if not process_single_url:
print(f"{Fore.RED}Ошибка: Нет URL для обработки{Style.RESET_ALL}")
sys.exit(1)
if not downloader.validate_url(process_single_url):
print(f"{Fore.RED}Ошибка: Некорректный URL YouTube{Style.RESET_ALL}")
sys.exit(1)
if info:
show_video_info(downloader, process_single_url)
return
if formats:
show_video_formats(downloader, process_single_url)
return
# Пакетная загрузка
print(f"\n{Fore.GREEN}Начинаю обработку {len(urls_to_process)} URL...{Style.RESET_ALL}")
successful = 0
failed = 0
failed_urls = []
for i, current_url in enumerate(urls_to_process, 1):
print(f"\n{Fore.CYAN}[{i}/{len(urls_to_process)}] Обработка: {current_url}{Style.RESET_ALL}")
# Проверка URL
if not downloader.validate_url(current_url):
print(f"{Fore.RED}✗ Некорректный URL, пропускаем{Style.RESET_ALL}")
failed += 1
failed_urls.append((current_url, "Некорректный URL"))
if not continue_on_error:
break
continue
try:
success = False
if playlist:
success = downloader.download_playlist(current_url, quality, audio_only, output)
else:
success = downloader.download_video(current_url, quality, audio_only, output)
if success:
successful += 1
print(f"{Fore.GREEN}✓ [{i}/{len(urls_to_process)}] Успешно загружено{Style.RESET_ALL}")
else:
failed += 1
failed_urls.append((current_url, "Ошибка загрузки"))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Ошибка загрузки{Style.RESET_ALL}")
if not continue_on_error:
break
except Exception as e:
failed += 1
error_msg = str(e)[:100] + "..." if len(str(e)) > 100 else str(e)
failed_urls.append((current_url, error_msg))
print(f"{Fore.RED}✗ [{i}/{len(urls_to_process)}] Исключение: {error_msg}{Style.RESET_ALL}")
if not continue_on_error:
break
# Показ результатов
print(f"\n{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if len(urls_to_process) > 1:
print(f"{Fore.GREEN}Пакетная загрузка завершена!{Style.RESET_ALL}")
print(f"{Fore.CYAN}Успешно: {successful}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Ошибок: {failed}{Style.RESET_ALL}")
print(f"{Fore.CYAN}Всего: {len(urls_to_process)}{Style.RESET_ALL}")
if failed_urls:
print(f"\n{Fore.YELLOW}Неудачные загрузки:{Style.RESET_ALL}")
for url_item, error in failed_urls[:5]: # Показываем только первые 5
short_url = url_item[:50] + "..." if len(url_item) > 50 else url_item
print(f"{short_url}: {error}")
if len(failed_urls) > 5:
print(f" ... и еще {len(failed_urls) - 5} ошибок")
else:
if successful > 0:
print(f"{Fore.GREEN}Загрузка успешно завершена!{Style.RESET_ALL}")
else:
print(f"{Fore.RED}Загрузка завершилась с ошибкой{Style.RESET_ALL}")
output_path = output or app_config.get("output_directory", "downloads")
print(f"{Fore.CYAN}Файлы сохранены в: {output_path}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{'='*60}{Style.RESET_ALL}")
if failed > 0 and not continue_on_error:
sys.exit(1)
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}Загрузка прервана пользователем{Style.RESET_ALL}")
sys.exit(0)
except Exception as e:
print(f"\n{Fore.RED}Произошла неожиданная ошибка: {str(e)}{Style.RESET_ALL}")
sys.exit(1)
@click.group()
def cli():
"""YouTube Downloader - управление настройками и загрузка видео"""
pass
@cli.command()
@click.option('--output-dir', default='downloads', help='Папка для загрузок по умолчанию')
@click.option('--video-quality', default='best', help='Качество видео по умолчанию')
@click.option('--audio-format', default='mp3', help='Формат аудио по умолчанию')
@click.option('--video-format', default='mp4', help='Формат видео по умолчанию')
def configure(output_dir, video_quality, audio_format, video_format):
"""Настройка параметров по умолчанию"""
config = Config()
config.set('output_directory', output_dir)
config.set('video_quality', video_quality)
config.set('audio_format', audio_format)
config.set('video_format', video_format)
config.save_config()
print(f"{Fore.GREEN}Конфигурация сохранена:{Style.RESET_ALL}")
print(f" Папка загрузок: {output_dir}")
print(f" Качество видео: {video_quality}")
print(f" Формат аудио: {audio_format}")
print(f" Формат видео: {video_format}")
@cli.command()
def show_config():
"""Показать текущие настройки"""
config = Config()
print(f"{Fore.CYAN}Текущая конфигурация:{Style.RESET_ALL}")
for key, value in config.config.items():
print(f" {key}: {value}")
if __name__ == '__main__':
# Если запущен без команды, используем основную функцию
if len(sys.argv) == 1 or (len(sys.argv) > 1 and not sys.argv[1] in ['configure', 'show-config']):
main()
else:
cli()

6
requirements.txt Normal file
View File

@@ -0,0 +1,6 @@
yt-dlp>=2024.1.1
click==8.1.7
tqdm==4.66.1
colorama==0.4.6
requests>=2.31.0
fake-useragent>=1.4.0

60
start.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
# YouTube Downloader Quick Start Script
echo "🎬 YouTube Downloader - Быстрый старт"
echo "========================================"
# Проверка Python
if ! command -v python3 &> /dev/null; then
echo "❌ Python3 не найден. Установите Python 3.7 или новее."
exit 1
fi
# Проверка зависимостей
if ! python3 -c "import yt_dlp" 2>/dev/null; then
echo "📦 Установка зависимостей..."
pip3 install -r requirements.txt
fi
# Проверка ffmpeg
if ! command -v ffmpeg &> /dev/null; then
echo "⚠️ ffmpeg не найден. Для конвертации аудио в MP3 установите ffmpeg:"
echo " ./install_ffmpeg.sh"
fi
# Создание папки загрузок
mkdir -p downloads
echo ""
echo "✅ Готово! Доступные команды:"
echo ""
echo "📹 Скачать видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID"
echo ""
echo "🎵 Только аудио:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --audio-only"
echo ""
echo "📋 Информация о видео:"
echo " python3 main.py https://www.youtube.com/watch?v=VIDEO_ID --info"
echo ""
echo "⚙️ Настройка:"
echo " python3 main.py configure"
echo ""
echo "📚 Справка:"
echo " python3 main.py --help"
echo ""
echo "🛠 Make команды:"
echo " make usage"
echo ""
# Тест функциональности
echo "🧪 Тест системы..."
if python3 -c "from downloader import YouTubeDownloader; d = YouTubeDownloader(); print('URL validation:', d.validate_url('https://www.youtube.com/watch?v=test'))" 2>/dev/null; then
echo "✅ Система работает корректно!"
else
echo "❌ Проблемы с системой. Проверьте зависимости."
fi
echo ""
echo "🎯 Пример использования:"
echo " python3 main.py https://www.youtube.com/watch?v=dQw4w9WgXcQ --info"

11
urls.txt Normal file
View File

@@ -0,0 +1,11 @@
# YouTube URL List Template
# Добавьте по одной ссылке YouTube на каждой строке
# Строки, начинающиеся с #, игнорируются (комментарии)
# Пример видео:
# https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Пример плейлиста:
# https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMt6VEY
# Добавьте ваши ссылки ниже:

211
utils.py Normal file
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(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()