mac support
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
a82c9741a5
commit
eb3642d652
47
PRD.md
47
PRD.md
@ -1,8 +1,8 @@
|
||||
|
||||
# 🎤 Karaoke Video Downloader – PRD (v3.4.3)
|
||||
# 🎤 Karaoke Video Downloader – PRD (v3.4.4)
|
||||
|
||||
## ✅ Overview
|
||||
A Python-based Windows CLI tool to download karaoke videos from YouTube channels/playlists using `yt-dlp.exe`, with advanced tracking, songlist prioritization, and flexible configuration. The codebase has been comprehensively refactored into a modular architecture with centralized utilities for improved maintainability, error handling, and code reuse.
|
||||
A Python-based cross-platform CLI tool to download karaoke videos from YouTube channels/playlists using `yt-dlp`, with advanced tracking, songlist prioritization, and flexible configuration. Supports Windows and macOS with automatic platform detection. The codebase has been comprehensively refactored into a modular architecture with centralized utilities for improved maintainability, error handling, and code reuse.
|
||||
|
||||
---
|
||||
|
||||
@ -63,9 +63,9 @@ The codebase has been refactored into focused modules with centralized utilities
|
||||
---
|
||||
|
||||
## ⚙️ Platform & Stack
|
||||
- **Platform:** Windows
|
||||
- **Platform:** Windows, macOS
|
||||
- **Interface:** Command-line (CLI)
|
||||
- **Tech Stack:** Python 3.7+, yt-dlp.exe, mutagen (for ID3 tagging)
|
||||
- **Tech Stack:** Python 3.7+, yt-dlp (platform-specific binary), mutagen (for ID3 tagging)
|
||||
|
||||
---
|
||||
|
||||
@ -164,9 +164,11 @@ KaroakeVideoDownloader/
|
||||
├── downloads/ # All video output
|
||||
│ └── [ChannelName]/ # Per-channel folders
|
||||
├── logs/ # Download logs
|
||||
├── downloader/yt-dlp.exe # yt-dlp binary
|
||||
├── tests/ # Diagnostic and test scripts
|
||||
│ └── test_installation.py
|
||||
├── downloader/yt-dlp.exe # yt-dlp binary (Windows)
|
||||
├── downloader/yt-dlp_macos # yt-dlp binary (macOS)
|
||||
├── src/tests/ # Test scripts
|
||||
│ ├── test_macos.py # macOS setup and functionality tests
|
||||
│ └── test_platform.py # Platform detection tests
|
||||
├── download_karaoke.py # Main entry point (thin wrapper)
|
||||
├── README.md
|
||||
├── PRD.md
|
||||
@ -530,6 +532,37 @@ def download_new_mode(self, ...):
|
||||
- [ ] Advanced configuration UI
|
||||
- [ ] Real-time download progress visualization
|
||||
|
||||
## 🔧 Recent Bug Fixes & Improvements (v3.4.4)
|
||||
### **macOS Support with Automatic Platform Detection**
|
||||
- **Cross-platform compatibility**: Added support for macOS alongside Windows
|
||||
- **Automatic platform detection**: Detects operating system and selects appropriate yt-dlp binary
|
||||
- **Flexible yt-dlp integration**: Supports both binary files (`yt-dlp_macos`) and pip installation (`python3 -m yt_dlp`)
|
||||
- **Setup automation**: `setup_macos.py` script for easy macOS setup with FFmpeg and yt-dlp installation
|
||||
- **Command parsing**: Intelligent parsing of yt-dlp commands (file paths vs. module commands)
|
||||
- **Enhanced validation**: Platform-specific error messages and validation in CLI
|
||||
- **Backward compatibility**: Maintains full compatibility with existing Windows installations
|
||||
|
||||
### **Benefits of macOS Support**
|
||||
- **Native macOS experience**: No need for Windows compatibility layers or virtualization
|
||||
- **Automatic setup**: Simple setup script handles all dependencies
|
||||
- **Flexible installation**: Choose between binary download or pip installation
|
||||
- **Consistent functionality**: All features work identically on both platforms
|
||||
- **Easy maintenance**: Platform detection handles configuration automatically
|
||||
|
||||
### **Setup Instructions**
|
||||
```bash
|
||||
# Automatic setup (recommended)
|
||||
python3 setup_macos.py
|
||||
|
||||
# Test installation
|
||||
python3 src/tests/test_macos.py
|
||||
|
||||
# Manual setup options
|
||||
# 1. Install yt-dlp via pip: pip3 install yt-dlp
|
||||
# 2. Download binary: curl -L -o downloader/yt-dlp_macos https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos
|
||||
# 3. Install FFmpeg: brew install ffmpeg
|
||||
```
|
||||
|
||||
## 🔧 Recent Bug Fixes & Improvements (v3.4.6)
|
||||
### **Dry Run Mode**
|
||||
- **New `--dry-run` parameter**: Build download plan and show what would be downloaded without actually downloading anything
|
||||
|
||||
58
README.md
58
README.md
@ -1,6 +1,6 @@
|
||||
# 🎤 Karaoke Video Downloader
|
||||
|
||||
A Python-based Windows CLI tool to download karaoke videos from YouTube channels/playlists using `yt-dlp.exe`, with advanced tracking, songlist prioritization, and flexible configuration.
|
||||
A Python-based cross-platform CLI tool to download karaoke videos from YouTube channels/playlists using `yt-dlp`, with advanced tracking, songlist prioritization, and flexible configuration. Supports Windows and macOS with automatic platform detection.
|
||||
|
||||
## ✨ Features
|
||||
- 🎵 **Channel & Playlist Downloads**: Download all videos from a YouTube channel or playlist
|
||||
@ -24,6 +24,7 @@ A Python-based Windows CLI tool to download karaoke videos from YouTube channels
|
||||
- 📊 **Unmatched Songs Reports**: Generate detailed reports of songs that couldn't be found in any channel with `--generate-unmatched-report`
|
||||
- 🛡️ **Duplicate File Prevention**: Automatically detects and prevents duplicate files with `(2)`, `(3)` suffixes, with cleanup utility for existing duplicates
|
||||
- 🏷️ **Consistent Metadata**: Filename and ID3 tag use identical artist/title format for clear file identification
|
||||
- 🍎 **macOS Support**: Automatic platform detection and setup with native macOS binaries and FFmpeg integration
|
||||
|
||||
## 🏗️ Architecture
|
||||
The codebase has been comprehensively refactored into a modular architecture with centralized utilities for improved maintainability, error handling, and code reuse:
|
||||
@ -185,13 +186,53 @@ python data/cleanup_duplicate_files.py
|
||||
```
|
||||
|
||||
## 📋 Requirements
|
||||
- **Windows 10/11**
|
||||
- **Windows 10/11 or macOS 10.14+**
|
||||
- **Python 3.7+**
|
||||
- **yt-dlp.exe** (in `downloader/`)
|
||||
- **yt-dlp binary** (platform-specific, see setup instructions below)
|
||||
- **mutagen** (for ID3 tagging, optional)
|
||||
- **ffmpeg/ffprobe** (for video validation, optional but recommended)
|
||||
- **rapidfuzz** (for fuzzy matching, optional, falls back to difflib)
|
||||
|
||||
## 🍎 macOS Setup
|
||||
|
||||
### Automatic Setup (Recommended)
|
||||
Run the macOS setup script to automatically set up yt-dlp and FFmpeg:
|
||||
|
||||
```bash
|
||||
python3 setup_macos.py
|
||||
```
|
||||
|
||||
This script will:
|
||||
- Detect your macOS version
|
||||
- Offer installation options for yt-dlp (pip or binary download)
|
||||
- Install FFmpeg via Homebrew
|
||||
- Test the installation
|
||||
|
||||
### Manual Setup
|
||||
If you prefer to set up manually:
|
||||
|
||||
#### Option 1: Install yt-dlp via pip
|
||||
```bash
|
||||
pip3 install yt-dlp
|
||||
```
|
||||
|
||||
#### Option 2: Download yt-dlp binary
|
||||
```bash
|
||||
mkdir -p downloader
|
||||
curl -L -o downloader/yt-dlp_macos https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos
|
||||
chmod +x downloader/yt-dlp_macos
|
||||
```
|
||||
|
||||
#### Install FFmpeg
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
### Test Installation
|
||||
```bash
|
||||
python3 src/tests/test_macos.py
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
> **💡 Pro Tip**: For a complete list of all available commands, see `commands.txt` - you can copy/paste any command directly into your terminal!
|
||||
@ -370,9 +411,11 @@ KaroakeVideoDownloader/
|
||||
├── downloads/ # All video output
|
||||
│ └── [ChannelName]/ # Per-channel folders
|
||||
├── logs/ # Download logs
|
||||
├── downloader/yt-dlp.exe # yt-dlp binary
|
||||
├── tests/ # Diagnostic and test scripts
|
||||
│ └── test_installation.py
|
||||
├── downloader/yt-dlp.exe # yt-dlp binary (Windows)
|
||||
├── downloader/yt-dlp_macos # yt-dlp binary (macOS)
|
||||
├── src/tests/ # Test scripts
|
||||
│ ├── test_macos.py # macOS setup and functionality tests
|
||||
│ └── test_platform.py # Platform detection tests
|
||||
├── download_karaoke.py # Main entry point (thin wrapper)
|
||||
├── README.md
|
||||
├── PRD.md
|
||||
@ -574,7 +617,8 @@ The codebase has been comprehensively refactored to improve maintainability and
|
||||
- **Robust download plan execution:** Fixed index management in download plan execution to prevent errors during interrupted downloads.
|
||||
|
||||
## 🐞 Troubleshooting
|
||||
- Ensure `yt-dlp.exe` is in the `downloader/` folder
|
||||
- **Windows**: Ensure `yt-dlp.exe` is in the `downloader/` folder
|
||||
- **macOS**: Run `python3 setup_macos.py` to set up yt-dlp and FFmpeg
|
||||
- Check `logs/` for error details
|
||||
- Use `python -m karaoke_downloader.check_resolution` to verify video quality
|
||||
- If you see errors about ffmpeg/ffprobe, install [ffmpeg](https://ffmpeg.org/download.html) and ensure it is in your PATH
|
||||
|
||||
21
commands.txt
21
commands.txt
@ -1,6 +1,6 @@
|
||||
# 🎤 Karaoke Video Downloader - CLI Commands Reference
|
||||
# Copy and paste these commands into your terminal
|
||||
# Updated: v3.4.4 (includes all videos download mode, manual video collection, channel parsing rules, and all previous improvements)
|
||||
# Updated: v3.4.4 (includes macOS support, all videos download mode, manual video collection, channel parsing rules, and all previous improvements)
|
||||
|
||||
## 📥 BASIC DOWNLOADS
|
||||
|
||||
@ -313,6 +313,24 @@ python download_karaoke.py --parallel --workers 3 --manual --limit 5
|
||||
# 7c. Manual videos with fuzzy matching
|
||||
python download_karaoke.py --manual --fuzzy-match --fuzzy-threshold 85 --limit 10
|
||||
|
||||
## 🍎 macOS SETUP COMMANDS
|
||||
|
||||
# Automatic macOS setup (detects OS and installs yt-dlp + FFmpeg)
|
||||
python3 setup_macos.py
|
||||
|
||||
# Test macOS setup and functionality
|
||||
python3 src/tests/test_macos.py
|
||||
|
||||
# Manual macOS setup options
|
||||
# Install yt-dlp via pip
|
||||
pip3 install yt-dlp
|
||||
|
||||
# Download yt-dlp binary for macOS
|
||||
mkdir -p downloader && curl -L -o downloader/yt-dlp_macos https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos && chmod +x downloader/yt-dlp_macos
|
||||
|
||||
# Install FFmpeg via Homebrew
|
||||
brew install ffmpeg
|
||||
|
||||
## 🔧 TROUBLESHOOTING COMMANDS
|
||||
|
||||
# Check if everything is working
|
||||
@ -352,6 +370,7 @@ python download_karaoke.py --clear-server-duplicates
|
||||
# - Use --fuzzy-match for better song discovery
|
||||
# - Use --refresh sparingly (forces re-scan)
|
||||
# - Clear cache if you encounter issues
|
||||
# - macOS users: Run `python3 setup_macos.py` for automatic setup
|
||||
|
||||
# Parallel download tips:
|
||||
# - Start with --workers 3 for conservative approach
|
||||
|
||||
19
data/channel_cache/@LetsSingKaraoke.json
Normal file
19
data/channel_cache/@LetsSingKaraoke.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"channel_id": "@LetsSingKaraoke",
|
||||
"videos": [
|
||||
{
|
||||
"title": "Sub Urban - Cradles | Karaoke (instrumental)",
|
||||
"id": "8uj7IzhdiO4"
|
||||
},
|
||||
{
|
||||
"title": "Sia - Snowman | Karaoke (instrumental)",
|
||||
"id": "ZbWHuncTgsM"
|
||||
},
|
||||
{
|
||||
"title": "Trevor Daniel - Falling | Karaoke (Instrumental)",
|
||||
"id": "nU7n2aq7f98"
|
||||
}
|
||||
],
|
||||
"last_updated": "2025-08-05T15:59:09.280488",
|
||||
"video_count": 3
|
||||
}
|
||||
10
data/channel_cache/@LetsSingKaraoke_raw_output.txt
Normal file
10
data/channel_cache/@LetsSingKaraoke_raw_output.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# Raw yt-dlp output for @LetsSingKaraoke
|
||||
# Channel URL: https://www.youtube.com/@LetsSingKaraoke/videos
|
||||
# Command: downloader/yt-dlp_macos --flat-playlist --print %(title)s|%(id)s|%(url)s --verbose https://www.youtube.com/@LetsSingKaraoke/videos
|
||||
# Timestamp: 2025-08-05T15:59:09.280155
|
||||
# Total lines: 3
|
||||
################################################################################
|
||||
|
||||
1: Sub Urban - Cradles | Karaoke (instrumental)|8uj7IzhdiO4|https://www.youtube.com/watch?v=8uj7IzhdiO4
|
||||
2: Sia - Snowman | Karaoke (instrumental)|ZbWHuncTgsM|https://www.youtube.com/watch?v=ZbWHuncTgsM
|
||||
3: Trevor Daniel - Falling | Karaoke (Instrumental)|nU7n2aq7f98|https://www.youtube.com/watch?v=nU7n2aq7f98
|
||||
@ -34,5 +34,12 @@
|
||||
"include_console": true,
|
||||
"include_file": true
|
||||
},
|
||||
"platform_settings": {
|
||||
"auto_detect_platform": true,
|
||||
"yt_dlp_paths": {
|
||||
"windows": "downloader/yt-dlp.exe",
|
||||
"macos": "downloader/yt-dlp_macos"
|
||||
}
|
||||
},
|
||||
"yt_dlp_path": "downloader/yt-dlp.exe"
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
{
|
||||
"timestamp": "2025-08-05T16:01:09.018725",
|
||||
"download_plan": [
|
||||
{
|
||||
"video_id": "oHV8Iw0R4BY",
|
||||
"artist": "Shaboozey, Jelly Roll",
|
||||
"title": "Amen",
|
||||
"filename": "Shaboozey, Jelly Roll - Amen.mp4",
|
||||
"channel_name": "@SingKingKaraoke",
|
||||
"video_title": "Shaboozey, Jelly Roll - Amen (Karaoke Version)",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "Jm3a-VAomH0",
|
||||
"artist": "Pet Shop Boys",
|
||||
"title": "Domino Dancing",
|
||||
"filename": "Pet Shop Boys - Domino Dancing.mp4",
|
||||
"channel_name": "@KaraokeOnVEVO",
|
||||
"video_title": "Pet Shop Boys - Domino Dancing (Karaoke)",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "6Vb0igX0-Ss",
|
||||
"artist": "Chappell Roan",
|
||||
"title": "The Giver",
|
||||
"filename": "Chappell Roan - The Giver.mp4",
|
||||
"channel_name": "@StingrayKaraoke",
|
||||
"video_title": "Chappell Roan - The Giver (Karaoke Version)",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "b1k2_B9oCr4",
|
||||
"artist": "James Arthur",
|
||||
"title": "Train Wreck",
|
||||
"filename": "James Arthur - Train Wreck.mp4",
|
||||
"channel_name": "@sing2karaoke",
|
||||
"video_title": "James Arthur Train Wreck",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "cg10FeEYSSQ",
|
||||
"artist": "Caesars",
|
||||
"title": "Jerk It Out",
|
||||
"filename": "Caesars - Jerk It Out.mp4",
|
||||
"channel_name": "@ZoomKaraokeOfficial",
|
||||
"video_title": "Caesars - Jerk It Out - Karaoke Version from Zoom Karaoke",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "m51bbu2ghp4",
|
||||
"artist": "Jin",
|
||||
"title": "Don't Say You Love Me",
|
||||
"filename": "Jin - Dont Say You Love Me.mp4",
|
||||
"channel_name": "@VocalStarKaraoke",
|
||||
"video_title": "Don't Say You Love Me - Jin KARAOKE With Vocal Guide",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "qegLWI99Wg0",
|
||||
"artist": "Ed Sheeran & Beyoncé",
|
||||
"title": "Perfect Duet",
|
||||
"filename": "Ed Sheeran & Beyoncé - Perfect Duet.mp4",
|
||||
"channel_name": "Unknown",
|
||||
"video_title": "Ed Sheeran & Beyoncé - Perfect Duet",
|
||||
"force_download": false
|
||||
},
|
||||
{
|
||||
"video_id": "ZbWHuncTgsM",
|
||||
"artist": "Sia",
|
||||
"title": "Snowman | Karaoke (instrumental)",
|
||||
"filename": "Sia - Snowman Karaoke (instrumental).mp4",
|
||||
"channel_name": "@LetsSingKaraoke",
|
||||
"video_title": "Sia - Snowman | Karaoke (instrumental)",
|
||||
"force_download": false
|
||||
}
|
||||
],
|
||||
"unmatched": []
|
||||
}
|
||||
@ -31110,5 +31110,29 @@
|
||||
"channel": "@SingKingKaraoke",
|
||||
"marked_at": "2025-07-29T14:46:07.860090",
|
||||
"reason": "already_on_server"
|
||||
},
|
||||
"teddy swims_lose control": {
|
||||
"artist": "Teddy Swims",
|
||||
"title": "Lose Control",
|
||||
"video_title": "Teddy Swims - Lose Control",
|
||||
"channel": "songlist",
|
||||
"marked_at": "2025-08-05T16:07:47.891258",
|
||||
"reason": "already_on_server"
|
||||
},
|
||||
"benson boone_mystical magical": {
|
||||
"artist": "Benson Boone",
|
||||
"title": "Mystical Magical",
|
||||
"video_title": "Benson Boone - Mystical Magical",
|
||||
"channel": "songlist",
|
||||
"marked_at": "2025-08-05T16:07:47.906113",
|
||||
"reason": "already_on_server"
|
||||
},
|
||||
"the weeknd_the hills": {
|
||||
"artist": "The Weeknd",
|
||||
"title": "The Hills",
|
||||
"video_title": "The Weeknd - The Hills",
|
||||
"channel": "songlist",
|
||||
"marked_at": "2025-08-05T16:07:47.922750",
|
||||
"reason": "already_on_server"
|
||||
}
|
||||
}
|
||||
12
data/unmatched_songs_report_20250805_160748.json
Normal file
12
data/unmatched_songs_report_20250805_160748.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"generated_at": "2025-08-05T16:07:48.031279",
|
||||
"total_unmatched": 1,
|
||||
"unmatched_songs": [
|
||||
{
|
||||
"artist": "SZA",
|
||||
"title": "30 For 30",
|
||||
"position": 3,
|
||||
"search_key": "sza_30 for 30"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
downloader/yt-dlp_macos
Executable file
BIN
downloader/yt-dlp_macos
Executable file
Binary file not shown.
@ -328,11 +328,36 @@ Examples:
|
||||
print("❌ Error: --channel-workers must be between 1 and 10")
|
||||
sys.exit(1)
|
||||
|
||||
yt_dlp_path = Path("downloader/yt-dlp.exe")
|
||||
if not yt_dlp_path.exists():
|
||||
print("❌ Error: yt-dlp.exe not found in downloader/ directory")
|
||||
print("Please ensure yt-dlp.exe is present in the downloader/ folder")
|
||||
sys.exit(1)
|
||||
# Load configuration to get platform-aware yt-dlp path
|
||||
from karaoke_downloader.config_manager import load_config
|
||||
config = load_config()
|
||||
yt_dlp_path = config.yt_dlp_path
|
||||
|
||||
# Check if it's a command string (like "python3 -m yt_dlp") or a file path
|
||||
if yt_dlp_path.startswith(('python', 'python3')):
|
||||
# It's a command string, test if it works
|
||||
try:
|
||||
import subprocess
|
||||
cmd = yt_dlp_path.split() + ["--version"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"Command failed: {result.stderr}")
|
||||
except Exception as e:
|
||||
platform_name = "macOS" if sys.platform == "darwin" else "Windows"
|
||||
print(f"❌ Error: yt-dlp command failed: {yt_dlp_path}")
|
||||
print(f"Please ensure yt-dlp is properly installed for {platform_name}")
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
# It's a file path, check if it exists
|
||||
yt_dlp_file = Path(yt_dlp_path)
|
||||
if not yt_dlp_file.exists():
|
||||
platform_name = "macOS" if sys.platform == "darwin" else "Windows"
|
||||
binary_name = yt_dlp_file.name
|
||||
print(f"❌ Error: {binary_name} not found in downloader/ directory")
|
||||
print(f"Please ensure {binary_name} is present in the downloader/ folder for {platform_name}")
|
||||
print(f"Expected path: {yt_dlp_file}")
|
||||
sys.exit(1)
|
||||
|
||||
downloader = KaraokeDownloader()
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ Provides centralized configuration loading, validation, and management.
|
||||
"""
|
||||
|
||||
import json
|
||||
import platform
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@ -42,6 +44,13 @@ DEFAULT_CONFIG = {
|
||||
"include_console": True,
|
||||
"include_file": True,
|
||||
},
|
||||
"platform_settings": {
|
||||
"auto_detect_platform": True,
|
||||
"yt_dlp_paths": {
|
||||
"windows": "downloader/yt-dlp.exe",
|
||||
"macos": "downloader/yt-dlp_macos"
|
||||
}
|
||||
},
|
||||
"yt_dlp_path": "downloader/yt-dlp.exe",
|
||||
}
|
||||
|
||||
@ -55,6 +64,23 @@ RESOLUTION_MAP = {
|
||||
}
|
||||
|
||||
|
||||
def detect_platform() -> str:
|
||||
"""Detect the current platform and return platform name."""
|
||||
system = platform.system().lower()
|
||||
if system == "windows":
|
||||
return "windows"
|
||||
elif system == "darwin":
|
||||
return "macos"
|
||||
else:
|
||||
return "windows" # Default to Windows for other platforms
|
||||
|
||||
|
||||
def get_platform_yt_dlp_path(platform_paths: Dict[str, str]) -> str:
|
||||
"""Get the appropriate yt-dlp path for the current platform."""
|
||||
platform_name = detect_platform()
|
||||
return platform_paths.get(platform_name, platform_paths.get("windows", "downloader/yt-dlp.exe"))
|
||||
|
||||
|
||||
@dataclass
|
||||
class DownloadSettings:
|
||||
"""Configuration for download settings."""
|
||||
@ -234,11 +260,21 @@ class ConfigManager:
|
||||
folder_structure = FolderStructure(**config_data.get("folder_structure", {}))
|
||||
logging_config = LoggingConfig(**config_data.get("logging", {}))
|
||||
|
||||
# Handle platform-specific yt-dlp path
|
||||
yt_dlp_path = config_data.get("yt_dlp_path", "downloader/yt-dlp.exe")
|
||||
|
||||
# Check if platform auto-detection is enabled
|
||||
platform_settings = config_data.get("platform_settings", {})
|
||||
if platform_settings.get("auto_detect_platform", True):
|
||||
platform_paths = platform_settings.get("yt_dlp_paths", {})
|
||||
if platform_paths:
|
||||
yt_dlp_path = get_platform_yt_dlp_path(platform_paths)
|
||||
|
||||
return AppConfig(
|
||||
download_settings=download_settings,
|
||||
folder_structure=folder_structure,
|
||||
logging=logging_config,
|
||||
yt_dlp_path=config_data.get("yt_dlp_path", "downloader/yt-dlp.exe"),
|
||||
yt_dlp_path=yt_dlp_path,
|
||||
_config_file=self.config_file,
|
||||
)
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ class KaraokeDownloader:
|
||||
self.config = self.config_manager.load_config()
|
||||
|
||||
# Initialize paths
|
||||
self.yt_dlp_path = Path(self.config.yt_dlp_path)
|
||||
self.yt_dlp_path = self.config.yt_dlp_path # Keep as string for command parsing
|
||||
self.downloads_dir = Path(self.config.folder_structure.downloads_dir)
|
||||
self.logs_dir = Path(self.config.folder_structure.logs_dir)
|
||||
|
||||
@ -799,7 +799,6 @@ class KaraokeDownloader:
|
||||
from karaoke_downloader.cache_manager import get_download_plan_cache_file, save_plan_cache
|
||||
|
||||
plan_kwargs = {
|
||||
"mode": "latest_per_channel",
|
||||
"channels": len(channel_urls),
|
||||
"limit_per_channel": limit,
|
||||
"force_download": force_download,
|
||||
|
||||
@ -551,10 +551,10 @@ class TrackingManager:
|
||||
print(f" 📡 Channel URL: {channel_url}")
|
||||
|
||||
import subprocess
|
||||
from karaoke_downloader.youtube_utils import _parse_yt_dlp_command
|
||||
|
||||
# First, let's get the total count to show progress
|
||||
count_cmd = [
|
||||
yt_dlp_path,
|
||||
count_cmd = _parse_yt_dlp_command(yt_dlp_path) + [
|
||||
"--flat-playlist",
|
||||
"--print",
|
||||
"%(title)s",
|
||||
@ -576,8 +576,7 @@ class TrackingManager:
|
||||
print(f" ⚠️ Channel test error: {e}")
|
||||
|
||||
# Now fetch all videos with progress indicators
|
||||
cmd = [
|
||||
yt_dlp_path,
|
||||
cmd = _parse_yt_dlp_command(yt_dlp_path) + [
|
||||
"--flat-playlist",
|
||||
"--print",
|
||||
"%(title)s|%(id)s|%(url)s",
|
||||
|
||||
@ -9,6 +9,19 @@ from typing import Any, Dict, List, Optional, Union
|
||||
from karaoke_downloader.config_manager import AppConfig
|
||||
|
||||
|
||||
def _parse_yt_dlp_command(yt_dlp_path: str) -> List[str]:
|
||||
"""
|
||||
Parse yt-dlp path/command into a list of command arguments.
|
||||
Handles both file paths and command strings like 'python3 -m yt_dlp'.
|
||||
"""
|
||||
if yt_dlp_path.startswith(('python', 'python3')):
|
||||
# It's a Python module command
|
||||
return yt_dlp_path.split()
|
||||
else:
|
||||
# It's a file path
|
||||
return [yt_dlp_path]
|
||||
|
||||
|
||||
def get_channel_info(
|
||||
channel_url: str, yt_dlp_path: str = "downloader/yt-dlp.exe"
|
||||
) -> tuple[str, str]:
|
||||
@ -43,7 +56,7 @@ def get_playlist_info(
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Get playlist information using yt-dlp."""
|
||||
try:
|
||||
cmd = [yt_dlp_path, "--dump-json", "--flat-playlist", playlist_url]
|
||||
cmd = _parse_yt_dlp_command(yt_dlp_path) + ["--dump-json", "--flat-playlist", playlist_url]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
videos = []
|
||||
for line in result.stdout.strip().split("\n"):
|
||||
@ -75,8 +88,7 @@ def build_yt_dlp_command(
|
||||
Returns:
|
||||
List of command arguments for subprocess.run
|
||||
"""
|
||||
cmd = [
|
||||
str(yt_dlp_path),
|
||||
cmd = _parse_yt_dlp_command(yt_dlp_path) + [
|
||||
"--no-check-certificates",
|
||||
"--ignore-errors",
|
||||
"--no-warnings",
|
||||
@ -128,7 +140,7 @@ def show_available_formats(
|
||||
timeout: Timeout in seconds
|
||||
"""
|
||||
print(f"🔍 Checking available formats for: {video_url}")
|
||||
format_cmd = [str(yt_dlp_path), "--list-formats", video_url]
|
||||
format_cmd = _parse_yt_dlp_command(yt_dlp_path) + ["--list-formats", video_url]
|
||||
try:
|
||||
format_result = subprocess.run(
|
||||
format_cmd, capture_output=True, text=True, timeout=timeout
|
||||
|
||||
220
setup_macos.py
Normal file
220
setup_macos.py
Normal file
@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
macOS setup script for Karaoke Video Downloader.
|
||||
This script helps users set up yt-dlp and FFmpeg on macOS.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def check_ffmpeg():
|
||||
"""Check if FFmpeg is installed."""
|
||||
try:
|
||||
result = subprocess.run(["ffmpeg", "-version"], capture_output=True, text=True, timeout=10)
|
||||
return result.returncode == 0
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return False
|
||||
|
||||
|
||||
def check_yt_dlp():
|
||||
"""Check if yt-dlp is installed via pip or binary."""
|
||||
# Check pip installation
|
||||
try:
|
||||
result = subprocess.run([sys.executable, "-m", "yt_dlp", "--version"],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
|
||||
pass
|
||||
|
||||
# Check binary file
|
||||
binary_path = Path("downloader/yt-dlp_macos")
|
||||
if binary_path.exists():
|
||||
try:
|
||||
result = subprocess.run([str(binary_path), "--version"],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
return result.returncode == 0
|
||||
except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def install_ffmpeg():
|
||||
"""Install FFmpeg via Homebrew."""
|
||||
print("🎬 Installing FFmpeg...")
|
||||
|
||||
# Check if Homebrew is installed
|
||||
try:
|
||||
subprocess.run(["brew", "--version"], capture_output=True, check=True)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
print("❌ Homebrew is not installed. Please install Homebrew first:")
|
||||
print(" /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
|
||||
return False
|
||||
|
||||
try:
|
||||
print("🍺 Installing FFmpeg via Homebrew...")
|
||||
result = subprocess.run(["brew", "install", "ffmpeg"],
|
||||
capture_output=True, text=True, check=True)
|
||||
print("✅ FFmpeg installed successfully!")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Failed to install FFmpeg: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def download_yt_dlp_binary():
|
||||
"""Download yt-dlp binary for macOS."""
|
||||
print("📥 Downloading yt-dlp binary for macOS...")
|
||||
|
||||
# Create downloader directory if it doesn't exist
|
||||
downloader_dir = Path("downloader")
|
||||
downloader_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Download yt-dlp binary
|
||||
binary_path = downloader_dir / "yt-dlp_macos"
|
||||
url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos"
|
||||
|
||||
try:
|
||||
print(f"📡 Downloading from: {url}")
|
||||
result = subprocess.run(["curl", "-L", "-o", str(binary_path), url],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
# Make it executable
|
||||
binary_path.chmod(0o755)
|
||||
print(f"✅ yt-dlp binary downloaded to: {binary_path}")
|
||||
|
||||
# Test the binary
|
||||
test_result = subprocess.run([str(binary_path), "--version"],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
if test_result.returncode == 0:
|
||||
version = test_result.stdout.strip()
|
||||
print(f"✅ Binary test successful! Version: {version}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Binary test failed: {test_result.stderr}")
|
||||
return False
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Failed to download yt-dlp binary: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error downloading binary: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def install_yt_dlp():
|
||||
"""Install yt-dlp via pip."""
|
||||
print("📦 Installing yt-dlp...")
|
||||
|
||||
try:
|
||||
result = subprocess.run([sys.executable, "-m", "pip", "install", "yt-dlp"],
|
||||
capture_output=True, text=True, check=True)
|
||||
print("✅ yt-dlp installed successfully!")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Failed to install yt-dlp: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def test_installation():
|
||||
"""Test the installation."""
|
||||
print("\n🧪 Testing installation...")
|
||||
|
||||
# Test FFmpeg
|
||||
if check_ffmpeg():
|
||||
print("✅ FFmpeg is working!")
|
||||
else:
|
||||
print("❌ FFmpeg is not working")
|
||||
return False
|
||||
|
||||
# Test yt-dlp
|
||||
if check_yt_dlp():
|
||||
print("✅ yt-dlp is working!")
|
||||
else:
|
||||
print("❌ yt-dlp is not working")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
print("🍎 macOS Setup for Karaoke Video Downloader")
|
||||
print("=" * 50)
|
||||
|
||||
# Check current status
|
||||
print("🔍 Checking current installation...")
|
||||
ffmpeg_installed = check_ffmpeg()
|
||||
yt_dlp_installed = check_yt_dlp()
|
||||
|
||||
print(f"FFmpeg: {'✅ Installed' if ffmpeg_installed else '❌ Not installed'}")
|
||||
print(f"yt-dlp: {'✅ Installed' if yt_dlp_installed else '❌ Not installed'}")
|
||||
|
||||
if ffmpeg_installed and yt_dlp_installed:
|
||||
print("\n🎉 Everything is already installed and working!")
|
||||
return
|
||||
|
||||
# Install missing components
|
||||
print("\n🚀 Installing missing components...")
|
||||
|
||||
# Install FFmpeg if needed
|
||||
if not ffmpeg_installed:
|
||||
print("\n🎬 FFmpeg Installation Options:")
|
||||
print("1. Install via Homebrew (recommended)")
|
||||
print("2. Download from ffmpeg.org")
|
||||
print("3. Skip FFmpeg installation")
|
||||
|
||||
choice = input("\nChoose an option (1-3): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
if not install_ffmpeg():
|
||||
print("❌ FFmpeg installation failed")
|
||||
return
|
||||
elif choice == "2":
|
||||
print("📥 Please download FFmpeg from: https://ffmpeg.org/download.html")
|
||||
print(" Extract and add to your PATH, then run this script again.")
|
||||
return
|
||||
elif choice == "3":
|
||||
print("⚠️ FFmpeg is required for video processing. Some features may not work.")
|
||||
else:
|
||||
print("❌ Invalid choice")
|
||||
return
|
||||
|
||||
# Install yt-dlp if needed
|
||||
if not yt_dlp_installed:
|
||||
print("\n📦 yt-dlp Installation Options:")
|
||||
print("1. Install via pip (recommended)")
|
||||
print("2. Download binary file")
|
||||
print("3. Skip yt-dlp installation")
|
||||
|
||||
choice = input("\nChoose an option (1-3): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
if not install_yt_dlp():
|
||||
print("❌ yt-dlp installation failed")
|
||||
return
|
||||
elif choice == "2":
|
||||
if not download_yt_dlp_binary():
|
||||
print("❌ yt-dlp binary download failed")
|
||||
return
|
||||
elif choice == "3":
|
||||
print("❌ yt-dlp is required for video downloading.")
|
||||
return
|
||||
else:
|
||||
print("❌ Invalid choice")
|
||||
return
|
||||
|
||||
# Test installation
|
||||
if test_installation():
|
||||
print("\n🎉 Setup completed successfully!")
|
||||
print("You can now use the Karaoke Video Downloader on macOS.")
|
||||
print("Run: python download_karaoke.py --help")
|
||||
else:
|
||||
print("\n❌ Setup failed. Please check the error messages above.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user