init commit
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
.venv/
|
||||
.env
|
||||
downloads/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.sqlite3
|
||||
instance/
|
||||
*.log
|
||||
*.pot
|
||||
*.mo
|
||||
*.swp
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
0
.history/.gitignore_20250928091714
Normal file
0
.history/.gitignore_20250928091714
Normal file
16
.history/.gitignore_20250928091756
Normal file
16
.history/.gitignore_20250928091756
Normal file
@@ -0,0 +1,16 @@
|
||||
.venv/
|
||||
.env
|
||||
downloads/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.sqlite3
|
||||
instance/
|
||||
*.log
|
||||
*.pot
|
||||
*.mo
|
||||
*.swp
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
125
.history/BATCH_GUIDE_20250928091617.md
Normal file
125
.history/BATCH_GUIDE_20250928091617.md
Normal 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 стратегии
|
||||
# Просто дождитесь повторных попыток
|
||||
```
|
||||
125
.history/BATCH_GUIDE_20250928091648.md
Normal file
125
.history/BATCH_GUIDE_20250928091648.md
Normal 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 стратегии
|
||||
# Просто дождитесь повторных попыток
|
||||
```
|
||||
58
.history/Makefile_20250928084550
Normal file
58
.history/Makefile_20250928084550
Normal 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 - Установить все и запустить примеры"
|
||||
58
.history/Makefile_20250928085321
Normal file
58
.history/Makefile_20250928085321
Normal 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 - Установить все и запустить примеры"
|
||||
65
.history/Makefile_20250928085725
Normal file
65
.history/Makefile_20250928085725
Normal 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 - Установить все и запустить примеры"
|
||||
72
.history/Makefile_20250928090451
Normal file
72
.history/Makefile_20250928090451
Normal 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
|
||||
72
.history/Makefile_20250928090643
Normal file
72
.history/Makefile_20250928090643
Normal 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
|
||||
76
.history/Makefile_20250928090919
Normal file
76
.history/Makefile_20250928090919
Normal 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
|
||||
77
.history/Makefile_20250928090936
Normal file
77
.history/Makefile_20250928090936
Normal 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
|
||||
77
.history/Makefile_20250928091647
Normal file
77
.history/Makefile_20250928091647
Normal 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
|
||||
64
.history/README_20250928083717.md
Normal file
64
.history/README_20250928083717.md
Normal 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 # Документация
|
||||
```
|
||||
215
.history/README_20250928085030.md
Normal file
215
.history/README_20250928085030.md
Normal 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
|
||||
215
.history/README_20250928085321.md
Normal file
215
.history/README_20250928085321.md
Normal 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
|
||||
224
.history/README_20250928090503.md
Normal file
224
.history/README_20250928090503.md
Normal 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
|
||||
226
.history/README_20250928090513.md
Normal file
226
.history/README_20250928090513.md
Normal 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
|
||||
237
.history/README_20250928090529.md
Normal file
237
.history/README_20250928090529.md
Normal 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
|
||||
237
.history/README_20250928090643.md
Normal file
237
.history/README_20250928090643.md
Normal 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
|
||||
243
.history/README_20250928091227.md
Normal file
243
.history/README_20250928091227.md
Normal 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
|
||||
258
.history/README_20250928091244.md
Normal file
258
.history/README_20250928091244.md
Normal 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
|
||||
261
.history/README_20250928091258.md
Normal file
261
.history/README_20250928091258.md
Normal 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
|
||||
261
.history/README_20250928091647.md
Normal file
261
.history/README_20250928091647.md
Normal 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
.history/batch_urls_20250928090854.txt
Normal file
14
.history/batch_urls_20250928090854.txt
Normal 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
|
||||
14
.history/batch_urls_20250928091648.txt
Normal file
14
.history/batch_urls_20250928091648.txt
Normal 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
|
||||
55
.history/config_20250928083741.py
Normal file
55
.history/config_20250928083741.py
Normal 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
|
||||
55
.history/config_20250928083750.py
Normal file
55
.history/config_20250928083750.py
Normal 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
|
||||
55
.history/config_20250928085321.py
Normal file
55
.history/config_20250928085321.py
Normal 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
|
||||
213
.history/downloader_20250928083918.py
Normal file
213
.history/downloader_20250928083918.py
Normal 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 []
|
||||
215
.history/downloader_20250928083931.py
Normal file
215
.history/downloader_20250928083931.py
Normal 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 []
|
||||
215
.history/downloader_20250928084005.py
Normal file
215
.history/downloader_20250928084005.py
Normal 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 []
|
||||
215
.history/downloader_20250928084015.py
Normal file
215
.history/downloader_20250928084015.py
Normal 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 []
|
||||
216
.history/downloader_20250928084028.py
Normal file
216
.history/downloader_20250928084028.py
Normal 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 []
|
||||
218
.history/downloader_20250928084040.py
Normal file
218
.history/downloader_20250928084040.py
Normal 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 []
|
||||
218
.history/downloader_20250928085321.py
Normal file
218
.history/downloader_20250928085321.py
Normal 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 []
|
||||
243
.history/downloader_20250928085551.py
Normal file
243
.history/downloader_20250928085551.py
Normal 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 []
|
||||
303
.history/downloader_20250928085626.py
Normal file
303
.history/downloader_20250928085626.py
Normal 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 []
|
||||
303
.history/downloader_20250928085636.py
Normal file
303
.history/downloader_20250928085636.py
Normal 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 []
|
||||
303
.history/downloader_20250928085650.py
Normal file
303
.history/downloader_20250928085650.py
Normal 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 []
|
||||
299
.history/downloader_20250928085708.py
Normal file
299
.history/downloader_20250928085708.py
Normal 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 []
|
||||
299
.history/downloader_20250928090643.py
Normal file
299
.history/downloader_20250928090643.py
Normal 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
.history/examples_20250928084356.py
Normal file
126
.history/examples_20250928084356.py
Normal 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}")
|
||||
126
.history/examples_20250928085321.py
Normal file
126
.history/examples_20250928085321.py
Normal 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
.history/install_ffmpeg_20250928085959.sh
Normal file
63
.history/install_ffmpeg_20250928085959.sh
Normal 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
|
||||
63
.history/install_ffmpeg_20250928090643.sh
Normal file
63
.history/install_ffmpeg_20250928090643.sh
Normal 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
|
||||
155
.history/main_20250928084237.py
Normal file
155
.history/main_20250928084237.py
Normal 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()
|
||||
155
.history/main_20250928085321.py
Normal file
155
.history/main_20250928085321.py
Normal 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()
|
||||
209
.history/main_20250928090815.py
Normal file
209
.history/main_20250928090815.py
Normal 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()
|
||||
261
.history/main_20250928090840.py
Normal file
261
.history/main_20250928090840.py
Normal 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()
|
||||
261
.history/main_20250928091647.py
Normal file
261
.history/main_20250928091647.py
Normal 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()
|
||||
4
.history/requirements_20250928083659.txt
Normal file
4
.history/requirements_20250928083659.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
yt-dlp==2023.12.30
|
||||
click==8.1.7
|
||||
tqdm==4.66.1
|
||||
colorama==0.4.6
|
||||
4
.history/requirements_20250928085321.txt
Normal file
4
.history/requirements_20250928085321.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
yt-dlp==2023.12.30
|
||||
click==8.1.7
|
||||
tqdm==4.66.1
|
||||
colorama==0.4.6
|
||||
6
.history/requirements_20250928085551.txt
Normal file
6
.history/requirements_20250928085551.txt
Normal 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
|
||||
6
.history/requirements_20250928090643.txt
Normal file
6
.history/requirements_20250928090643.txt
Normal 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
|
||||
54
.history/start_20250928085105.sh
Normal file
54
.history/start_20250928085105.sh
Normal 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"
|
||||
54
.history/start_20250928085321.sh
Normal file
54
.history/start_20250928085321.sh
Normal 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"
|
||||
60
.history/start_20250928090541.sh
Normal file
60
.history/start_20250928090541.sh
Normal 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"
|
||||
60
.history/start_20250928090643.sh
Normal file
60
.history/start_20250928090643.sh
Normal 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"
|
||||
211
.history/utils_20250928084712.py
Normal file
211
.history/utils_20250928084712.py
Normal 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()
|
||||
211
.history/utils_20250928084729.py
Normal file
211
.history/utils_20250928084729.py
Normal 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()
|
||||
211
.history/utils_20250928085321.py
Normal file
211
.history/utils_20250928085321.py
Normal 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()
|
||||
211
.history/utils_20250928090908.py
Normal file
211
.history/utils_20250928090908.py
Normal 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()
|
||||
211
.history/utils_20250928091647.py
Normal file
211
.history/utils_20250928091647.py
Normal 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
125
BATCH_GUIDE.md
Normal 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
77
Makefile
Normal 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
261
README.md
Normal 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
14
batch_urls.txt
Normal 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
13
config.json
Normal 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
55
config.py
Normal 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
299
downloader.py
Normal 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
126
examples.py
Normal 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
63
install_ffmpeg.sh
Executable 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
261
main.py
Normal 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
6
requirements.txt
Normal 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
60
start.sh
Executable 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
11
urls.txt
Normal 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
211
utils.py
Normal 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()
|
||||
Reference in New Issue
Block a user