uDownloader 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- udownloader-0.1.0/LICENSE +21 -0
- udownloader-0.1.0/MANIFEST.in +4 -0
- udownloader-0.1.0/PKG-INFO +229 -0
- udownloader-0.1.0/README.md +191 -0
- udownloader-0.1.0/pyproject.toml +73 -0
- udownloader-0.1.0/requirements.txt +2 -0
- udownloader-0.1.0/setup.cfg +4 -0
- udownloader-0.1.0/setup.py +10 -0
- udownloader-0.1.0/tests/test_udownloader.py +149 -0
- udownloader-0.1.0/uDownloader.egg-info/PKG-INFO +229 -0
- udownloader-0.1.0/uDownloader.egg-info/SOURCES.txt +20 -0
- udownloader-0.1.0/uDownloader.egg-info/dependency_links.txt +1 -0
- udownloader-0.1.0/uDownloader.egg-info/entry_points.txt +3 -0
- udownloader-0.1.0/uDownloader.egg-info/requires.txt +13 -0
- udownloader-0.1.0/uDownloader.egg-info/top_level.txt +1 -0
- udownloader-0.1.0/youdownload/__init__.py +29 -0
- udownloader-0.1.0/youdownload/async_downloader.py +230 -0
- udownloader-0.1.0/youdownload/cli.py +86 -0
- udownloader-0.1.0/youdownload/config.py +84 -0
- udownloader-0.1.0/youdownload/desktop.py +440 -0
- udownloader-0.1.0/youdownload/downloader.py +137 -0
- udownloader-0.1.0/youdownload/history.py +164 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 uDownloader Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uDownloader
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast, async YouTube/Instagram/TikTok downloader with desktop GUI and CLI
|
|
5
|
+
Author: Developer
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/uDownloader
|
|
8
|
+
Project-URL: Documentation, https://github.com/yourusername/uDownloader#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/yourusername/uDownloader.git
|
|
10
|
+
Project-URL: Issues, https://github.com/yourusername/uDownloader/issues
|
|
11
|
+
Keywords: youtube,instagram,downloader,video,audio,desktop
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Multimedia :: Video
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: yt-dlp>=2024.8.2
|
|
28
|
+
Requires-Dist: PyQt6>=6.6.0
|
|
29
|
+
Provides-Extra: cli
|
|
30
|
+
Provides-Extra: desktop
|
|
31
|
+
Requires-Dist: PyQt6>=6.6.0; extra == "desktop"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=24.8.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.15.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# uDownloader
|
|
40
|
+
|
|
41
|
+
[](https://www.python.org/downloads/)
|
|
42
|
+
[](./docs/CHANGELOG.md)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[](https://github.com/yourusername/uDownloader/actions)
|
|
45
|
+
|
|
46
|
+
A fast, powerful Python application to download YouTube videos or audio. Supports single videos or entire playlists with both CLI and **Desktop GUI**. Uses `yt-dlp` for efficiency and modern format handling.
|
|
47
|
+
|
|
48
|
+
**Available as a pip-installable package!** Install with `pip install udownloader`
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
### Core Features
|
|
53
|
+
- Download video or audio from YouTube, Instagram, TikTok, Twitter, and more
|
|
54
|
+
- Process single content or playlists
|
|
55
|
+
- Command-line interface with quality and format options
|
|
56
|
+
- Configuration file support with sensible defaults
|
|
57
|
+
- Detailed progress reporting during downloads
|
|
58
|
+
- Automatic retry logic for failed downloads
|
|
59
|
+
- Platform-specific organization (YouTube, Instagram, Twitter, TikTok, etc.)
|
|
60
|
+
|
|
61
|
+
### Desktop Application
|
|
62
|
+
- **Modern PyQt6 GUI** for intuitive downloading
|
|
63
|
+
- **Async/Concurrent Downloads** - download multiple videos simultaneously
|
|
64
|
+
- **Download History Tracking** - view all past downloads with statistics
|
|
65
|
+
- **Live Progress Monitoring** - track active downloads in real-time
|
|
66
|
+
- **Queue Management** - manage multiple downloads at once
|
|
67
|
+
- **Settings Panel** - configure quality, output, retries, and concurrent limits
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
### Option 1: Install via pip (Recommended)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# From PyPI (when published)
|
|
75
|
+
pip install udownloader
|
|
76
|
+
|
|
77
|
+
# Or install from source (development mode)
|
|
78
|
+
git clone <repository-url>
|
|
79
|
+
cd uDownloader
|
|
80
|
+
pip install -e .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Option 2: Manual installation from source
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git clone <repository-url>
|
|
87
|
+
cd uDownloader
|
|
88
|
+
python3 -m venv venv
|
|
89
|
+
source venv/bin/activate
|
|
90
|
+
pip install -r requirements.txt
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
After installation, you'll have two commands available:
|
|
94
|
+
- `udownloader` - CLI version
|
|
95
|
+
- `udownloader-desktop` - Desktop GUI version
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
## Technology Stack
|
|
99
|
+
|
|
100
|
+
| Component | Technology | Purpose |
|
|
101
|
+
|-----------|-----------|---------|
|
|
102
|
+
| **Language** | Python 3.8+ | Core application |
|
|
103
|
+
| **Video Downloading** | yt-dlp 2024.8.2+ | Format detection & download |
|
|
104
|
+
| **Desktop GUI** | PyQt6 6.6.0+ | Modern graphical interface |
|
|
105
|
+
| **Async Processing** | asyncio + ThreadPoolExecutor | Concurrent downloads |
|
|
106
|
+
| **Configuration** | JSON | User settings persistence |
|
|
107
|
+
| **History Tracking** | JSON | Download records database |
|
|
108
|
+
| **Testing** | pytest + pytest-cov | Unit testing & coverage |
|
|
109
|
+
| **Code Quality** | black + flake8 | Formatting & linting |
|
|
110
|
+
| **Packaging** | setuptools + wheel | PyPI distribution |
|
|
111
|
+
| **CI/CD** | GitHub Actions | Automated testing & publishing |
|
|
112
|
+
|
|
113
|
+
**Key Dependencies:**
|
|
114
|
+
- `yt-dlp` - Video downloading engine
|
|
115
|
+
- `PyQt6` - Desktop application framework
|
|
116
|
+
- `pytest` - Testing framework (dev only)
|
|
117
|
+
- `black` - Code formatter (dev only)
|
|
118
|
+
- `flake8` - Code linter (dev only)
|
|
119
|
+
|
|
120
|
+
## Quick Start
|
|
121
|
+
|
|
122
|
+
### CLI Mode
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Initialize config file (creates ~/.uDownloader/config.json)
|
|
126
|
+
udownloader --init-config
|
|
127
|
+
|
|
128
|
+
# Download best quality video
|
|
129
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID
|
|
130
|
+
|
|
131
|
+
# Download as MP3 audio only
|
|
132
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --audio
|
|
133
|
+
|
|
134
|
+
# Download specific quality (best, 1080p, 720p, 480p, 360p)
|
|
135
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --quality 720p
|
|
136
|
+
|
|
137
|
+
# Download entire playlist to custom location
|
|
138
|
+
udownloader --url https://youtube.com/playlist?list=PLAYLIST_ID --output ~/Downloads --retries 5
|
|
139
|
+
|
|
140
|
+
# Verbose logging for debugging
|
|
141
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --verbose
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Desktop Application
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Launch desktop app (from installed package)
|
|
148
|
+
udownloader-desktop
|
|
149
|
+
|
|
150
|
+
# Or if running from source directory
|
|
151
|
+
python launcher_desktop.py
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Features in Desktop App:**
|
|
155
|
+
- **Download Tab**: Paste URLs and start downloads with quality selection
|
|
156
|
+
- **Queue Tab**: Monitor active downloads in real-time
|
|
157
|
+
- **History Tab**: View all previous downloads with details
|
|
158
|
+
- **Statistics Tab**: See download stats by platform and overall success rate
|
|
159
|
+
- **Settings**: Configure output directory, quality, concurrent limits, and retry attempts
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
The configuration file is automatically created at `~/.uDownloader/config.json` when you run `--init-config`.
|
|
164
|
+
|
|
165
|
+
### Default Settings:
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"output_dir": "uDownload",
|
|
169
|
+
"audio_only": false,
|
|
170
|
+
"video_quality": "best",
|
|
171
|
+
"audio_quality": "192",
|
|
172
|
+
"format_preference": "mp4",
|
|
173
|
+
"max_concurrent_downloads": 1,
|
|
174
|
+
"timeout": 300,
|
|
175
|
+
"retries": 3,
|
|
176
|
+
"verbose": false
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
You can edit this file directly to change defaults, or override with command-line arguments (CLI) or settings dialog (Desktop app).
|
|
181
|
+
|
|
182
|
+
## Command-line Options
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
--url URL, -u URL YouTube video or playlist URL (required for downloads)
|
|
186
|
+
--output OUTPUT, -o OUTPUT Directory to save downloads (default: config or 'uDownload')
|
|
187
|
+
--audio, -a Download audio only (mp3)
|
|
188
|
+
--quality {best,1080p,720p,480p,360p} Video quality preference
|
|
189
|
+
--config CONFIG, -c CONFIG Path to custom config file
|
|
190
|
+
--init-config Create default config file and exit
|
|
191
|
+
--retries RETRIES, -r RETRIES Number of retries on failure (default: 3)
|
|
192
|
+
--verbose, -v Show detailed logging
|
|
193
|
+
--help, -h Show help message
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Architecture
|
|
197
|
+
|
|
198
|
+
### Modules
|
|
199
|
+
- **`cli.py`** - Command-line interface
|
|
200
|
+
- **`downloader.py`** - Single download handler
|
|
201
|
+
- **`async_downloader.py`** - Async/concurrent download manager
|
|
202
|
+
- **`desktop.py`** - PyQt6 desktop GUI application
|
|
203
|
+
- **`history.py`** - Download history tracking and statistics
|
|
204
|
+
- **`config.py`** - Configuration management
|
|
205
|
+
|
|
206
|
+
### Key Features in Code
|
|
207
|
+
- **`AsyncDownloader`** class handles concurrent downloads using ThreadPoolExecutor
|
|
208
|
+
- **`DownloadHistory`** class manages download records in JSON format
|
|
209
|
+
- **`uDownloaderApp`** is the main desktop application with multi-tab interface
|
|
210
|
+
- **`DownloadWorker`** runs downloads in separate thread to keep UI responsive
|
|
211
|
+
|
|
212
|
+
## Documentation
|
|
213
|
+
|
|
214
|
+
Comprehensive documentation is available in the [docs](docs/) folder:
|
|
215
|
+
|
|
216
|
+
- **[Installation Guide](docs/INSTALLATION.md)** - Platform-specific installation instructions
|
|
217
|
+
- **[Quick Reference](docs/QUICK_REFERENCE.md)** - Commands, workflows, and FAQ
|
|
218
|
+
- **[Contributing Guide](docs/CONTRIBUTING.md)** - Development setup and contribution guidelines
|
|
219
|
+
- **[CI/CD Setup](docs/CICD_SETUP.md)** - GitHub Actions and testing infrastructure
|
|
220
|
+
- **[Changelog](docs/CHANGELOG.md)** - Version history and planned features
|
|
221
|
+
- **[Security Policy](docs/SECURITY.md)** - Vulnerability reporting and security practices
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
uDownloader is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
**Made with ❤️ for video downloaders everywhere**
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# uDownloader
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](./docs/CHANGELOG.md)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/yourusername/uDownloader/actions)
|
|
7
|
+
|
|
8
|
+
A fast, powerful Python application to download YouTube videos or audio. Supports single videos or entire playlists with both CLI and **Desktop GUI**. Uses `yt-dlp` for efficiency and modern format handling.
|
|
9
|
+
|
|
10
|
+
**Available as a pip-installable package!** Install with `pip install udownloader`
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
### Core Features
|
|
15
|
+
- Download video or audio from YouTube, Instagram, TikTok, Twitter, and more
|
|
16
|
+
- Process single content or playlists
|
|
17
|
+
- Command-line interface with quality and format options
|
|
18
|
+
- Configuration file support with sensible defaults
|
|
19
|
+
- Detailed progress reporting during downloads
|
|
20
|
+
- Automatic retry logic for failed downloads
|
|
21
|
+
- Platform-specific organization (YouTube, Instagram, Twitter, TikTok, etc.)
|
|
22
|
+
|
|
23
|
+
### Desktop Application
|
|
24
|
+
- **Modern PyQt6 GUI** for intuitive downloading
|
|
25
|
+
- **Async/Concurrent Downloads** - download multiple videos simultaneously
|
|
26
|
+
- **Download History Tracking** - view all past downloads with statistics
|
|
27
|
+
- **Live Progress Monitoring** - track active downloads in real-time
|
|
28
|
+
- **Queue Management** - manage multiple downloads at once
|
|
29
|
+
- **Settings Panel** - configure quality, output, retries, and concurrent limits
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
### Option 1: Install via pip (Recommended)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# From PyPI (when published)
|
|
37
|
+
pip install udownloader
|
|
38
|
+
|
|
39
|
+
# Or install from source (development mode)
|
|
40
|
+
git clone <repository-url>
|
|
41
|
+
cd uDownloader
|
|
42
|
+
pip install -e .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Option 2: Manual installation from source
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone <repository-url>
|
|
49
|
+
cd uDownloader
|
|
50
|
+
python3 -m venv venv
|
|
51
|
+
source venv/bin/activate
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
After installation, you'll have two commands available:
|
|
56
|
+
- `udownloader` - CLI version
|
|
57
|
+
- `udownloader-desktop` - Desktop GUI version
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Technology Stack
|
|
61
|
+
|
|
62
|
+
| Component | Technology | Purpose |
|
|
63
|
+
|-----------|-----------|---------|
|
|
64
|
+
| **Language** | Python 3.8+ | Core application |
|
|
65
|
+
| **Video Downloading** | yt-dlp 2024.8.2+ | Format detection & download |
|
|
66
|
+
| **Desktop GUI** | PyQt6 6.6.0+ | Modern graphical interface |
|
|
67
|
+
| **Async Processing** | asyncio + ThreadPoolExecutor | Concurrent downloads |
|
|
68
|
+
| **Configuration** | JSON | User settings persistence |
|
|
69
|
+
| **History Tracking** | JSON | Download records database |
|
|
70
|
+
| **Testing** | pytest + pytest-cov | Unit testing & coverage |
|
|
71
|
+
| **Code Quality** | black + flake8 | Formatting & linting |
|
|
72
|
+
| **Packaging** | setuptools + wheel | PyPI distribution |
|
|
73
|
+
| **CI/CD** | GitHub Actions | Automated testing & publishing |
|
|
74
|
+
|
|
75
|
+
**Key Dependencies:**
|
|
76
|
+
- `yt-dlp` - Video downloading engine
|
|
77
|
+
- `PyQt6` - Desktop application framework
|
|
78
|
+
- `pytest` - Testing framework (dev only)
|
|
79
|
+
- `black` - Code formatter (dev only)
|
|
80
|
+
- `flake8` - Code linter (dev only)
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### CLI Mode
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Initialize config file (creates ~/.uDownloader/config.json)
|
|
88
|
+
udownloader --init-config
|
|
89
|
+
|
|
90
|
+
# Download best quality video
|
|
91
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID
|
|
92
|
+
|
|
93
|
+
# Download as MP3 audio only
|
|
94
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --audio
|
|
95
|
+
|
|
96
|
+
# Download specific quality (best, 1080p, 720p, 480p, 360p)
|
|
97
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --quality 720p
|
|
98
|
+
|
|
99
|
+
# Download entire playlist to custom location
|
|
100
|
+
udownloader --url https://youtube.com/playlist?list=PLAYLIST_ID --output ~/Downloads --retries 5
|
|
101
|
+
|
|
102
|
+
# Verbose logging for debugging
|
|
103
|
+
udownloader --url https://youtube.com/watch?v=VIDEO_ID --verbose
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Desktop Application
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Launch desktop app (from installed package)
|
|
110
|
+
udownloader-desktop
|
|
111
|
+
|
|
112
|
+
# Or if running from source directory
|
|
113
|
+
python launcher_desktop.py
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Features in Desktop App:**
|
|
117
|
+
- **Download Tab**: Paste URLs and start downloads with quality selection
|
|
118
|
+
- **Queue Tab**: Monitor active downloads in real-time
|
|
119
|
+
- **History Tab**: View all previous downloads with details
|
|
120
|
+
- **Statistics Tab**: See download stats by platform and overall success rate
|
|
121
|
+
- **Settings**: Configure output directory, quality, concurrent limits, and retry attempts
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
The configuration file is automatically created at `~/.uDownloader/config.json` when you run `--init-config`.
|
|
126
|
+
|
|
127
|
+
### Default Settings:
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"output_dir": "uDownload",
|
|
131
|
+
"audio_only": false,
|
|
132
|
+
"video_quality": "best",
|
|
133
|
+
"audio_quality": "192",
|
|
134
|
+
"format_preference": "mp4",
|
|
135
|
+
"max_concurrent_downloads": 1,
|
|
136
|
+
"timeout": 300,
|
|
137
|
+
"retries": 3,
|
|
138
|
+
"verbose": false
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
You can edit this file directly to change defaults, or override with command-line arguments (CLI) or settings dialog (Desktop app).
|
|
143
|
+
|
|
144
|
+
## Command-line Options
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
--url URL, -u URL YouTube video or playlist URL (required for downloads)
|
|
148
|
+
--output OUTPUT, -o OUTPUT Directory to save downloads (default: config or 'uDownload')
|
|
149
|
+
--audio, -a Download audio only (mp3)
|
|
150
|
+
--quality {best,1080p,720p,480p,360p} Video quality preference
|
|
151
|
+
--config CONFIG, -c CONFIG Path to custom config file
|
|
152
|
+
--init-config Create default config file and exit
|
|
153
|
+
--retries RETRIES, -r RETRIES Number of retries on failure (default: 3)
|
|
154
|
+
--verbose, -v Show detailed logging
|
|
155
|
+
--help, -h Show help message
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Architecture
|
|
159
|
+
|
|
160
|
+
### Modules
|
|
161
|
+
- **`cli.py`** - Command-line interface
|
|
162
|
+
- **`downloader.py`** - Single download handler
|
|
163
|
+
- **`async_downloader.py`** - Async/concurrent download manager
|
|
164
|
+
- **`desktop.py`** - PyQt6 desktop GUI application
|
|
165
|
+
- **`history.py`** - Download history tracking and statistics
|
|
166
|
+
- **`config.py`** - Configuration management
|
|
167
|
+
|
|
168
|
+
### Key Features in Code
|
|
169
|
+
- **`AsyncDownloader`** class handles concurrent downloads using ThreadPoolExecutor
|
|
170
|
+
- **`DownloadHistory`** class manages download records in JSON format
|
|
171
|
+
- **`uDownloaderApp`** is the main desktop application with multi-tab interface
|
|
172
|
+
- **`DownloadWorker`** runs downloads in separate thread to keep UI responsive
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
Comprehensive documentation is available in the [docs](docs/) folder:
|
|
177
|
+
|
|
178
|
+
- **[Installation Guide](docs/INSTALLATION.md)** - Platform-specific installation instructions
|
|
179
|
+
- **[Quick Reference](docs/QUICK_REFERENCE.md)** - Commands, workflows, and FAQ
|
|
180
|
+
- **[Contributing Guide](docs/CONTRIBUTING.md)** - Development setup and contribution guidelines
|
|
181
|
+
- **[CI/CD Setup](docs/CICD_SETUP.md)** - GitHub Actions and testing infrastructure
|
|
182
|
+
- **[Changelog](docs/CHANGELOG.md)** - Version history and planned features
|
|
183
|
+
- **[Security Policy](docs/SECURITY.md)** - Vulnerability reporting and security practices
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
uDownloader is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
**Made with ❤️ for video downloaders everywhere**
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "uDownloader"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Fast, async YouTube/Instagram/TikTok downloader with desktop GUI and CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Developer"}]
|
|
13
|
+
keywords = ["youtube", "instagram", "downloader", "video", "audio", "desktop"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: X11 Applications :: Qt",
|
|
17
|
+
"Intended Audience :: End Users/Desktop",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Multimedia :: Video",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = [
|
|
30
|
+
"yt-dlp>=2024.8.2",
|
|
31
|
+
"PyQt6>=6.6.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
cli = []
|
|
36
|
+
desktop = ["PyQt6>=6.6.0"]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.0",
|
|
39
|
+
"pytest-cov>=4.0",
|
|
40
|
+
"black>=24.8.0",
|
|
41
|
+
"ruff>=0.15.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/yourusername/uDownloader"
|
|
46
|
+
Documentation = "https://github.com/yourusername/uDownloader#readme"
|
|
47
|
+
Repository = "https://github.com/yourusername/uDownloader.git"
|
|
48
|
+
Issues = "https://github.com/yourusername/uDownloader/issues"
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
udownloader = "youdownload.cli:main"
|
|
52
|
+
udownloader-desktop = "youdownload.desktop:main"
|
|
53
|
+
|
|
54
|
+
[tool.setuptools]
|
|
55
|
+
packages = ["youdownload"]
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.package-data]
|
|
58
|
+
youdownload = []
|
|
59
|
+
|
|
60
|
+
[tool.black]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 100
|
|
66
|
+
target-version = "py38"
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
testpaths = ["tests"]
|
|
70
|
+
python_files = ["test_*.py"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
select = ["E", "F", "W"] # pycodestyle, Pyflakes, warnings
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Tests for youdownload package."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from youdownload import __version__
|
|
5
|
+
from youdownload.config import load_config, DEFAULT_CONFIG
|
|
6
|
+
from youdownload.history import DownloadHistory
|
|
7
|
+
from youdownload.async_downloader import AsyncDownloader
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import tempfile
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestVersion:
|
|
13
|
+
"""Test package version."""
|
|
14
|
+
|
|
15
|
+
def test_version_exists(self):
|
|
16
|
+
"""Test that version is defined."""
|
|
17
|
+
assert __version__ is not None
|
|
18
|
+
assert isinstance(__version__, str)
|
|
19
|
+
|
|
20
|
+
def test_version_format(self):
|
|
21
|
+
"""Test version follows semantic versioning."""
|
|
22
|
+
parts = __version__.split(".")
|
|
23
|
+
assert len(parts) >= 2
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestConfig:
|
|
27
|
+
"""Test configuration management."""
|
|
28
|
+
|
|
29
|
+
def test_default_config_structure(self):
|
|
30
|
+
"""Test default config has required keys."""
|
|
31
|
+
required_keys = [
|
|
32
|
+
"output_dir",
|
|
33
|
+
"audio_only",
|
|
34
|
+
"video_quality",
|
|
35
|
+
"audio_quality",
|
|
36
|
+
"max_concurrent_downloads",
|
|
37
|
+
"retries",
|
|
38
|
+
]
|
|
39
|
+
for key in required_keys:
|
|
40
|
+
assert key in DEFAULT_CONFIG
|
|
41
|
+
|
|
42
|
+
def test_load_config_with_no_file(self):
|
|
43
|
+
"""Test loading config returns defaults when no file exists."""
|
|
44
|
+
# Use temp file that doesn't exist
|
|
45
|
+
config = load_config("/tmp/nonexistent_config_12345.json")
|
|
46
|
+
assert config["output_dir"] == "uDownload"
|
|
47
|
+
assert config["video_quality"] == "best"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestHistory:
|
|
51
|
+
"""Test download history tracking."""
|
|
52
|
+
|
|
53
|
+
def test_history_initialization(self):
|
|
54
|
+
"""Test history object initializes correctly."""
|
|
55
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
56
|
+
history_file = Path(f.name)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
history = DownloadHistory(history_file)
|
|
60
|
+
assert history.history_file == history_file
|
|
61
|
+
assert history_file.exists()
|
|
62
|
+
finally:
|
|
63
|
+
if history_file.exists():
|
|
64
|
+
history_file.unlink()
|
|
65
|
+
|
|
66
|
+
def test_add_and_get_history(self):
|
|
67
|
+
"""Test adding and retrieving history."""
|
|
68
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
69
|
+
history_file = Path(f.name)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
history = DownloadHistory(history_file)
|
|
73
|
+
|
|
74
|
+
record = {
|
|
75
|
+
"title": "Test Video",
|
|
76
|
+
"platform": "YouTube",
|
|
77
|
+
"url": "https://youtube.com/watch?v=test",
|
|
78
|
+
"success": True,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
history.add_download(record)
|
|
82
|
+
records = history.get_history()
|
|
83
|
+
|
|
84
|
+
assert len(records) > 0
|
|
85
|
+
assert records[0]["title"] == "Test Video"
|
|
86
|
+
finally:
|
|
87
|
+
if history_file.exists():
|
|
88
|
+
history_file.unlink()
|
|
89
|
+
|
|
90
|
+
def test_get_stats(self):
|
|
91
|
+
"""Test statistics calculation."""
|
|
92
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
93
|
+
history_file = Path(f.name)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
history = DownloadHistory(history_file)
|
|
97
|
+
|
|
98
|
+
# Add test records
|
|
99
|
+
history.add_download(
|
|
100
|
+
{
|
|
101
|
+
"title": "Video 1",
|
|
102
|
+
"platform": "YouTube",
|
|
103
|
+
"url": "https://example.com",
|
|
104
|
+
"success": True,
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
history.add_download(
|
|
108
|
+
{
|
|
109
|
+
"title": "Video 2",
|
|
110
|
+
"platform": "Instagram",
|
|
111
|
+
"url": "https://example.com",
|
|
112
|
+
"success": False,
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
stats = history.get_stats()
|
|
117
|
+
assert stats["total_downloads"] == 2
|
|
118
|
+
assert stats["successful"] == 1
|
|
119
|
+
assert stats["failed"] == 1
|
|
120
|
+
finally:
|
|
121
|
+
if history_file.exists():
|
|
122
|
+
history_file.unlink()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TestAsyncDownloader:
|
|
126
|
+
"""Test async downloader."""
|
|
127
|
+
|
|
128
|
+
def test_initialization(self):
|
|
129
|
+
"""Test AsyncDownloader initializes correctly."""
|
|
130
|
+
downloader = AsyncDownloader(max_concurrent=2)
|
|
131
|
+
assert downloader.max_concurrent == 2
|
|
132
|
+
assert downloader.executor is not None
|
|
133
|
+
|
|
134
|
+
def test_platform_detection(self):
|
|
135
|
+
"""Test platform detection."""
|
|
136
|
+
downloader = AsyncDownloader()
|
|
137
|
+
|
|
138
|
+
assert downloader.detect_platform("https://youtube.com/watch?v=test") == "YouTube"
|
|
139
|
+
assert downloader.detect_platform("https://youtu.be/test") == "YouTube"
|
|
140
|
+
assert downloader.detect_platform("https://instagram.com/p/test") == "Instagram"
|
|
141
|
+
assert downloader.detect_platform("https://tiktok.com/@user/video/123") == "TikTok"
|
|
142
|
+
assert downloader.detect_platform("https://twitter.com/user/status/123") == "Twitter"
|
|
143
|
+
assert downloader.detect_platform("https://x.com/user/status/123") == "Twitter"
|
|
144
|
+
assert downloader.detect_platform("https://vimeo.com/123") == "Vimeo"
|
|
145
|
+
assert downloader.detect_platform("https://facebook.com/video/123") == "Facebook"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
pytest.main([__file__, "-v"])
|