warp-beacon 2.3.39__tar.gz → 2.3.40__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.
Files changed (60) hide show
  1. {warp_beacon-2.3.39/warp_beacon.egg-info → warp_beacon-2.3.40}/PKG-INFO +2 -1
  2. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/pyproject.toml +2 -1
  3. warp_beacon-2.3.40/warp_beacon/__version__.py +2 -0
  4. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/exceptions.py +2 -0
  5. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/youtube/abstract.py +74 -16
  6. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/youtube/youtube.py +27 -7
  7. {warp_beacon-2.3.39 → warp_beacon-2.3.40/warp_beacon.egg-info}/PKG-INFO +2 -1
  8. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon.egg-info/requires.txt +1 -0
  9. warp_beacon-2.3.39/warp_beacon/__version__.py +0 -2
  10. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/LICENSE +0 -0
  11. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/MANIFEST.in +0 -0
  12. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/README.md +0 -0
  13. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/assets/placeholder.gif +0 -0
  14. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/etc/.gitignore +0 -0
  15. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/etc/accounts.json +0 -0
  16. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/etc/proxies.json +0 -0
  17. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/etc/warp_beacon.conf +0 -0
  18. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/etc/warp_beacon.service +0 -0
  19. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/setup.cfg +0 -0
  20. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/setup.py +0 -0
  21. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/__init__.py +0 -0
  22. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/compress/__init__.py +0 -0
  23. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/compress/video.py +0 -0
  24. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/jobs/__init__.py +0 -0
  25. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/jobs/abstract.py +0 -0
  26. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/jobs/download_job.py +0 -0
  27. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/jobs/types.py +0 -0
  28. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/jobs/upload_job.py +0 -0
  29. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/mediainfo/__init__.py +0 -0
  30. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/mediainfo/abstract.py +0 -0
  31. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/mediainfo/audio.py +0 -0
  32. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/mediainfo/silencer.py +0 -0
  33. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/mediainfo/video.py +0 -0
  34. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scheduler/__init__.py +0 -0
  35. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scheduler/instagram_human.py +0 -0
  36. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scheduler/scheduler.py +0 -0
  37. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/__init__.py +0 -0
  38. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/abstract.py +0 -0
  39. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/account_selector.py +0 -0
  40. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/fail_handler.py +0 -0
  41. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/instagram/__init__.py +0 -0
  42. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/instagram/instagram.py +0 -0
  43. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/link_resolver.py +0 -0
  44. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/youtube/__init__.py +0 -0
  45. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/youtube/music.py +0 -0
  46. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/scraper/youtube/shorts.py +0 -0
  47. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/storage/__init__.py +0 -0
  48. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/storage/mongo.py +0 -0
  49. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/__init__.py +0 -0
  50. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/bot.py +0 -0
  51. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/caption_shortener.py +0 -0
  52. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/handlers.py +0 -0
  53. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/placeholder_message.py +0 -0
  54. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/telegram/utils.py +0 -0
  55. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/uploader/__init__.py +0 -0
  56. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon/warp_beacon.py +0 -0
  57. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon.egg-info/SOURCES.txt +0 -0
  58. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon.egg-info/dependency_links.txt +0 -0
  59. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon.egg-info/entry_points.txt +0 -0
  60. {warp_beacon-2.3.39 → warp_beacon-2.3.40}/warp_beacon.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warp_beacon
3
- Version: 2.3.39
3
+ Version: 2.3.40
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -237,6 +237,7 @@ Requires-Dist: pillow
237
237
  Requires-Dist: pymongo
238
238
  Requires-Dist: instagrapi==2.0.0
239
239
  Requires-Dist: bs4
240
+ Requires-Dist: yt_dlp
240
241
  Dynamic: author
241
242
  Dynamic: home-page
242
243
  Dynamic: license-file
@@ -25,7 +25,8 @@ dependencies = [
25
25
  "pillow",
26
26
  "pymongo",
27
27
  "instagrapi == 2.0.0",
28
- "bs4"
28
+ "bs4",
29
+ "yt_dlp"
29
30
  ]
