media-downloader 0.11.14__py2.py3-none-any.whl → 0.11.15__py2.py3-none-any.whl

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.

Potentially problematic release.


This version of media-downloader might be problematic. Click here for more details.

@@ -51,61 +51,15 @@ class YtDlpLogger:
51
51
 
52
52
 
53
53
  class MediaDownloader:
54
-
55
- def __init__(
56
- self, links: list = [], download_directory: str = None, audio: bool = False
57
- ):
58
- self.links = links
59
- if download_directory:
60
- self.download_directory = download_directory
61
- else:
62
- self.download_directory = f'{os.path.expanduser("~")}/Downloads'
63
- self.audio = audio
64
- self.logger = logging.getLogger("MediaDownloader")
65
-
66
- def open_file(self, file):
67
- youtube_urls = open(file, "r")
68
- for url in youtube_urls:
69
- self.links.append(url)
70
- self.links = list(dict.fromkeys(self.links))
71
-
72
- def get_save_path(self) -> str:
73
- return self.download_directory
74
-
75
- def set_save_path(self, download_directory):
76
- self.download_directory = download_directory.replace(os.sep, "/")
77
- self.logger.debug(f"Set download directory to: {self.download_directory}")
78
-
79
- def reset_links(self):
80
- self.logger.debug("Resetting links")
54
+ def __init__(self):
81
55
  self.links = []
56
+ self.download_directory = f"{os.path.expanduser('~')}/Downloads"
57
+ self.audio = False
58
+ self.logger = logging.getLogger("MediaDownloader")
59
+ self.progress_callback = None # Store callback for progress updates
82
60
 
83
- def extend_links(self, urls):
84
- self.logger.debug(f"Extending links: {urls}")
85
- self.links.extend(urls)
86
- self.links = list(dict.fromkeys(self.links))
87
-
88
- def append_link(self, url):
89
- self.logger.debug(f"Appending link: {url}")
90
- self.links.append(url)
91
- self.links = list(dict.fromkeys(self.links))
92
-
93
- def get_links(self) -> List[str]:
94
- return self.links
95
-
96
- def set_audio(self, audio=False):
97
- self.audio = audio
98
- self.logger.debug(f"Audio mode set to: {audio}")
99
-
100
- def download_all(self):
101
- self.logger.debug(f"Downloading {len(self.links)} links")
102
- pool = Pool(processes=os.cpu_count())
103
- try:
104
- pool.map(self.download_video, self.links)
105
- finally:
106
- pool.close()
107
- pool.join()
108
- self.reset_links()
61
+ def set_progress_callback(self, callback):
62
+ self.progress_callback = callback
109
63
 
110
64
  def download_video(self, link):
111
65
  self.logger.debug(f"Downloading video: {link}")
@@ -125,10 +79,10 @@ class MediaDownloader:
125
79
  ydl_opts = {
126
80
  "format": "bestaudio/best" if self.audio else "best",
127
81
  "outtmpl": outtmpl,
128
- "quiet": True, # Suppress yt_dlp console output
129
- "no_warnings": True, # Suppress warnings
130
- "progress_with_newline": False, # Disable progress output
131
- "logger": YtDlpLogger(self.logger), # Use custom logger
82
+ "quiet": True,
83
+ "no_warnings": True,
84
+ "progress_hooks": [self.progress_hook], # Add progress hook
85
+ "logger": YtDlpLogger(self.logger),
132
86
  }
133
87
  if self.audio:
