ytfetch 1.2.0__tar.gz → 2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytfetch
3
- Version: 1.2.0
3
+ Version: 2.0.0
4
4
  Summary: A wrapper for yt-dlp with custom simple fetching flags
5
5
  Project-URL: Homepage, https://github.com/voidsnax/ytFetch
6
6
  Author: voidsnax
@@ -14,11 +14,10 @@ Description-Content-Type: text/markdown
14
14
 
15
15
  # ytfetch
16
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.
17
+ **ytfetch** simplifies media downloading and playlist management by adding playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
18
18
 
19
19
  ## ✨ Features
20
20
 
21
- * **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
22
21
  * **Search:** Use `-list` to display or search across single or multiple playlists contents.
23
22
  * **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
24
23
  * **Video Quality:** Mention preffered video quality directly.
@@ -68,7 +67,6 @@ Download builds from [here](https://ffmpeg.org/download.html)
68
67
  Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
69
68
  Also check [yt-dlp dependency](https://github.com/yt-dlp/yt-dlp#dependencies) for optional dependencies.
70
69
 
71
- *Note: `yt-dlp` should also be available in your system PATH for using the `-list` flag. If the package is installed in a virtual environment (venv) or isolated using tools like `uv`, ensure yt-dlp added to your system env PATH.*
72
70
 
73
71
  ## 📦 Installation
74
72
 
@@ -94,13 +92,11 @@ usage: ytfetch URL ... [OPTIONS] [yt-dlp OPTIONS]
94
92
 
95
93
  options:
96
94
  -h Show this help message and exit
97
- -help Show yt-dlp help message
95
+ --help Show yt-dlp help message and exit
98
96
  -avcmp3 Download video in AVC (h.264) & audio in mp3 format
99
97
  -q QUALITY Video quality (e.g., 1080, 720). Default is 1080
100
98
  -mp3 Extract audio only as MP3
101
99
  -audio Extract audio only
102
- -fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
103
- Multiple value must match number of playlist URLs provided
104
100
  -list [NAME] List playlist contents. Accepts values for searching across playlist
105
101
  ```
106
102
 
@@ -126,17 +122,6 @@ Extract Audio Only in Mp3
126
122
  ```bash
127
123
  ytfetch "VIDEO_URL" -mp3
128
124
  ```
129
- #### Smart Fetching
130
-
131
- Global Limit
132
- ```bash
133
- ytfetch "PLAYLIST_URL_1" "PLAYLIST_URL_2" -fetch "1-10"
134
- ```
135
- Mapped Ranges
136
- ```bash
137
- ytfetch "PLAYLIST_A" "PLAYLIST_B" -fetch "1-5" "10-15"
138
- ```
139
- *Note: The number of fetch arguments must match the number of URLs*
140
125
 
141
126
  #### Listing and Searching
142
127
 
@@ -148,10 +133,6 @@ Search through a playlist
148
133
  ```bash
149
134
  ytfetch "PLAYLIST_URL" -list "remix"
150
135
  ```
151
- Combined Fetch & Search
152
- ```bash
153
- ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
154
- ```
155
136
 
156
137
  #### Passthrough Options
157
138
 
@@ -169,6 +150,11 @@ Limit Speed
169
150
  ```bash
170
151
  ytfetch -r "500K" "VIDEO_URL"
171
152
  ```
153
+ Selected range Search
154
+ ```bash
155
+ ytfetch "PLAYLIST_URL" -I ":50" -list "search_word"
156
+ ```
157
+ *and so on...*
172
158
 
173
159
  ## 🔖 License
174
160
 
@@ -1,10 +1,9 @@
1
1
  # ytfetch
2
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.
3
+ **ytfetch** simplifies media downloading and playlist management by adding playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
4
4
 
5
5
  ## ✨ Features
6
6
 
7
- * **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
8
7
  * **Search:** Use `-list` to display or search across single or multiple playlists contents.
9
8
  * **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
10
9
  * **Video Quality:** Mention preffered video quality directly.
@@ -21,7 +20,6 @@ Download builds from [here](https://ffmpeg.org/download.html)
21
20
  Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
22
21
  Also check [yt-dlp dependency](https://github.com/yt-dlp/yt-dlp#dependencies) for optional dependencies.
23
22
 
24
- *Note: `yt-dlp` should also be available in your system PATH for using the `-list` flag. If the package is installed in a virtual environment (venv) or isolated using tools like `uv`, ensure yt-dlp added to your system env PATH.*
25
23
 
26
24
  ## 📦 Installation
27
25
 
@@ -33,8 +31,8 @@ pip install ytfetch
33
31
 
34
32
  ### via source
35
33
  ```bash
36
- git clone <your-repo-url>
37
- cd <your-repo>
34
+ git clone https://github.com/voidsnax/ytFetch.git
35
+ cd ytFetch
38
36
  pip install -e .
39
37
  ```
40
38
 
@@ -47,14 +45,11 @@ usage: ytfetch URL ... [OPTIONS] [yt-dlp OPTIONS]
47
45
 
48
46
  options:
49
47
  -h Show this help message and exit
50
- -help Show yt-dlp help messag
48
+ --help Show yt-dlp help messag and exit
51
49
  -avcmp3 Download video in AVC (h.264) & audio in mp3 format
52
50
  -q QUALITY Video quality (e.g., 1080, 720). Default is 1080
53
51
  -mp3 Extract audio only as MP3
54
- -audio Extract audio only
55
- -fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
56
- Multiple value must number of playlist URLs provided
57
- match number of playlist URLs provided
52
+ -audio Extract best audio
58
53
  -list [NAME] List playlist contents. Accepts values for searching across playlist
59
54
  ```
60
55
 
@@ -80,17 +75,6 @@ Extract Audio Only in Mp3
80
75
  ```bash
81
76
  ytfetch "VIDEO_URL" -mp3
82
77
  ```
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
78
 
95
79
  #### Listing and Searching
96
80
 
@@ -102,10 +86,6 @@ Search through a playlist
102
86
  ```bash
103
87
  ytfetch "PLAYLIST_URL" -list "remix"
104
88
  ```
105
- Combined Fetch & Search
106
- ```bash
107
- ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
108
- ```
109
89
 
110
90
  #### Passthrough Options
111
91
 
@@ -123,6 +103,11 @@ Limit Speed
123
103
  ```bash
124
104
  ytfetch -r "500K" "VIDEO_URL"
125
105
  ```
106
+ Selected range Search
107
+ ```bash
108
+ ytfetch "PLAYLIST_URL" -I ":50" -list "search_word"
109
+ ```
110
+ *and so on...*
126
111
 
127
112
  ## 🔖 License
128
113
 
@@ -1,10 +1,9 @@
1
1
  # ytfetch
2
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.
3
+ **ytfetch** simplifies media downloading and playlist management by adding playlist searching, and streamlined format handling on top of the powerful [yt-dlp](https://github.com/yt-dlp/yt-dlp) engine.
4
4
 
5
5
  ## ✨ Features
6
6
 
7
- * **Smart Fetching:** Apply limits to multiple playlists or map specific ranges in a single download.
8
7
  * **Search:** Use `-list` to display or search across single or multiple playlists contents.
9
8
  * **Direct Download:** Single flag to download in legacy `avc/h.264` and `mp3` format and audio only extraction.
10
9
  * **Video Quality:** Mention preffered video quality directly.
@@ -54,7 +53,6 @@ Download builds from [here](https://ffmpeg.org/download.html)
54
53
  Check this [guide](https://github.com/yt-dlp/yt-dlp/wiki/EJS) for more info.
55
54
  Also check [yt-dlp dependency](https://github.com/yt-dlp/yt-dlp#dependencies) for optional dependencies.
56
55
 
57
- *Note: `yt-dlp` should also be available in your system PATH for using the `-list` flag. If the package is installed in a virtual environment (venv) or isolated using tools like `uv`, ensure yt-dlp added to your system env PATH.*
58
56
 
59
57
  ## 📦 Installation
60
58
 
@@ -80,13 +78,11 @@ usage: ytfetch URL ... [OPTIONS] [yt-dlp OPTIONS]
80
78
 
81
79
  options:
82
80
  -h Show this help message and exit
83
- -help Show yt-dlp help message
81
+ --help Show yt-dlp help message and exit
84
82
  -avcmp3 Download video in AVC (h.264) & audio in mp3 format
85
83
  -q QUALITY Video quality (e.g., 1080, 720). Default is 1080
86
84
  -mp3 Extract audio only as MP3
87
85
  -audio Extract audio only
88
- -fetch RANGE [RANGE ...] Alternate for --playlist-items. Single value applies globally
89
- Multiple value must match number of playlist URLs provided
90
86
  -list [NAME] List playlist contents. Accepts values for searching across playlist
91
87
  ```
92
88
 
@@ -112,17 +108,6 @@ Extract Audio Only in Mp3
112
108
  ```bash
113
109
  ytfetch "VIDEO_URL" -mp3
114
110
  ```
115
- #### Smart Fetching
116
-
117
- Global Limit
118
- ```bash
119
- ytfetch "PLAYLIST_URL_1" "PLAYLIST_URL_2" -fetch "1-10"
120
- ```
121
- Mapped Ranges
122
- ```bash
123
- ytfetch "PLAYLIST_A" "PLAYLIST_B" -fetch "1-5" "10-15"
124
- ```
125
- *Note: The number of fetch arguments must match the number of URLs*
126
111
 
127
112
  #### Listing and Searching
128
113
 
@@ -134,10 +119,6 @@ Search through a playlist
134
119
  ```bash
135
120
  ytfetch "PLAYLIST_URL" -list "remix"
136
121
  ```
137
- Combined Fetch & Search
138
- ```bash
139
- ytfetch "PLAYLIST_URL" -fetch ":50" -list "search_word"
140
- ```
141
122
 
142
123
  #### Passthrough Options
143
124
 
@@ -155,6 +136,11 @@ Limit Speed
155
136
  ```bash
156
137
  ytfetch -r "500K" "VIDEO_URL"
157
138
  ```
139
+ Selected range Search
140
+ ```bash
141
+ ytfetch "PLAYLIST_URL" -I ":50" -list "search_word"
142
+ ```
143
+ *and so on...*
158
144
 
159
145
  ## 🔖 License
160
146
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ytfetch"
7
- version = "1.2.0"
7
+ version = "2.0.0"
8
8
  description = "A wrapper for yt-dlp with custom simple fetching flags"
9
9
  authors = [
10
10
  { name = "voidsnax" }
@@ -1,30 +1,29 @@
1
1
  import argparse
2
2
 
3
-
4
3
  def parse_arguments():
5
4
  parser = argparse.ArgumentParser(
6
- usage = "ytfetch URL ... [OPTIONS] [yt-dlp OPTIONS] ",
7
- description="ytfetch: yt-dlp wrapper with flexible args.",
8
- epilog = "type yt-dlp -h for yt-dlp options \n"
9
- "Doc & issues: https://github.com/voidsnax/ytFetch",
5
+ usage = "ytfetch URL ... [OPTIONS] [yt-dlp OPTIONS]",
6
+ description="ytfetch: yt-dlp wrapper with some flexible args.",
7
+ epilog = "Doc & issues: https://github.com/voidsnax/ytFetch",
10
8
  formatter_class=AlignedHelpFormatter,
11
- allow_abbrev=False
9
+ allow_abbrev=False,
10
+ add_help=False
12
11
  )
13
12
 
14
- parser.add_argument("url", nargs="+", metavar="URL")
15
- parser.add_argument("-avcmp3", action="store_true",
16
- help="Download video in AVC (h.264) & audio in mp3 format")
13
+ parser.add_argument("-h", action="help", help="Show this help message and exit")
14
+ parser.add_argument("--help", action="store_true", help="Show yt-dlp help message and exit")
17
15
  parser.add_argument("-q", metavar="QUALITY", nargs="?", default="default",
18
- help="Video quality (e.g., 1080, 720). Default will pick best video")
19
- parser.add_argument("-mp3", action="store_true",
20
- help="Extract audio only as MP3")
21
- parser.add_argument("-audio", action="store_true",
22
- help="Extract audio only (bestaudio)")
23
- parser.add_argument("-fetch", metavar="RANGE", nargs="+",
24
- help="Alternate for --playlist-items. Single value applies globally\n"
25
- "Multiple values must match number of playlist URLs provided")
16
+ help="Video quality (e.g., 1080, 720). Default will pick best video"
17
+ )
18
+ parser.add_argument("-avcmp3", action="store_true",
19
+ help="Download video in AVC (h.264) & audio in mp3 format"
20
+ )
21
+ parser.add_argument("-mp3", action="store_true", help="Extract audio only as MP3")
22
+ parser.add_argument("-audio", action="store_true", help="Extract audio only (bestaudio)")
23
+
26
24
  parser.add_argument("-list", metavar="NAME", nargs="?", const="default",
27
- help="List playlist contents. Accepts values for searching across playlist")
25
+ help="List playlist contents. Accepts values for searching across playlist"
26
+ )
28
27
 
29
28
  args, unknown_args = parser.parse_known_args()
30
29
  return args, unknown_args
@@ -4,7 +4,6 @@ from .utils import is_android
4
4
 
5
5
  init(autoreset=True)
6
6
 
7
-
8
7
  # --- Custom Loggers ---
9
8
  class YTFetchLogger:
10
9
  def debug(self, msg):
@@ -24,7 +23,13 @@ class YTFetchLogger:
24
23
  print(f"\n{clean_msg}")
25
24
 
26
25
  if "Merging formats" in msg:
27
- print(msg.replace("[Merger] ", ""))
26
+ print(msg)
27
+
28
+ if "Adding metadata" in msg:
29
+ print(msg)
30
+
31
+ if "Adding thumbnail" in msg:
32
+ print(msg)
28
33
 
29
34
  def info(self, msg):
30
35
  pass
@@ -37,8 +42,13 @@ class YTFetchLogger:
37
42
  def error(self, msg):
38
43
  print(f"\n{msg}")
39
44
 
40
- class ErrorOnlyLogger:
41
- def debug(self, msg): pass
45
+ class ListLogger:
46
+ def debug(self, msg):
47
+ if "Downloading playlist:" in msg:
48
+ playlist_title = msg.replace("[download] Downloading playlist: ", "")
49
+ playlist_string = f"▶ Playlist: {playlist_title}"
50
+ print(f"▶ Playlist: {Fore.CYAN}{playlist_title}{Fore.RESET}")
51
+ print("-" * len(playlist_string))
42
52
  def info(self, msg): pass
43
53
  def warning(self, msg): pass
44
54
  def error(self, msg): print(msg)
@@ -0,0 +1,97 @@
1
+ import sys
2
+ import signal
3
+
4
+ signal.signal(signal.SIGINT, lambda x, y: sys.exit(0))
5
+
6
+ import yt_dlp
7
+ from colorama import Fore
8
+ from urllib.parse import urlsplit
9
+
10
+ from .cli import parse_arguments
11
+ from .utils import (
12
+ format_selector,
13
+ print_error
14
+ )
15
+ from .formatter import (
16
+ ListLogger,
17
+ YTFetchLogger,
18
+ progress_hook
19
+ )
20
+
21
+ signal.signal(signal.SIGINT, signal.default_int_handler)
22
+
23
+ def process_urls(cli_args, extra_ydl_args):
24
+
25
+ if cli_args.help:
26
+ yt_dlp.parse_options(["-h"])
27
+
28
+ ydl_args = []
29
+
30
+ # --- List ---
31
+ if cli_args.list:
32
+ ydl_args.extend(["--flat-playlist", "--print", "%(playlist_index)s - %(title)s"])
33
+
34
+ if cli_args.list != 'default':
35
+ list_val = cli_args.list
36
+ parsed = urlsplit(list_val) # to skip urls
37
+ if all([parsed.scheme, parsed.netloc]):
38
+ extra_ydl_args.append(list_val)
39
+ else:
40
+ ydl_args.extend(["--match-filters", f"title ~= '(?i){list_val}'"])
41
+
42
+ else:
43
+ # --- format selection ---
44
+ ydl_args.extend(["--format", format_selector(cli_args)])
45
+
46
+ # --- audio quality ---
47
+ if cli_args.q == 'default':
48
+ a_quality = '2'
49
+ else:
50
+ a_quality = cli_args.q
51
+
52
+ # --- mp3 ---
53
+ if cli_args.mp3:
54
+ ydl_args.extend(["-x", "--audio-format", "mp3", "--audio-quality", a_quality])
55
+
56
+ # --- audio ---
57
+ elif cli_args.audio:
58
+ ydl_args.extend(["-x", "--audio-quality", a_quality])
59
+
60
+ # --- avcmp3 ---
61
+ elif cli_args.avcmp3:
62
+ ydl_args.extend(["--merge-output-format", "mp4", "--postprocessor-args", "ffmpeg:-c:v copy -c:a libmp3lame -b:a 192k"])
63
+
64
+ # --- default output template ---
65
+ ydl_args.extend(["-o", "%(title)s.%(ext)s"])
66
+
67
+ # --- final parsing ---
68
+ ydl_args.extend(extra_ydl_args)
69
+ _, _, urls, ydl_opts = yt_dlp.parse_options(ydl_args)
70
+
71
+ if not urls:
72
+ print_error("Provide atleast one URL...")
73
+ return
74
+
75
+ # --- output template aliases ---
76
+ if ydl_opts.get('outtmpl', {}).get('default') in ['autonumber', 'num']: # type: ignore
77
+ ydl_opts["outtmpl"] = {'default': '%(playlist_autonumber)s - %(title)s.%(ext)s'}
78
+
79
+ # --- logger ---
80
+ if not cli_args.list: ydl_opts['progress_hooks'] = [progress_hook]
81
+ if not ydl_opts.get('verbose'):
82
+ ydl_opts['logger'] = ListLogger() if cli_args.list else YTFetchLogger() # type: ignore
83
+
84
+ # --- download ---
85
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
86
+ for url in urls:
87
+ ydl.download([url])
88
+
89
+ def main():
90
+ try:
91
+ args, extra_args = parse_arguments()
92
+ process_urls(args, extra_args)
93
+ except KeyboardInterrupt:
94
+ print(f"\n\n{Fore.YELLOW}Process aborted by user{Fore.RESET}")
95
+ sys.exit(1)
96
+ except Exception as e:
97
+ print_error(f"Unexpected error: {e}")
@@ -0,0 +1,27 @@
1
+ import sys
2
+ from colorama import Fore, init
3
+
4
+ init(autoreset=True)
5
+
6
+ def format_selector(args):
7
+ height = args.q.rstrip("p")
8
+
9
+ if args.mp3 or args.audio:
10
+ return "bestaudio/best"
11
+
12
+ if args.avcmp3:
13
+ if height != "default":
14
+ return f"bestvideo[vcodec^=avc1][height<={height}]+bestaudio/best[vcodec^=avc1][height<={height}]"
15
+
16
+ return f"bestvideo[vcodec^=avc1]+bestaudio/best[vcodec^=avc1]"
17
+
18
+ if height != "default":
19
+ return f"bestvideo[height<={height}]+bestaudio/best[height<={height}]"
20
+
21
+ return f"bestvideo+bestaudio/best"
22
+
23
+ def print_error(msg):
24
+ print(f"{Fore.RED}{msg}")
25
+
26
+ def is_android():
27
+ return hasattr(sys, 'getandroidapilevel')
@@ -1,160 +0,0 @@
1
- import sys
2
- import signal
3
-
4
- signal.signal(signal.SIGINT, lambda x, y: sys.exit(0))
5
-
6
- import subprocess
7
- import yt_dlp
8
- from colorama import Fore
9
-
10
- from .cli import parse_arguments
11
- from .utils import (
12
- parse_passthrough_args,
13
- validate_fetch_ranges,
14
- format_selector,
15
- print_error,
16
- is_ytdlp_available
17
- )
18
- from .formatter import (
19
- YTFetchLogger,
20
- ErrorOnlyLogger,
21
- progress_hook
22
- )
23
-
24
- signal.signal(signal.SIGINT, signal.default_int_handler)
25
-
26
-
27
- def process_urls(custom_args, raw_ytdlp_args):
28
- urls = custom_args.url
29
- passthrough_opts = parse_passthrough_args(raw_ytdlp_args)
30
-
31
- ydl_opts = {
32
- 'outtmpl': '%(title)s.%(ext)s',
33
- 'logger': YTFetchLogger(),
34
- 'progress_hooks': [progress_hook],
35
- }
36
-
37
- ydl_opts.update(passthrough_opts)
38
-
39
- # --- List ---
40
- if not is_ytdlp_available():
41
- print_error("yt-dlp cant be accesed via terminal, Make sure its available in system PATH")
42
- return
43
- if custom_args.list:
44
- base_cmd = [
45
- "yt-dlp",
46
- "--flat-playlist",
47
- "--print", "%(playlist_index)s - %(title)s"
48
- ]
49
-
50
- passthrough_args = [arg for arg in raw_ytdlp_args if arg not in urls]
51
- base_cmd.extend(passthrough_args)
52
- command = []
53
-
54
- ydl_opts['extract_flat'] = 'in_playlist'
55
- ydl_opts['logger'] = ErrorOnlyLogger()
56
-
57
- search_mode = False
58
-
59
- fetch_ranges = custom_args.fetch
60
- if fetch_ranges:
61
- if len(fetch_ranges) >1:
62
- validate_fetch_ranges(fetch_ranges,urls)
63
- for fetch_range in fetch_ranges:
64
- command.append(base_cmd + [ "--playlist-items", fetch_range])
65
- else:
66
- command.append(base_cmd + ["--playlist-items", fetch_ranges[0]])
67
- else:
68
- command.append(base_cmd)
69
-
70
- # make sure command is a list of list
71
- if len(command) == 1 and len(urls) > 1:
72
- command = [cmd.copy() for cmd in [command[0]] * len(urls)]
73
-
74
-
75
- if custom_args.list != 'default':
76
- search_mode = True
77
- list_val = custom_args.list
78
- search_pattern = list_val.lower()
79
- for url, cmd in zip(urls, command):
80
- # currently for title fetching this is more reliable
81
- with yt_dlp.YoutubeDL(ydl_opts) as ydl: #type: ignore
82
- try:
83
- info = ydl.extract_info(url, download=False)
84
- if 'entries' not in info:
85
- print(f"Single Video: {info.get('title')}")
86
- else:
87
- title = f"▶ Playlist: {info.get('title')}"
88
- print(f"▶ Playlist: {Fore.CYAN}{info.get('title')}{Fore.RESET}")
89
- print(f"{"-" * len(title)}")
90
- except Exception as e:
91
- print(f"Error getting {url} title: {e}")
92
- try:
93
- cmd.append(url)
94
- # print(cmd)
95
- result = subprocess.run(cmd, capture_output=True, text=True)
96
- found_match = False
97
- for line in result.stdout.splitlines():
98
- idx, title = line.split(" - ", 1)
99
- video = f"{Fore.YELLOW}{idx}{Fore.RESET} - {title}"
100
- if search_mode:
101
- if search_pattern in line.lower():
102
- found_match = True
103
- print(video)
104
- else:
105
- print(video)
106
- if not found_match and search_mode:
107
- print(f"No match found for {Fore.YELLOW}{search_pattern}{Fore.RESET}")
108
- except Exception as e:
109
- print_error(f"Error listing {url}: {e}")
110
-
111
- return
112
-
113
- # --- format selection ---
114
- ydl_opts['format'] = format_selector(custom_args)
115
-
116
- if custom_args.mp3:
117
- ydl_opts['postprocessors'] = [{
118
- 'key': 'FFmpegExtractAudio',
119
- 'preferredcodec': 'mp3',
120
- 'preferredquality': '192',
121
- }]
122
-
123
- elif custom_args.avcmp3:
124
- ydl_opts['merge_output_format'] = 'mp4'
125
- ydl_opts['postprocessor_args'] = {
126
- 'ffmpeg': ['-c:v', 'copy', '-c:a', 'libmp3lame', '-b:a', '192k']
127
- }
128
-
129
- # --- Download ---
130
- def run_download(url, playlist_items=None):
131
- opts = ydl_opts.copy()
132
- if playlist_items:
133
- opts['playlist_items'] = playlist_items
134
- with yt_dlp.YoutubeDL(opts) as ydl: #type:ignore
135
- try:
136
- ydl.download([url])
137
- except Exception as e:
138
- # print(f"Download error for {url}: {e}")
139
- pass
140
-
141
- fetch_ranges = custom_args.fetch
142
- if fetch_ranges:
143
- if len(fetch_ranges) == 1:
144
- for url in urls:
145
- run_download(url, fetch_ranges[0])
146
- else:
147
- validate_fetch_ranges(fetch_ranges,urls)
148
- for url, rng in zip(urls, fetch_ranges):
149
- run_download(url, rng)
150
- else:
151
- for url in urls:
152
- run_download(url)
153
-
154
- def main():
155
- try:
156
- custom_args, ytdlp_args = parse_arguments()
157
- process_urls(custom_args, ytdlp_args)
158
- except KeyboardInterrupt:
159
- print(f"\n\n{Fore.YELLOW}Process aborted by user{Fore.RESET}")
160
- sys.exit(1)
@@ -1,70 +0,0 @@
1
- import shutil
2
- import sys
3
- from colorama import Fore, init
4
- from yt_dlp.options import create_parser
5
-
6
- init(autoreset=True)
7
-
8
- def parse_passthrough_args(args_list):
9
- """
10
- Parses unknown arguments using yt-dlp's internal parser.
11
- Returns a dictionary containing ONLY the options explicitly passed by the user.
12
- """
13
- parser = create_parser()
14
- try:
15
- parsed, _ = parser.parse_known_args(args_list)
16
- except Exception as e:
17
- print(e,end='')
18
- sys.exit(1)
19
-
20
- opts = vars(parsed) # convert to dict
21
-
22
- # Filter out defaults to return ONLY user-provided args
23
- # create a baseline parser to see what the defaults are
24
- base_parser = create_parser()
25
- base_parsed, _ = base_parser.parse_args([])
26
- base_opts = vars(base_parsed)
27
-
28
- # Build dictionary of only changed items
29
- final_opts = {}
30
- for key, value in opts.items():
31
- # Only keep the option if the user changed it from the default
32
- if base_opts[key] != value:
33
- final_opts[key] = value
34
-
35
- return final_opts
36
-
37
-
38
- def validate_fetch_ranges(fetch_ranges, urls):
39
- if len(fetch_ranges) != len(urls):
40
- print_error(f"Error: Provided {len(fetch_ranges)} fetch ranges for {len(urls)} URLs.")
41
- sys.exit(1)
42
-
43
-
44
- def format_selector(args):
45
- height = args.q.rstrip("p")
46
-
47
- if args.mp3 or args.audio:
48
- return "bestaudio/best"
49
-
50
- if args.avcmp3:
51
- if height != "default":
52
- return f"bestvideo[vcodec^=avc1][height<={height}]+bestaudio/best[vcodec^=avc1][height<={height}]"
53
-
54
- return f"bestvideo[vcodec^=avc1]+bestaudio/best[vcodec^=avc1]"
55
-
56
- if height != "default":
57
- return f"bestvideo[height<={height}]+bestaudio/best[height<={height}]"
58
-
59
- return f"bestvideo+bestaudio/best"
60
-
61
- def print_error(msg):
62
- print(f"{Fore.RED}{msg}")
63
-
64
- def is_android():
65
- return hasattr(sys, 'getandroidapilevel')
66
-
67
- def is_ytdlp_available():
68
- """Return True if yt-dlp is available in the system PATH."""
69
- return shutil.which("yt-dlp") is not None
70
-
File without changes
File without changes
File without changes
File without changes