30
31
  keywords = ["bot", "telegram", "instagram", "export"]
31
32
  classifiers = [
@@ -0,0 +1,2 @@
1
+ __version__ = "2.3.40"
2
+
@@ -54,6 +54,8 @@ def extract_exception_message(e: Exception) -> str:
54
54
  msg = e.message
55
55
  elif hasattr(e, "reason"):
56
56
  msg = e.reason
57
+ elif hasattr(e, "msg"):
58
+ msg = e.msg
57
59
  elif hasattr(e, "args"):
58
60
  msg = str(e.args)
59
61
 
@@ -7,26 +7,29 @@ import socket
7
7
  import ssl
8
8
  #from abc import abstractmethod
9
9
  from typing import Callable, Union, Optional
10
+ import logging
10
11
  import json
11
12
  import urllib
12
13
  import http.client
14
+ import pytubefix.exceptions
13
15
  import requests
14
16
  from PIL import Image
15
17
  import numpy as np
18
+ from urllib.parse import urlparse, parse_qs
16
19
 
17
- from warp_beacon.jobs.download_job import DownloadJob
18
- from warp_beacon.scraper.abstract import ScraperAbstract
19
- #from warp_beacon.mediainfo.abstract import MediaInfoAbstract
20
- from warp_beacon.scraper.exceptions import TimeOut, Unavailable, extract_exception_message
21
-
20
+ import pytubefix
22
21
  from pytubefix import YouTube
23
22
  from pytubefix.innertube import _default_clients
24
23
  from pytubefix.streams import Stream
25
24
  from pytubefix.innertube import InnerTube, _client_id, _client_secret
26
- from pytubefix.exceptions import VideoUnavailable, VideoPrivate, MaxRetriesExceeded
25
+ #from pytubefix.exceptions import VideoUnavailable, VideoPrivate, MaxRetriesExceeded
27
26
  from pytubefix import request
27
+ import yt_dlp
28
28
 
29
- import logging
29
+ from warp_beacon.jobs.download_job import DownloadJob
30
+ from warp_beacon.scraper.abstract import ScraperAbstract
31
+ #from warp_beacon.mediainfo.abstract import MediaInfoAbstract
32
+ from warp_beacon.scraper.exceptions import TimeOut, Unavailable, extract_exception_message
30
33
 
31
34
  def patched_fetch_bearer_token(self) -> None:
32
35
  """Fetch an OAuth token."""
@@ -101,13 +104,24 @@ class YoutubeAbstract(ScraperAbstract):
101
104
  os.rename(filename, new_filepath)
102
105
 
103
106
  return new_filepath
107
+
108
+ def get_video_id(self, url: str) -> Optional[str]:
109
+ parsed_url = urlparse(url)
110
+ query = parse_qs(parsed_url.query)
111
+ return query.get('v', [None])[0]
104
112
 
105
113
  def remove_tmp_files(self) -> None:
106
114
  for i in os.listdir(self.DOWNLOAD_DIR):
107
115
  if "yt_download_" in i:
108
116
  os.unlink("%s/%s" % (self.DOWNLOAD_DIR, i))
109
117
 
110
- def calculate_size_with_padding(self, image: Image, aspect_ratio_width: int, aspect_ratio_height: int, target_size: tuple=(320, 320), background_color: tuple=(0, 0, 0)) -> Image:
118
+ def calculate_size_with_padding(self,
119
+ image: Image,
120
+ aspect_ratio_width: int,
121
+ aspect_ratio_height: int,
122
+ target_size: tuple=(320, 320),
123
+ background_color: tuple=(0, 0, 0)
124
+ ) -> Image:
111
125
  aspect_ratio = aspect_ratio_width / aspect_ratio_height
112
126
  target_width, target_height = target_size
113
127
 
@@ -194,7 +208,7 @@ class YoutubeAbstract(ScraperAbstract):
194
208
  kwargs["timeout"] = timeout
195
209
  ret_val = func(*args, **kwargs)
196
210
  break
197
- except MaxRetriesExceeded:
211
+ except pytubefix.exceptions.MaxRetriesExceeded:
198
212
  # do noting, not interested
199
213
  pass
200
214
  #except http.client.IncompleteRead as e:
@@ -216,7 +230,21 @@ class YoutubeAbstract(ScraperAbstract):
216
230
  retries += 1
217
231
  timeout += timeout_increment
218
232
  time.sleep(pause_secs)
219
- except (VideoUnavailable, VideoPrivate) as e:
233
+ except (pytubefix.exceptions.VideoUnavailable, pytubefix.exceptions.VideoPrivate) as e:
234
+ raise Unavailable(extract_exception_message(e))
235
+ except yt_dlp.utils.DownloadError as e:
236
+ raise Unavailable(extract_exception_message(e))
237
+ except yt_dlp.utils.GeoRestrictedError:
238
+ raise Unavailable(extract_exception_message(e))
239
+ except yt_dlp.utils.PostProcessingError as e:
240
+ raise Unavailable(extract_exception_message(e))
241
+ except yt_dlp.utils.ExtractorError as e:
242
+ raise Unavailable(extract_exception_message(e))
243
+ except yt_dlp.utils.MaxDownloadsReached as e:
244
+ raise Unavailable(extract_exception_message(e))
245
+ except yt_dlp.utils.UnavailableVideoError as e:
246
+ raise Unavailable(extract_exception_message(e))
247
+ except yt_dlp.utils.ThrottledDownload as e:
220
248
  raise Unavailable(extract_exception_message(e))
221
249
 
222
250
  return ret_val
@@ -241,8 +269,8 @@ class YoutubeAbstract(ScraperAbstract):
241
269
  yt_opts["token_file"] = self.YT_SESSION_FILE % self.account_index
242
270
  if self.proxy:
243
271
  proxy_dsn = self.proxy.get("dsn", "")
272
+ logging.info("Using proxy DSN '%s'", proxy_dsn)
244
273
  if proxy_dsn:
245
- logging.info("Using proxy DSN '%s'", proxy_dsn)
246
274
  if "https://" in proxy_dsn:
247
275
  yt_opts["proxies"] = {"https": proxy_dsn}
248
276
  elif "http://" in proxy_dsn:
@@ -251,14 +279,44 @@ class YoutubeAbstract(ScraperAbstract):
251
279
  logging.warning("Proxy DSN malformed!")
252
280
  return YouTube(**yt_opts)
253
281
 
282
+ def build_yt_dlp(self) -> yt_dlp.YoutubeDL:
283
+ auth_data = {}
284
+ with open(self.YT_SESSION_FILE % self.account_index, 'r', encoding="utf-8") as f:
285
+ auth_data = json.loads(f.read())
286
+ time_name = str(time.time()).replace('.', '_')
287
+ ydl_opts = {
288
+ 'outtmpl': f'{self.DOWNLOAD_DIR}/{time_name}.%(ext)s',
289
+ 'format': 'bestvideo+bestaudio/best',
290
+ 'merge_output_format': 'mp4',
291
+ 'noplaylist': True,
292
+ 'tv_auth': auth_data
293
+ }
294
+
295
+ if self.proxy:
296
+ proxy_dsn = self.proxy.get("dsn", "")
297
+ logging.info("Using proxy DSN '%s'", proxy_dsn)
298
+ if proxy_dsn:
299
+ ydl_opts["proxy"] = proxy_dsn
300
+
301
+ return yt_dlp.YoutubeDL(ydl_opts)
302
+
254
303
  def _download(self, _: str) -> list:
255
304
  raise NotImplementedError("You should to implement _download method")
305
+
306
+ def _download_yt_dlp(self, _: str) -> list:
307
+ raise NotImplementedError("You should to implement _download_yt_dlp method")
256
308
 
257
309
  def download(self, job: DownloadJob) -> list:
310
+ ret = []
258
311
  try:
259
- return self.download_hndlr(self._download, job.url, session=False)
260
- except Unavailable:
261
- logging.warning("Failed sessionless download attempt. Trying with session.")
262
- time.sleep(2)
312
+ ret = self.download_hndlr(self._download, job.url, session=True)
313
+ except (Unavailable, TimeOut):
314
+ logging.warning("Download failed, trying to download with yt_dlp")
263
315
 
264
- return self.download_hndlr(self._download, job.url, session=True)
316
+ try:
317
+ ret = self.download_hndlr(self._download_yt_dlp, job.url)
318
+ except NotImplementedError:
319
+ logging.info("yt_dlp is not supported for this submodule yet")
320
+ raise Unavailable("Video unvailable")
321
+
322
+ return ret
@@ -1,10 +1,10 @@
1
- from warp_beacon.jobs.types import JobType
2
- from warp_beacon.scraper.youtube.abstract import YoutubeAbstract
3
- from warp_beacon.scraper.exceptions import YoutubeLiveError, NotFound, YotubeAgeRestrictedError
1
+ import logging
4
2
 
5
3
  from pytubefix.exceptions import AgeRestrictedError
6
4
 
7
- import logging
5
+ from warp_beacon.jobs.types import JobType
6
+ from warp_beacon.scraper.youtube.abstract import YoutubeAbstract
7
+ from warp_beacon.scraper.exceptions import YoutubeLiveError, NotFound, YotubeAgeRestrictedError
8
8
 
9
9
  class YoutubeScraper(YoutubeAbstract):
10
10
  YT_MAX_RETRIES_DEFAULT = 8
@@ -32,14 +32,14 @@ class YoutubeScraper(YoutubeAbstract):
32
32
  res = []
33
33
  try:
34
34
  thumbnail = None
35
+ video_id = self.get_video_id(url)
36
+ if video_id:
37
+ thumbnail = self.download_hndlr(self.download_thumbnail, video_id)
35
38
  yt = self.build_yt(url, session=session)
36
39
 
37
40
  if self.is_live(yt.initial_data):
38
41
  raise YoutubeLiveError("Youtube Live is not supported")
39
42
 
40
- if yt:
41
- thumbnail = self.download_hndlr(self.download_thumbnail, yt.video_id)
42
-
43
43
  stream = yt.streams.get_highest_resolution()
44
44
 
45
45
  if not stream:
@@ -65,4 +65,24 @@ class YoutubeScraper(YoutubeAbstract):
65
65
  except AgeRestrictedError as e:
66
66
  raise YotubeAgeRestrictedError("Youtube Age Restricted error")
67
67
 
68
+ return res
69
+
70
+ def _download_yt_dlp(self, url: str) -> list:
71
+ res = []
72
+ thumbnail = None
73
+ video_id = self.get_video_id(url)
74
+ if video_id:
75
+ thumbnail = self.download_hndlr(self.download_thumbnail, video_id)
76
+ with self.build_yt_dlp() as ydl:
77
+ info = ydl.extract_info(url, download=True)
78
+ local_file = ydl.prepare_filename(info)
79
+ logging.debug("Temp filename: '%s'", local_file)
80
+ res.append({
81
+ "local_media_path": local_file,
82
+ "performer": info.get("uploader", "Unknown"),
83
+ "thumb": thumbnail,
84
+ "canonical_name": info.get("title", ''),
85
+ "media_type": JobType.VIDEO
86
+ })
87
+
68
88
  return res
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warp_beacon
3
- Version: 2.3.39
3
+ Version: 2.3.40
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -237,6 +237,7 @@ Requires-Dist: pillow
237
237
  Requires-Dist: pymongo
238
238
  Requires-Dist: instagrapi==2.0.0
239
239
  Requires-Dist: bs4
240
+ Requires-Dist: yt_dlp
240
241
  Dynamic: author
241
242
  Dynamic: home-page
242
243
  Dynamic: license-file
@@ -11,3 +11,4 @@ pillow
11
11
  pymongo
12
12
  instagrapi==2.0.0
13
13
  bs4
14
+ yt_dlp
@@ -1,2 +0,0 @@
1
- __version__ = "2.3.39"
2
-
File without changes
File without changes
File without changes
File without changes
File without changes