134
88
  ydl_opts["postprocessors"] = [
@@ -142,7 +96,7 @@ class MediaDownloader:
142
96
  try:
143
97
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
144
98
  info = ydl.extract_info(link, download=True)
145
- return ydl.prepare_filename(info) # Return the actual file path
99
+ return ydl.prepare_filename(info)
146
100
  except Exception as e:
147
101
  self.logger.error(f"Failed to download {link}: {str(e)}")
148
102
  try:
@@ -156,43 +110,18 @@ class MediaDownloader:
156
110
  return None
157
111
 
158
112
  def get_channel_videos(self, channel, limit=-1):
159
- self.logger.debug(f"Fetching videos for channel: {channel}, limit: {limit}")
160
- username = channel
161
- attempts = 0
162
- while attempts < 3:
163
- url = f"https://www.youtube.com/user/{username}/videos"
164
- self.logger.debug(f"Trying URL: {url}")
165
- page = requests.get(url).content
166
- data = str(page).split(" ")
167
- item = 'href="/watch?'
168
- vids = [
169
- line.replace('href="', "youtube.com") for line in data if item in line
170
- ]
171
- if vids:
172
- self.logger.debug(f"Found {len(vids)} videos")
173
- x = 0
174
- for vid in vids:
175
- if limit < 0 or x < limit:
176
- self.append_link(vid)
177
- x += 1
178
- return
179
- else:
180
- url = f"https://www.youtube.com/c/{channel}/videos"
113
+ self.logger.debug(f"Fetching videos for channel: {channel}, limit: {limit}")
114
+ username = channel
115
+ attempts = 0
116
+ while attempts < 3:
117
+ url = f"https://www.youtube.com/user/{username}/videos"
181
118
  self.logger.debug(f"Trying URL: {url}")
182
119
  page = requests.get(url).content
183
120
  data = str(page).split(" ")
184
- item = "https://i.ytimg.com/vi/"
185
- vids = []
186
- for line in data:
187
- if item in line:
188
- try:
189
- found = re.search(
190
- "https://i.ytimg.com/vi/(.+?)/hqdefault.", line
191
- ).group(1)
192
- vid = f"https://www.youtube.com/watch?v={found}"
193
- vids.append(vid)
194
- except AttributeError:
195
- continue
121
+ item = 'href="/watch?'
122
+ vids = [
123
+ line.replace('href="', "youtube.com") for line in data if item in line
124
+ ]
196
125
  if vids:
197
126
  self.logger.debug(f"Found {len(vids)} videos")
198
127
  x = 0
@@ -201,8 +130,59 @@ class MediaDownloader:
201
130
  self.append_link(vid)
202
131
  x += 1
203
132
  return
204
- attempts += 1
205
- self.logger.error(f"Could not find user or channel: {channel}")
133
+ else:
134
+ url = f"https://www.youtube.com/c/{channel}/videos"
135
+ self.logger.debug(f"Trying URL: {url}")
136
+ page = requests.get(url).content
137
+ data = str(page).split(" ")
138
+ item = "https://i.ytimg.com/vi/"
139
+ vids = []
140
+ for line in data:
141
+ if item in line:
142
+ try:
143
+ found = re.search(
144
+ "https://i.ytimg.com/vi/(.+?)/hqdefault.", line
145
+ ).group(1)
146
+ vid = f"https://www.youtube.com/watch?v={found}"
147
+ vids.append(vid)
148
+ except AttributeError:
149
+ continue
150
+ if vids:
151
+ self.logger.debug(f"Found {len(vids)} videos")
152
+ x = 0
153
+ for vid in vids:
154
+ if limit < 0 or x < limit:
155
+ self.append_link(vid)
156
+ x += 1
157
+ return
158
+ attempts += 1
159
+ self.logger.error(f"Could not find user or channel: {channel}")
160
+
161
+ def progress_hook(self, d):
162
+ if self.progress_callback and d["status"] == "downloading":
163
+ if d.get("total_bytes") and d.get("downloaded_bytes"):
164
+ progress = (d["downloaded_bytes"] / d["total_bytes"]) * 100
165
+ self.progress_callback(progress=progress, total=100)
166
+ elif d.get("downloaded_bytes"):
167
+ # Indeterminate progress if total_bytes is unavailable
168
+ self.progress_callback(progress=d["downloaded_bytes"])
169
+ elif d["status"] == "finished":
170
+ if self.progress_callback:
171
+ self.progress_callback(progress=100, total=100)
172
+
173
+ def download_all(self):
174
+ self.logger.debug(f"Downloading {len(self.links)} links")
175
+ pool = Pool(processes=os.cpu_count())
176
+ try:
177
+ results = pool.map(self.download_video, self.links)
178
+ self.reset_links()
179
+ for result in results:
180
+ if result and os.path.exists(result):
181
+ return result
182
+ return None
183
+ finally:
184
+ pool.close()
185
+ pool.join()
206
186
 
207
187
 
208
188
  def media_downloader(argv):
@@ -32,27 +32,40 @@ async def download_media(
32
32
  RuntimeError: If the download fails.
33
33
  """
34
34
  logger = logging.getLogger("MediaDownloader")
35
- logger.debug(
36
- f"Starting download for URL: {video_url}, directory: {download_directory}, audio_only: {audio_only}"
37
- )
35
+ logger.debug(f"Starting download for URL: {video_url}, directory: {download_directory}, audio_only: {audio_only}")
38
36
 
39
37
  try:
40
- # Validate inputs
41
38
  if not video_url or not download_directory:
42
39
  raise ValueError("video_url and download_directory must not be empty")
43
-
44
- # Ensure the download directory exists
45
40
  os.makedirs(download_directory, exist_ok=True)
46
41
 
47
- # Initialize MediaDownloader
48
42
  downloader = MediaDownloader(
49
43
  download_directory=download_directory, audio=audio_only
50
44
  )
45
+
46
+ # Set progress callback for yt_dlp
47
+ async def progress_callback(progress, total=None):
48
+ if ctx:
49
+ await ctx.report_progress(progress=progress, total=total)
50
+ logger.debug(f"Reported progress: {progress}/{total}")
51
+
52
+ downloader.set_progress_callback(progress_callback)
53
+
54
+ # Report initial progress
55
+ if ctx:
56
+ await ctx.report_progress(progress=0, total=100)
57
+ logger.debug("Reported initial progress: 0/100")
58
+
59
+ # Perform the download
51
60
  file_path = downloader.download_video(link=video_url)
52
61
 
53
- # Check if the file was downloaded
54
- # if not file_path:
55
- # raise RuntimeError("Download failed or file not found")
62
+ if not file_path or not os.path.exists(file_path):
63
+ raise RuntimeError("Download failed or file not found")
64
+
65
+ # Report completion
66
+ if ctx:
67
+ await ctx.report_progress(progress=100, total=100)
68
+ logger.debug("Reported final progress: 100/100")
56
69
 
57
70
  logger.debug(f"Download completed, file path: {file_path}")
58
71
  return file_path
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  # coding: utf-8
3
3
 
4
- __version__ = "0.11.14"
4
+ __version__ = "0.11.15"
5
5
  __author__ = "Audel Rouhi"
6
6
  __credits__ = "Audel Rouhi"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: media-downloader
3
- Version: 0.11.14
3
+ Version: 0.11.15
4
4
  Summary: Download audio/videos from the internet!
5
5
  Home-page: https://github.com/Knuckles-Team/media-downloader
6
6
  Author: Audel Rouhi
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.14
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: fastmcp (>=2.11.3)
24
- Requires-Dist: yt-dlp (>=2023.12.30)
24
+ Requires-Dist: yt-dlp (>=2025.8.20)
25
25
 
26
26
  # Media Downloader
27
27
 
@@ -45,7 +45,7 @@ Requires-Dist: yt-dlp (>=2023.12.30)
45
45
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/media-downloader)
46
46
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/media-downloader)
47
47
 
48
- *Version: 0.11.14*
48
+ *Version: 0.11.15*
49
49
 
50
50
  Download videos and audio from the internet!
51
51
 
@@ -0,0 +1,11 @@
1
+ media_downloader/__init__.py,sha256=d0PDULMlfmN1E52NKz20Wprllpq-UYMsBU3-2p4ZEk8,460
2
+ media_downloader/__main__.py,sha256=aHajZBE7fyiC7E5qjV5LMe9nCPkQV2YKu6FU8Ubkdm4,112
3
+ media_downloader/media_downloader.py,sha256=Ihre9gBnaqorHSmOCFXyF_wYhUL_ohS3JFguZW2mAgQ,8894
4
+ media_downloader/media_downloader_mcp.py,sha256=fULxaUQe3-YBveYvOP3eKpaWBa2nrKHaNtr02t4kGU0,3547
5
+ media_downloader/version.py,sha256=D9vc4IaPvZ2ts54UPiI9aLExP3B_yt4LOaTNPUSXy_o,118
6
+ media_downloader-0.11.15.dist-info/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
7
+ media_downloader-0.11.15.dist-info/METADATA,sha256=3--6QBQ1aSppevqUfntO5O6pG5K_gpBtz3maPxN0SIU,5522
8
+ media_downloader-0.11.15.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
9
+ media_downloader-0.11.15.dist-info/entry_points.txt,sha256=Hjp1vLkHPq_bABsuh4kpL5nddi1wn9Ftota-72_GgW4,142
10
+ media_downloader-0.11.15.dist-info/top_level.txt,sha256=B2OBmgONOm0hIyx2HJ8qFPOI_p5HOeolrYvmslVC1fc,17
11
+ media_downloader-0.11.15.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- media_downloader/__init__.py,sha256=d0PDULMlfmN1E52NKz20Wprllpq-UYMsBU3-2p4ZEk8,460
2
- media_downloader/__main__.py,sha256=aHajZBE7fyiC7E5qjV5LMe9nCPkQV2YKu6FU8Ubkdm4,112
3
- media_downloader/media_downloader.py,sha256=IALP6PvypBjvtFbqHyG8FN3B7BONBJpNiz1vsJrPUoY,9139
4
- media_downloader/media_downloader_mcp.py,sha256=zFjamIgQznRQvRdF98XWgdDeWN3A59dyaugXnHxBZrI,2983
5
- media_downloader/version.py,sha256=lsEyJBBq_52dtz5_4fHeohUPAKr4Tf_LbF6XI2Xid5s,118
6
- media_downloader-0.11.14.dist-info/LICENSE,sha256=Z1xmcrPHBnGCETO_LLQJUeaSNBSnuptcDVTt4kaPUOE,1060
7
- media_downloader-0.11.14.dist-info/METADATA,sha256=FQ-gxkQ9yOqP-BXqwcZXZl0xDqkYq48elNYmFKPktHk,5523
8
- media_downloader-0.11.14.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
9
- media_downloader-0.11.14.dist-info/entry_points.txt,sha256=Hjp1vLkHPq_bABsuh4kpL5nddi1wn9Ftota-72_GgW4,142
10
- media_downloader-0.11.14.dist-info/top_level.txt,sha256=B2OBmgONOm0hIyx2HJ8qFPOI_p5HOeolrYvmslVC1fc,17
11
- media_downloader-0.11.14.dist-info/RECORD,,