ytfetch 1.0.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.
- ytfetch-1.0.0/.github/workflows/publish.yaml +35 -0
- ytfetch-1.0.0/.gitignore +4 -0
- ytfetch-1.0.0/LICENSE +21 -0
- ytfetch-1.0.0/PKG-INFO +176 -0
- ytfetch-1.0.0/README-pypi.md +129 -0
- ytfetch-1.0.0/README.md +162 -0
- ytfetch-1.0.0/pyproject.toml +29 -0
- ytfetch-1.0.0/src/ytfetch/__init__.py +0 -0
- ytfetch-1.0.0/src/ytfetch/cli.py +247 -0
- ytfetch-1.0.0/src/ytfetch/utils.py +64 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
name: Build and upload to PyPI
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: pypi
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.12'
|
|
25
|
+
|
|
26
|
+
- name: Install build tool
|
|
27
|
+
run: pip install build
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: python -m build
|
|
31
|
+
|
|
32
|
+
- name: Publish to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
34
|
+
with:
|
|
35
|
+
skip-existing: true
|
ytfetch-1.0.0/.gitignore
ADDED
ytfetch-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 voidsnax
|
|
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.
|
ytfetch-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ytfetch
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A wrapper for yt-dlp with custom simple fetching flags
|
|
5
|
+
Project-URL: Homepage, https://github.com/voidsnax/reVidx
|
|
6
|
+
Author: voidsnax
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: converter,h264,mp3,playlist,youtubedownloader
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Requires-Dist: colorama
|
|
12
|
+
Requires-Dist: yt-dlp>=2025.12.08
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# ytfetch
|
|
16
|
+
|
|
17
|
+
**ytfetch** simplifies media downloading and playlist management by adding "Smart Fetching,"playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
|
|
18
|
+
|
|
19
|
+
## ✨ Features
|
|
20
|
+
|
|
21
|
+
* **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
|
|
22
|
+
* **Search:** Use `-list` to display or search across single or multiple playlists contents.
|
|
23
|
+
* **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
|
|
24
|
+
* **Video Quality:** Mention preffered video quality directly.
|
|
25
|
+
* **Native Compatibility:** Can also pass through any `yt-dlp` flag.
|
|
26
|
+
* **Minimal Output logs:** Clean and minimal logs.
|
|
27
|
+
|
|
28
|
+
## 📋 Requirements
|
|
29
|
+
|
|
30
|
+
- [Python](https://www.python.org/): 3.8 or higher.
|
|
31
|
+
- [FFmpeg](https://ffmpeg.org/) and [FFprobe](https://ffmpeg.org/ffprobe.html): Should available in your system PATH.
|
|
32
|
+
Download builds from [here](https://ffmpeg.org/download.html)
|
|
33
|
+
|
|
34
|
+
<details>
|
|
35
|
+
<summary>or</summary>
|
|
36
|
+
|
|
37
|
+
- On Windows:
|
|
38
|
+
```bash
|
|
39
|
+
winget install BtbN.FFmpeg.GPL
|
|
40
|
+
```
|
|
41
|
+
or
|
|
42
|
+
```bash
|
|
43
|
+
winget install Gyan.FFmpeg
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- On Termux (android):
|
|
47
|
+
```bash
|
|
48
|
+
pkg install ffmpeg
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- On Mac (using Homebrew):
|
|
52
|
+
```bash
|
|
53
|
+
brew install ffmpeg
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- On Arch Linux:
|
|
57
|
+
```bash
|
|
58
|
+
sudo pacman -S ffmpeg
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- On Ubuntu/Debian:
|
|
62
|
+
```bash
|
|
63
|
+
sudo apt install ffmpeg
|
|
64
|
+
```
|
|
65
|
+
</details>
|
|
66
|
+
- [Deno](https://deno.com/): Or other JavaScript runtime/engine like [node.js](https://nodejs.org/en) or [bun](https://bun.sh/)
|
|
67
|
+
|
|
68
|
+
Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## 📦 Installation
|
|
72
|
+
|
|
73
|
+
### via pip
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install ytfetch
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### via source
|
|
80
|
+
```bash
|
|
81
|
+
git clone <your-repo-url>
|
|
82
|
+
cd <your-repo>
|
|
83
|
+
pip install -e .
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 🚀 Usage
|
|
87
|
+
|
|
88
|
+
### General Syntax and Options
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
usage: Usage: ytfetch [yt-dlp OPTIONS] URL ... [OPTIONS]
|
|
92
|
+
|
|
93
|
+
options:
|
|
94
|
+
-h Show this help message and exit.
|
|
95
|
+
-help Show yt-dlp help message.
|
|
96
|
+
-avcmp3 Download video in AVC (h.264) format (mp4) + extract audio
|
|
97
|
+
-q QUALITY Video quality (e.g., 1080, 720). Default is 1080.
|
|
98
|
+
-mp3 Extract audio only as MP3
|
|
99
|
+
-audio Extract audio only
|
|
100
|
+
-fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
|
|
101
|
+
Multiple value must number of playlist URLs provided
|
|
102
|
+
match number of playlist URLs provided.
|
|
103
|
+
-list [NAME] List playlist contents. Accepts values for searching across playlist
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
*Note: Arguments are position-independent except for `-list` and `-flag` where URLs can be mentioned after it*
|
|
107
|
+
|
|
108
|
+
### Examples
|
|
109
|
+
|
|
110
|
+
#### Basic Downloading
|
|
111
|
+
|
|
112
|
+
Download a video at the default quality (1080p)
|
|
113
|
+
```bash
|
|
114
|
+
ytfetch "VIDEO_URL"
|
|
115
|
+
```
|
|
116
|
+
Download a video in 720p
|
|
117
|
+
```bash
|
|
118
|
+
ytfetch "VIDEO_URL" -q 720
|
|
119
|
+
```
|
|
120
|
+
#### Format Conversion
|
|
121
|
+
|
|
122
|
+
Download as AVC(H.264) and Mp3 in MP4 Container
|
|
123
|
+
```bash
|
|
124
|
+
ytfetch "VIDEO_URL" -avcmp3
|
|
125
|
+
```
|
|
126
|
+
Extract Audio Only in Mp3
|
|
127
|
+
```bash
|
|
128
|
+
ytfetch "VIDEO_URL" -mp3
|
|
129
|
+
```
|
|
130
|
+
#### Smart Fetching
|
|
131
|
+
|
|
132
|
+
Global Limit
|
|
133
|
+
```bash
|
|
134
|
+
ytfetch "PLAYLIST_URL_1" "PLAYLIST_URL_2" -fetch "1-10"
|
|
135
|
+
```
|
|
136
|
+
Mapped Ranges
|
|
137
|
+
```bash
|
|
138
|
+
ytfetch "PLAYLIST_A" "PLAYLIST_B" -fetch "1-5" "10-15"
|
|
139
|
+
```
|
|
140
|
+
*Note: The number of fetch arguments must match the number of URLs*
|
|
141
|
+
|
|
142
|
+
#### Listing and Searching
|
|
143
|
+
|
|
144
|
+
List a Playlist
|
|
145
|
+
```bash
|
|
146
|
+
ytfetch "PLAYLIST_URL" -list
|
|
147
|
+
```
|
|
148
|
+
Search through a playlist
|
|
149
|
+
```bash
|
|
150
|
+
ytfetch "PLAYLIST_URL" -list "remix"
|
|
151
|
+
```
|
|
152
|
+
Combined Fetch & Search
|
|
153
|
+
```bash
|
|
154
|
+
ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Passthrough Options
|
|
158
|
+
|
|
159
|
+
You can append any standard yt-dlp option to your command. Both long and short flags are supported.
|
|
160
|
+
|
|
161
|
+
Download via Proxy
|
|
162
|
+
```bash
|
|
163
|
+
ytfetch --proxy "socks5://127.0.0.1:1080" "VIDEO_URL"
|
|
164
|
+
```
|
|
165
|
+
Use Cookies
|
|
166
|
+
```bash
|
|
167
|
+
ytfetch --cookies "cookies.txt" "VIDEO_URL"
|
|
168
|
+
```
|
|
169
|
+
Limit Speed
|
|
170
|
+
```bash
|
|
171
|
+
ytfetch -r "500K" "VIDEO_URL"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 🔖 License
|
|
175
|
+
|
|
176
|
+
MIT License
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# ytfetch
|
|
2
|
+
|
|
3
|
+
**ytfetch** simplifies media downloading and playlist management by adding "Smart Fetching,"playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
* **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
|
|
8
|
+
* **Search:** Use `-list` to display or search across single or multiple playlists contents.
|
|
9
|
+
* **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
|
|
10
|
+
* **Video Quality:** Mention preffered video quality directly.
|
|
11
|
+
* **Native Compatibility:** Can also pass through any `yt-dlp` flag.
|
|
12
|
+
* **Minimal Output logs:** Clean and minimal logs.
|
|
13
|
+
|
|
14
|
+
## 📋 Requirements
|
|
15
|
+
|
|
16
|
+
- [Python](https://www.python.org/): 3.8 or higher.
|
|
17
|
+
- [FFmpeg](https://ffmpeg.org/) and [FFprobe](https://ffmpeg.org/ffprobe.html): Should available in your system PATH.
|
|
18
|
+
Download builds from [here](https://ffmpeg.org/download.html)
|
|
19
|
+
- [Deno](https://deno.com/): Or other JavaScript runtime/engine like [node.js](https://nodejs.org/en) or [bun](https://bun.sh/)
|
|
20
|
+
|
|
21
|
+
Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## 📦 Installation
|
|
25
|
+
|
|
26
|
+
### via pip
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ytfetch
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### via source
|
|
33
|
+
```bash
|
|
34
|
+
git clone <your-repo-url>
|
|
35
|
+
cd <your-repo>
|
|
36
|
+
pip install -e .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 🚀 Usage
|
|
40
|
+
|
|
41
|
+
### General Syntax and Options
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
usage: Usage: ytfetch [yt-dlp OPTIONS] URL ... [OPTIONS]
|
|
45
|
+
|
|
46
|
+
options:
|
|
47
|
+
-h Show this help message and exit.
|
|
48
|
+
-help Show yt-dlp help message.
|
|
49
|
+
-avcmp3 Download video in AVC (h.264) format (mp4) + extract audio
|
|
50
|
+
-q QUALITY Video quality (e.g., 1080, 720). Default is 1080.
|
|
51
|
+
-mp3 Extract audio only as MP3
|
|
52
|
+
-audio Extract audio only
|
|
53
|
+
-fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
|
|
54
|
+
Multiple value must number of playlist URLs provided
|
|
55
|
+
match number of playlist URLs provided.
|
|
56
|
+
-list [NAME] List playlist contents. Accepts values for searching across playlist
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
*Note: Arguments are position-independent except for `-list` and `-flag` where URLs can be mentioned after it*
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
#### Basic Downloading
|
|
64
|
+
|
|
65
|
+
Download a video at the default quality (1080p)
|
|
66
|
+
```bash
|
|
67
|
+
ytfetch "VIDEO_URL"
|
|
68
|
+
```
|
|
69
|
+
Download a video in 720p
|
|
70
|
+
```bash
|
|
71
|
+
ytfetch "VIDEO_URL" -q 720
|
|
72
|
+
```
|
|
73
|
+
#### Format Conversion
|
|
74
|
+
|
|
75
|
+
Download as AVC(H.264) and Mp3 in MP4 Container
|
|
76
|
+
```bash
|
|
77
|
+
ytfetch "VIDEO_URL" -avcmp3
|
|
78
|
+
```
|
|
79
|
+
Extract Audio Only in Mp3
|
|
80
|
+
```bash
|
|
81
|
+
ytfetch "VIDEO_URL" -mp3
|
|
82
|
+
```
|
|
83
|
+
#### Smart Fetching
|
|
84
|
+
|
|
85
|
+
Global Limit
|
|
86
|
+
```bash
|
|
87
|
+
ytfetch "PLAYLIST_URL_1" "PLAYLIST_URL_2" -fetch "1-10"
|
|
88
|
+
```
|
|
89
|
+
Mapped Ranges
|
|
90
|
+
```bash
|
|
91
|
+
ytfetch "PLAYLIST_A" "PLAYLIST_B" -fetch "1-5" "10-15"
|
|
92
|
+
```
|
|
93
|
+
*Note: The number of fetch arguments must match the number of URLs*
|
|
94
|
+
|
|
95
|
+
#### Listing and Searching
|
|
96
|
+
|
|
97
|
+
List a Playlist
|
|
98
|
+
```bash
|
|
99
|
+
ytfetch "PLAYLIST_URL" -list
|
|
100
|
+
```
|
|
101
|
+
Search through a playlist
|
|
102
|
+
```bash
|
|
103
|
+
ytfetch "PLAYLIST_URL" -list "remix"
|
|
104
|
+
```
|
|
105
|
+
Combined Fetch & Search
|
|
106
|
+
```bash
|
|
107
|
+
ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Passthrough Options
|
|
111
|
+
|
|
112
|
+
You can append any standard yt-dlp option to your command. Both long and short flags are supported.
|
|
113
|
+
|
|
114
|
+
Download via Proxy
|
|
115
|
+
```bash
|
|
116
|
+
ytfetch --proxy "socks5://127.0.0.1:1080" "VIDEO_URL"
|
|
117
|
+
```
|
|
118
|
+
Use Cookies
|
|
119
|
+
```bash
|
|
120
|
+
ytfetch --cookies "cookies.txt" "VIDEO_URL"
|
|
121
|
+
```
|
|
122
|
+
Limit Speed
|
|
123
|
+
```bash
|
|
124
|
+
ytfetch -r "500K" "VIDEO_URL"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 🔖 License
|
|
128
|
+
|
|
129
|
+
MIT License
|
ytfetch-1.0.0/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# ytfetch
|
|
2
|
+
|
|
3
|
+
**ytfetch** simplifies media downloading and playlist management by adding "Smart Fetching,"playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
* **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
|
|
8
|
+
* **Search:** Use `-list` to display or search across single or multiple playlists contents.
|
|
9
|
+
* **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
|
|
10
|
+
* **Video Quality:** Mention preffered video quality directly.
|
|
11
|
+
* **Native Compatibility:** Can also pass through any `yt-dlp` flag.
|
|
12
|
+
* **Minimal Output logs:** Clean and minimal logs.
|
|
13
|
+
|
|
14
|
+
## 📋 Requirements
|
|
15
|
+
|
|
16
|
+
- [Python](https://www.python.org/): 3.8 or higher.
|
|
17
|
+
- [FFmpeg](https://ffmpeg.org/) and [FFprobe](https://ffmpeg.org/ffprobe.html): Should available in your system PATH.
|
|
18
|
+
Download builds from [here](https://ffmpeg.org/download.html)
|
|
19
|
+
|
|
20
|
+
<details>
|
|
21
|
+
<summary>or</summary>
|
|
22
|
+
|
|
23
|
+
- On Windows:
|
|
24
|
+
```bash
|
|
25
|
+
winget install BtbN.FFmpeg.GPL
|
|
26
|
+
```
|
|
27
|
+
or
|
|
28
|
+
```bash
|
|
29
|
+
winget install Gyan.FFmpeg
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- On Termux (android):
|
|
33
|
+
```bash
|
|
34
|
+
pkg install ffmpeg
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- On Mac (using Homebrew):
|
|
38
|
+
```bash
|
|
39
|
+
brew install ffmpeg
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- On Arch Linux:
|
|
43
|
+
```bash
|
|
44
|
+
sudo pacman -S ffmpeg
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- On Ubuntu/Debian:
|
|
48
|
+
```bash
|
|
49
|
+
sudo apt install ffmpeg
|
|
50
|
+
```
|
|
51
|
+
</details>
|
|
52
|
+
- [Deno](https://deno.com/): Or other JavaScript runtime/engine like [node.js](https://nodejs.org/en) or [bun](https://bun.sh/)
|
|
53
|
+
|
|
54
|
+
Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## 📦 Installation
|
|
58
|
+
|
|
59
|
+
### via pip
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install ytfetch
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### via source
|
|
66
|
+
```bash
|
|
67
|
+
git clone <your-repo-url>
|
|
68
|
+
cd <your-repo>
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 🚀 Usage
|
|
73
|
+
|
|
74
|
+
### General Syntax and Options
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
usage: Usage: ytfetch [yt-dlp OPTIONS] URL ... [OPTIONS]
|
|
78
|
+
|
|
79
|
+
options:
|
|
80
|
+
-h Show this help message and exit.
|
|
81
|
+
-help Show yt-dlp help message.
|
|
82
|
+
-avcmp3 Download video in AVC (h.264) format (mp4) + extract audio
|
|
83
|
+
-q QUALITY Video quality (e.g., 1080, 720). Default is 1080.
|
|
84
|
+
-mp3 Extract audio only as MP3
|
|
85
|
+
-audio Extract audio only
|
|
86
|
+
-fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
|
|
87
|
+
Multiple value must number of playlist URLs provided
|
|
88
|
+
match number of playlist URLs provided.
|
|
89
|
+
-list [NAME] List playlist contents. Accepts values for searching across playlist
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
*Note: Arguments are position-independent except for `-list` and `-flag` where URLs can be mentioned after it*
|
|
93
|
+
|
|
94
|
+
### Examples
|
|
95
|
+
|
|
96
|
+
#### Basic Downloading
|
|
97
|
+
|
|
98
|
+
Download a video at the default quality (1080p)
|
|
99
|
+
```bash
|
|
100
|
+
ytfetch "VIDEO_URL"
|
|
101
|
+
```
|
|
102
|
+
Download a video in 720p
|
|
103
|
+
```bash
|
|
104
|
+
ytfetch "VIDEO_URL" -q 720
|
|
105
|
+
```
|
|
106
|
+
#### Format Conversion
|
|
107
|
+
|
|
108
|
+
Download as AVC(H.264) and Mp3 in MP4 Container
|
|
109
|
+
```bash
|
|
110
|
+
ytfetch "VIDEO_URL" -avcmp3
|
|
111
|
+
```
|
|
112
|
+
Extract Audio Only in Mp3
|
|
113
|
+
```bash
|
|
114
|
+
ytfetch "VIDEO_URL" -mp3
|
|
115
|
+
```
|
|
116
|
+
#### Smart Fetching
|
|
117
|
+
|
|
118
|
+
Global Limit
|
|
119
|
+
```bash
|
|
120
|
+
ytfetch "PLAYLIST_URL_1" "PLAYLIST_URL_2" -fetch "1-10"
|
|
121
|
+
```
|
|
122
|
+
Mapped Ranges
|
|
123
|
+
```bash
|
|
124
|
+
ytfetch "PLAYLIST_A" "PLAYLIST_B" -fetch "1-5" "10-15"
|
|
125
|
+
```
|
|
126
|
+
*Note: The number of fetch arguments must match the number of URLs*
|
|
127
|
+
|
|
128
|
+
#### Listing and Searching
|
|
129
|
+
|
|
130
|
+
List a Playlist
|
|
131
|
+
```bash
|
|
132
|
+
ytfetch "PLAYLIST_URL" -list
|
|
133
|
+
```
|
|
134
|
+
Search through a playlist
|
|
135
|
+
```bash
|
|
136
|
+
ytfetch "PLAYLIST_URL" -list "remix"
|
|
137
|
+
```
|
|
138
|
+
Combined Fetch & Search
|
|
139
|
+
```bash
|
|
140
|
+
ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Passthrough Options
|
|
144
|
+
|
|
145
|
+
You can append any standard yt-dlp option to your command. Both long and short flags are supported.
|
|
146
|
+
|
|
147
|
+
Download via Proxy
|
|
148
|
+
```bash
|
|
149
|
+
ytfetch --proxy "socks5://127.0.0.1:1080" "VIDEO_URL"
|
|
150
|
+
```
|
|
151
|
+
Use Cookies
|
|
152
|
+
```bash
|
|
153
|
+
ytfetch --cookies "cookies.txt" "VIDEO_URL"
|
|
154
|
+
```
|
|
155
|
+
Limit Speed
|
|
156
|
+
```bash
|
|
157
|
+
ytfetch -r "500K" "VIDEO_URL"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 🔖 License
|
|
161
|
+
|
|
162
|
+
MIT License
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ytfetch"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A wrapper for yt-dlp with custom simple fetching flags"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "voidsnax" }
|
|
11
|
+
]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"yt-dlp>=2025.12.08",
|
|
17
|
+
"colorama"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
keywords = ["youtubedownloader", "converter", "h264", "mp3", "playlist"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/voidsnax/reVidx"
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
ytfetch = "ytfetch.cli:main"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/ytfetch"]
|
|
File without changes
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
import yt_dlp # type: ignore
|
|
4
|
+
from colorama import Fore, Style # type: ignore
|
|
5
|
+
import subprocess
|
|
6
|
+
from .utils import (
|
|
7
|
+
validate_fetch_ranges,
|
|
8
|
+
ErrorOnlyLogger,
|
|
9
|
+
AlignedHelpFormatter,
|
|
10
|
+
print_error,
|
|
11
|
+
parse_passthrough_args
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# --- Custom Logger ---
|
|
15
|
+
class YTFetchLogger:
|
|
16
|
+
def debug(self, msg):
|
|
17
|
+
message = [
|
|
18
|
+
"Downloading playlist:", "Downloading item", "Resuming",
|
|
19
|
+
"Destination:", "already been downloaded", "Finished downloading"
|
|
20
|
+
]
|
|
21
|
+
clean_msg = msg.replace("[download] ", "")
|
|
22
|
+
|
|
23
|
+
if any(phrase in msg for phrase in message):
|
|
24
|
+
print(clean_msg)
|
|
25
|
+
|
|
26
|
+
if "Merging formats" in msg:
|
|
27
|
+
print(msg.replace("[Merger] ", ""))
|
|
28
|
+
|
|
29
|
+
def info(self, msg):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def warning(self, msg):
|
|
33
|
+
if "Some web client https formats have been skipped" in msg:
|
|
34
|
+
return
|
|
35
|
+
print(f"{msg}")
|
|
36
|
+
|
|
37
|
+
def error(self, msg):
|
|
38
|
+
print(f"{msg}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# --- Custom Progress Hook ---
|
|
42
|
+
def progress_hook(d):
|
|
43
|
+
if d['status'] == 'downloading':
|
|
44
|
+
percent = d.get('_percent_str', '0.0%')
|
|
45
|
+
total = d.get('_total_bytes_str', d.get('_total_bytes_estimate_str', 'N/A'))
|
|
46
|
+
speed = d.get('_speed_str', 'N/A')
|
|
47
|
+
status = f"{percent} of {total} at {speed}"
|
|
48
|
+
print(f"\r{status}", end="")
|
|
49
|
+
sys.stdout.flush()
|
|
50
|
+
elif d['status'] == 'finished':
|
|
51
|
+
print()
|
|
52
|
+
|
|
53
|
+
# --- Argument Parsing ---
|
|
54
|
+
def parse_arguments():
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
usage = "ytfetch [yt-dlp OPTIONS] URL ... [OPTIONS]",
|
|
57
|
+
description="ytfetch: yt-dlp wrapper with flexible args.",
|
|
58
|
+
epilog = "type ytftech -help for yt-dlp options \n"
|
|
59
|
+
"Doc & issues: https://github.com/voidsnax/ytFetch",
|
|
60
|
+
formatter_class=AlignedHelpFormatter,
|
|
61
|
+
add_help=False,
|
|
62
|
+
allow_abbrev=False
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument('-h',action='help',
|
|
65
|
+
help='Show this help message and exit.')
|
|
66
|
+
parser.add_argument('-help',action="store_const",const="default",
|
|
67
|
+
help='Show yt-dlp help message.')
|
|
68
|
+
|
|
69
|
+
parser.add_argument("-avcmp3", action="store_true",
|
|
70
|
+
help="Download video in AVC (h.264) format (mp4) + extract audio")
|
|
71
|
+
parser.add_argument("-q", metavar="QUALITY",default="1080",
|
|
72
|
+
help="Video quality (e.g., 1080, 720). Default is 1080.")
|
|
73
|
+
parser.add_argument("-mp3", action="store_true",
|
|
74
|
+
help="Extract audio only as MP3")
|
|
75
|
+
parser.add_argument("-audio", action="store_true",
|
|
76
|
+
help="Extract audio only (bestaudio)")
|
|
77
|
+
parser.add_argument("-fetch", metavar="RANGE",nargs="+",
|
|
78
|
+
help="Alternate for --playlist-items. Single value applies globally\n"
|
|
79
|
+
"Multiple values must match number of playlist URLs provided")
|
|
80
|
+
parser.add_argument("-list", metavar="NAME",nargs="?",const="default",
|
|
81
|
+
help="List playlist contents. Accepts values for searching across playlist")
|
|
82
|
+
|
|
83
|
+
args, unknown_args = parser.parse_known_args()
|
|
84
|
+
return args, unknown_args
|
|
85
|
+
|
|
86
|
+
def get_urls_from_args(args_list):
|
|
87
|
+
return [a for a in args_list if a.startswith("http")]
|
|
88
|
+
|
|
89
|
+
def get_format_selector(args):
|
|
90
|
+
height = args.q.rstrip("p")
|
|
91
|
+
|
|
92
|
+
if args.mp3 or args.audio:
|
|
93
|
+
return "bestaudio"
|
|
94
|
+
|
|
95
|
+
if args.avcmp3:
|
|
96
|
+
return f"bestvideo[vcodec^=avc1][height<={height}]+bestaudio/best[vcodec^=avc1][height<={height}]"
|
|
97
|
+
return f"bestvideo[height<={height}]+bestaudio/best[height<={height}]"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def process_urls(custom_args, raw_ytdlp_args):
|
|
101
|
+
urls = get_urls_from_args(raw_ytdlp_args)
|
|
102
|
+
if custom_args.help:
|
|
103
|
+
yt_dlp.options.create_parser().print_help() # type: ignore
|
|
104
|
+
sys.exit()
|
|
105
|
+
if custom_args.list and custom_args.list.startswith('http'):
|
|
106
|
+
print_error(f"Provided URL link as argument for -list\nUse -list after URL")
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
if custom_args.fetch and any(item.startswith("http") for item in custom_args.fetch):
|
|
109
|
+
print_error(f"Provided a URL link after -fetch\nUse -fetch after URL")
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
if not urls:
|
|
112
|
+
print_error(f"Error: No URLs provided.")
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
passthrough_opts = parse_passthrough_args(raw_ytdlp_args)
|
|
116
|
+
|
|
117
|
+
ydl_opts = {
|
|
118
|
+
'outtmpl': '%(title)s.%(ext)s',
|
|
119
|
+
'logger': YTFetchLogger(),
|
|
120
|
+
'progress_hooks': [progress_hook],
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
ydl_opts.update(passthrough_opts)
|
|
124
|
+
|
|
125
|
+
# --- Mode: List ---
|
|
126
|
+
if custom_args.list:
|
|
127
|
+
base_cmd = [
|
|
128
|
+
"yt-dlp",
|
|
129
|
+
"--flat-playlist",
|
|
130
|
+
"--print", "%(playlist_index)s - %(title)s"
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
passthrough_args = [arg for arg in raw_ytdlp_args if arg not in urls]
|
|
134
|
+
base_cmd.extend(passthrough_args)
|
|
135
|
+
command = []
|
|
136
|
+
|
|
137
|
+
ydl_opts['extract_flat'] = 'in_playlist'
|
|
138
|
+
ydl_opts['logger'] = ErrorOnlyLogger()
|
|
139
|
+
|
|
140
|
+
search_mode = False
|
|
141
|
+
|
|
142
|
+
fetch_ranges = custom_args.fetch
|
|
143
|
+
if fetch_ranges:
|
|
144
|
+
if len(fetch_ranges) >1:
|
|
145
|
+
validate_fetch_ranges(fetch_ranges,urls)
|
|
146
|
+
for fetch_range in fetch_ranges:
|
|
147
|
+
command.append(base_cmd + [ "--playlist-items", fetch_range])
|
|
148
|
+
else:
|
|
149
|
+
command.append(base_cmd + ["--playlist-items", fetch_ranges[0]])
|
|
150
|
+
else:
|
|
151
|
+
command.append(base_cmd)
|
|
152
|
+
|
|
153
|
+
# make sure command is a list of list
|
|
154
|
+
if len(command) == 1 and len(urls) > 1:
|
|
155
|
+
command = [cmd.copy() for cmd in [command[0]] * len(urls)]
|
|
156
|
+
|
|
157
|
+
# print(command)
|
|
158
|
+
|
|
159
|
+
if custom_args.list != 'default':
|
|
160
|
+
search_mode = True
|
|
161
|
+
list_val = custom_args.list
|
|
162
|
+
search_pattern = list_val.lower()
|
|
163
|
+
for url, cmd in zip(urls, command):
|
|
164
|
+
# currently for title fetching this is more reliable
|
|
165
|
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl: #type: ignore
|
|
166
|
+
try:
|
|
167
|
+
info = ydl.extract_info(url, download=False) # type: ignore
|
|
168
|
+
if 'entries' not in info:
|
|
169
|
+
print(f"Single Video: {info.get('title')}")
|
|
170
|
+
else:
|
|
171
|
+
title = f"▶ Playlist: {info.get('title')}"
|
|
172
|
+
print(f"▶ Playlist: {Fore.CYAN}{info.get('title')}{Style.RESET_ALL}")
|
|
173
|
+
print(f"{"-" * len(title)}")
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print(f"Error getting {url} title: {e}")
|
|
176
|
+
try:
|
|
177
|
+
cmd.append(url)
|
|
178
|
+
# print(cmd)
|
|
179
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
180
|
+
found_match = False
|
|
181
|
+
for line in result.stdout.splitlines():
|
|
182
|
+
idx, title = line.split(" - ", 1)
|
|
183
|
+
video = f"{Fore.YELLOW}{idx}{Style.RESET_ALL} - {title}"
|
|
184
|
+
if search_mode:
|
|
185
|
+
if search_pattern in line.lower():
|
|
186
|
+
found_match = True
|
|
187
|
+
print(video)
|
|
188
|
+
else:
|
|
189
|
+
print(video)
|
|
190
|
+
if not found_match and search_mode:
|
|
191
|
+
print(f"No match found for {Fore.YELLOW}{search_pattern}{Style.RESET_ALL}")
|
|
192
|
+
except Exception as e:
|
|
193
|
+
print_error(f"Error listing {url}: {e}")
|
|
194
|
+
|
|
195
|
+
# stops further execution
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
ydl_opts['format'] = get_format_selector(custom_args)
|
|
199
|
+
|
|
200
|
+
if custom_args.mp3:
|
|
201
|
+
ydl_opts['postprocessors'] = [{
|
|
202
|
+
'key': 'FFmpegExtractAudio',
|
|
203
|
+
'preferredcodec': 'mp3',
|
|
204
|
+
'preferredquality': '192',
|
|
205
|
+
}]
|
|
206
|
+
|
|
207
|
+
if custom_args.avcmp3 and not custom_args.mp3:
|
|
208
|
+
ydl_opts['merge_output_format'] = 'mp4'
|
|
209
|
+
ydl_opts['postprocessor_args'] = {
|
|
210
|
+
'ffmpeg': ['-c:v', 'copy', '-c:a', 'libmp3lame', '-b:a', '192k']
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# --- Mode: Download ---
|
|
214
|
+
def run_download(url, playlist_items=None):
|
|
215
|
+
opts = ydl_opts.copy()
|
|
216
|
+
if playlist_items:
|
|
217
|
+
opts['playlist_items'] = playlist_items
|
|
218
|
+
with yt_dlp.YoutubeDL(opts) as ydl: #type:ignore
|
|
219
|
+
try:
|
|
220
|
+
ydl.download([url])
|
|
221
|
+
except Exception as e:
|
|
222
|
+
# print(f"Download error for {url}: {e}")
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
fetch_ranges = custom_args.fetch
|
|
226
|
+
if fetch_ranges:
|
|
227
|
+
if len(fetch_ranges) == 1:
|
|
228
|
+
for url in urls:
|
|
229
|
+
run_download(url, fetch_ranges[0])
|
|
230
|
+
else:
|
|
231
|
+
validate_fetch_ranges(fetch_ranges,urls)
|
|
232
|
+
for url, rng in zip(urls, fetch_ranges):
|
|
233
|
+
run_download(url, rng)
|
|
234
|
+
else:
|
|
235
|
+
for url in urls:
|
|
236
|
+
run_download(url)
|
|
237
|
+
|
|
238
|
+
def main():
|
|
239
|
+
try:
|
|
240
|
+
custom_args, ytdlp_args = parse_arguments()
|
|
241
|
+
process_urls(custom_args, ytdlp_args)
|
|
242
|
+
except KeyboardInterrupt:
|
|
243
|
+
print(f"\n\n{Fore.YELLOW}Process aborted by user.{Style.RESET_ALL}")
|
|
244
|
+
sys.exit(1)
|
|
245
|
+
|
|
246
|
+
if __name__ == "__main__":
|
|
247
|
+
main()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
import yt_dlp
|
|
4
|
+
from colorama import Fore, init # type: ignore
|
|
5
|
+
|
|
6
|
+
init(autoreset=True)
|
|
7
|
+
|
|
8
|
+
def validate_fetch_ranges(fetch_ranges, urls):
|
|
9
|
+
if len(fetch_ranges) != len(urls):
|
|
10
|
+
print_error(f"Error: Provided {len(fetch_ranges)} fetch ranges for {len(urls)} URLs.")
|
|
11
|
+
sys.exit(1)
|
|
12
|
+
|
|
13
|
+
class ErrorOnlyLogger:
|
|
14
|
+
def debug(self, msg): pass
|
|
15
|
+
def info(self, msg): pass
|
|
16
|
+
def warning(self, msg): pass
|
|
17
|
+
def error(self, msg): print(msg)
|
|
18
|
+
|
|
19
|
+
class AlignedHelpFormatter(argparse.HelpFormatter):
|
|
20
|
+
def __init__(self, *args, **kwargs):
|
|
21
|
+
kwargs['max_help_position'] = 35 # default is 24
|
|
22
|
+
super().__init__(*args, **kwargs)
|
|
23
|
+
def _fill_text(self, text, width, indent):
|
|
24
|
+
# Preserve newlines in Description and Epilog
|
|
25
|
+
# splitlines(keepends=True) ensures not losing of double newlines
|
|
26
|
+
return ''.join(indent + line for line in text.splitlines(keepends=True))
|
|
27
|
+
|
|
28
|
+
def _split_lines(self, text, width):
|
|
29
|
+
# Preserve newlines in help
|
|
30
|
+
return text.splitlines()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def print_error(msg):
|
|
34
|
+
print(f"{Fore.RED}{msg}")
|
|
35
|
+
|
|
36
|
+
def parse_passthrough_args(args_list):
|
|
37
|
+
"""
|
|
38
|
+
Parses unknown arguments using yt-dlp's internal parser.
|
|
39
|
+
Returns a dictionary containing ONLY the options explicitly passed by the user.
|
|
40
|
+
"""
|
|
41
|
+
filtered_args = [a for a in args_list if not a.startswith("http")]
|
|
42
|
+
if not filtered_args:
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
parser = yt_dlp.options.create_parser() # type: ignore
|
|
46
|
+
|
|
47
|
+
parsed, _ = parser.parse_known_args(filtered_args)
|
|
48
|
+
|
|
49
|
+
opts = vars(parsed) # convert to dict
|
|
50
|
+
|
|
51
|
+
# Filter out defaults to return ONLY user-provided args
|
|
52
|
+
# create a baseline parser to see what the defaults are
|
|
53
|
+
base_parser = yt_dlp.options.create_parser() # type: ignore
|
|
54
|
+
base_parsed, _ = base_parser.parse_args([])
|
|
55
|
+
base_opts = vars(base_parsed)
|
|
56
|
+
|
|
57
|
+
# Build dictionary of only changed items
|
|
58
|
+
final_opts = {}
|
|
59
|
+
for key, value in opts.items():
|
|
60
|
+
# Only keep the option if the user changed it from the default
|
|
61
|
+
if base_opts[key] != value:
|
|
62
|
+
final_opts[key] = value
|
|
63
|
+
|
|
64
|
+
return final_opts
|