warp-beacon 2.3.44__tar.gz → 2.3.45__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.44/warp_beacon.egg-info → warp_beacon-2.3.45}/PKG-INFO +1 -1
  2. warp_beacon-2.3.45/warp_beacon/__version__.py +2 -0
  3. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/jobs/abstract.py +2 -0
  4. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/__init__.py +25 -17
  5. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/account_selector.py +47 -12
  6. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/exceptions.py +3 -0
  7. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/youtube/music.py +35 -32
  8. {warp_beacon-2.3.44 → warp_beacon-2.3.45/warp_beacon.egg-info}/PKG-INFO +1 -1
  9. warp_beacon-2.3.44/warp_beacon/__version__.py +0 -2
  10. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/LICENSE +0 -0
  11. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/MANIFEST.in +0 -0
  12. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/README.md +0 -0
  13. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/assets/placeholder.gif +0 -0
  14. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/etc/.gitignore +0 -0
  15. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/etc/accounts.json +0 -0
  16. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/etc/proxies.json +0 -0
  17. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/etc/warp_beacon.conf +0 -0
  18. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/etc/warp_beacon.service +0 -0
  19. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/pyproject.toml +0 -0
  20. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/setup.cfg +0 -0
  21. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/setup.py +0 -0
  22. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/__init__.py +0 -0
  23. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/compress/__init__.py +0 -0
  24. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/compress/video.py +0 -0
  25. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/jobs/__init__.py +0 -0
  26. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/jobs/download_job.py +0 -0
  27. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/jobs/types.py +0 -0
  28. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/jobs/upload_job.py +0 -0
  29. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/mediainfo/__init__.py +0 -0
  30. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/mediainfo/abstract.py +0 -0
  31. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/mediainfo/audio.py +0 -0
  32. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/mediainfo/silencer.py +0 -0
  33. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/mediainfo/video.py +0 -0
  34. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scheduler/__init__.py +0 -0
  35. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scheduler/instagram_human.py +0 -0
  36. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scheduler/scheduler.py +0 -0
  37. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/abstract.py +0 -0
  38. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/fail_handler.py +0 -0
  39. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/instagram/__init__.py +0 -0
  40. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/instagram/instagram.py +0 -0
  41. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/link_resolver.py +0 -0
  42. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/youtube/__init__.py +0 -0
  43. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/youtube/abstract.py +0 -0
  44. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/youtube/shorts.py +0 -0
  45. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/scraper/youtube/youtube.py +0 -0
  46. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/storage/__init__.py +0 -0
  47. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/storage/mongo.py +0 -0
  48. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/__init__.py +0 -0
  49. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/bot.py +0 -0
  50. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/caption_shortener.py +0 -0
  51. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/handlers.py +0 -0
  52. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/placeholder_message.py +0 -0
  53. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/telegram/utils.py +0 -0
  54. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/uploader/__init__.py +0 -0
  55. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon/warp_beacon.py +0 -0
  56. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon.egg-info/SOURCES.txt +0 -0
  57. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon.egg-info/dependency_links.txt +0 -0
  58. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon.egg-info/entry_points.txt +0 -0
  59. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/warp_beacon.egg-info/requires.txt +0 -0
  60. {warp_beacon-2.3.44 → warp_beacon-2.3.45}/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.44
3
+ Version: 2.3.45
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
@@ -0,0 +1,2 @@
1
+ __version__ = "2.3.45"
2
+
@@ -38,6 +38,7 @@ class JobSettings(TypedDict):
38
38
  unvailable_error_count: int
39
39
  geoblock_error_count: int
40
40
  account_switches: int
41
+ bad_proxy_error_count: int
41
42
  yt_auth: bool
42
43
  session_validation: bool
43
44
  chat_type: ChatType
@@ -76,6 +77,7 @@ class AbstractJob(ABC):
76
77
  unvailable_error_count: int = 0
77
78
  geoblock_error_count: int = 0
78
79
  account_switches: int = 0
80
+ bad_proxy_error_count: int = 0
79
81
  yt_auth: bool = False
80
82
  session_validation: bool = False
81
83
  chat_type: ChatType = None
@@ -7,7 +7,8 @@ from queue import Empty
7
7
 
8
8
  import logging
9
9
 
10
- from warp_beacon.scraper.exceptions import NotFound, UnknownError, TimeOut, Unavailable, FileTooBig, YoutubeLiveError, YotubeAgeRestrictedError, IGRateLimitOccurred, CaptchaIssue, AllAccountsFailed
10
+ from warp_beacon.scraper.exceptions import NotFound, UnknownError, TimeOut, Unavailable, FileTooBig, YoutubeLiveError, \
11
+ YotubeAgeRestrictedError, IGRateLimitOccurred, CaptchaIssue, AllAccountsFailed, BadProxy
11
12
  from warp_beacon.mediainfo.video import VideoInfo
12
13
  from warp_beacon.mediainfo.audio import AudioInfo
13
14
  from warp_beacon.mediainfo.silencer import Silencer
@@ -116,21 +117,23 @@ class AsyncDownloader(object):
116
117
  continue
117
118
  actor = None
118
119
  self.acc_selector.set_module(job.job_origin)
120
+ proxy = selector.get_current_proxy()
119
121
  if job.job_origin is Origin.INSTAGRAM:
120
122
  from warp_beacon.scraper.instagram.instagram import InstagramScraper
121
- actor = InstagramScraper(selector.get_current(), selector.get_account_proxy())
123
+ actor = InstagramScraper(selector.get_current(), proxy)
122
124
  elif job.job_origin is Origin.YT_SHORTS:
123
125
  from warp_beacon.scraper.youtube.shorts import YoutubeShortsScraper
124
- actor = YoutubeShortsScraper(selector.get_current(), selector.get_account_proxy())
126
+ actor = YoutubeShortsScraper(selector.get_current(), proxy)
125
127
  elif job.job_origin is Origin.YT_MUSIC:
126
128
  from warp_beacon.scraper.youtube.music import YoutubeMusicScraper
127
- actor = YoutubeMusicScraper(selector.get_current(), selector.get_account_proxy())
129
+ actor = YoutubeMusicScraper(selector.get_current(), proxy)
128
130
  elif job.job_origin is Origin.YOUTUBE:
129
131
  from warp_beacon.scraper.youtube.youtube import YoutubeScraper
130
- actor = YoutubeScraper(selector.get_current(), selector.get_account_proxy())
132
+ actor = YoutubeScraper(selector.get_current(), proxy)
131
133
  actor.send_message_to_admin_func = self.send_message_to_admin
132
134
  actor.auth_event = self.auth_event
133
- while True:
135
+ # job retry loop
136
+ while self.allow_loop.value == 1:
134
137
  try:
135
138
  if job.session_validation:
136
139
  logging.info("Validating '%s' session ...", job.job_origin.value)
@@ -154,7 +157,7 @@ class AsyncDownloader(object):
154
157
  except Unavailable as e:
155
158
  logging.warning("Not found or unavailable error occurred!")
156
159
  logging.exception(e)
157
- if job.unvailable_error_count > self.acc_selector.count_service_accounts(job.job_origin):
160
+ if job.unvailable_error_count > selector.count_service_accounts(job.job_origin):
158
161
  self.uploader.queue_task(job.to_upload_job(
159
162
  job_failed=True,
160
163
  job_failed_msg="Video is unvailable for all your service accounts.")
@@ -165,17 +168,22 @@ class AsyncDownloader(object):
165
168
  selector.next()
166
169
  self.job_queue.put(job)
167
170
  break
168
- except TimeOut as e:
169
- logging.warning("Timeout error occurred!")
171
+ except (TimeOut, BadProxy) as e:
172
+ logging.warning("Timeout or BadProxy error occurred!")
170
173
  logging.exception(e)
171
- self.send_message_to_admin(
172
- f"Task <code>{job.job_id}</code> failed. URL: '{job.url}'. Reason: '<b>TimeOut</b>'."
173
- )
174
- self.uploader.queue_task(job.to_upload_job(
175
- job_failed=True,
176
- job_failed_msg="Failed to download content due timeout error. Please check you Internet connection, retry amount or request timeout bot configuration settings.")
177
- )
178
- break
174
+ if job.bad_proxy_error_count > len(selector.proxies):
175
+ self.send_message_to_admin(
176
+ f"Task <code>{job.job_id}</code> failed. URL: '{job.url}'. Reason: '<b>TimeOut</b>'."
177
+ )
178
+ self.uploader.queue_task(job.to_upload_job(
179
+ job_failed=True,
180
+ job_failed_msg="Failed to download content due timeout error. Please check you Internet connection, retry amount or request timeout bot configuration settings.")
181
+ )
182
+ break
183
+ job.bad_proxy_error_count += 1
184
+ logging.info("Trying next proxy")
185
+ selector.next_proxy()
186
+ self.job_queue.put(job)
179
187
  except FileTooBig as e:
180
188
  logging.warning("Telegram limits exceeded :(")
181
189
  logging.exception(e)
@@ -3,8 +3,9 @@ import time
3
3
  import random
4
4
  import json
5
5
  import re
6
- from typing import Optional
6
+ from typing import Optional, List
7
7
 
8
+ from itertools import cycle
8
9
  import multiprocessing
9
10
  import multiprocessing.managers
10
11
 
@@ -21,6 +22,7 @@ class AccountSelector(object):
21
22
  session_dir = "/var/warp_beacon"
22
23
  manager = None
23
24
  account_index = {}
25
+ current_proxy = None
24
26
 
25
27
  def __init__(self, manager: multiprocessing.managers.SyncManager, acc_file_path: str, proxy_file_path: str=None) -> None:
26
28
  self.manager = manager
@@ -36,22 +38,35 @@ class AccountSelector(object):
36
38
  if proxy_file_path:
37
39
  with open(proxy_file_path, 'r', encoding="utf-8") as f:
38
40
  self.proxies = json.loads(f.read())
41
+ self.current_proxy = self.get_random_account_proxy()
39
42
  else:
40
43
  raise ValueError("Accounts file not found")
41
44
 
42
- def get_account_proxy(self) -> Optional[dict]:
45
+ def get_current_proxy(self) -> Optional[dict]:
46
+ return self.current_proxy
47
+
48
+ def get_proxy_list(self) -> List[dict]:
49
+ matched_proxy = []
50
+ try:
51
+ acc_id, acc_data = self.get_current()
52
+ current_acc_pid = acc_data.get("proxy_id", "").strip()
53
+ for proxy in self.proxies:
54
+ pid = proxy.get("id", "").strip()
55
+ if pid and current_acc_pid and pid == current_acc_pid:
56
+ if "override_force_ipv6" in proxy:
57
+ self.accounts[self.current_module_name][acc_id]["force_ipv6"] = proxy.get("override_force_ipv6", False)
58
+ logging.info("Account proxy matched '%s'", proxy)
59
+ matched_proxy.append(proxy)
60
+ except Exception as e:
61
+ logging.warning("Failed to form proxy list!")
62
+ logging.exception(e)
63
+
64
+ return matched_proxy
65
+
66
+ def get_random_account_proxy(self) -> Optional[dict]:
43
67
  if self.proxies:
44
68
  try:
45
- acc_id, acc_data = self.get_current()
46
- current_acc_pid = acc_data.get("proxy_id", "").strip()
47
- matched_proxy = []
48
- for proxy in self.proxies:
49
- pid = proxy.get("id", "").strip()
50
- if pid and current_acc_pid and pid == current_acc_pid:
51
- if "override_force_ipv6" in proxy:
52
- self.accounts[self.current_module_name][acc_id]["force_ipv6"] = proxy.get("override_force_ipv6", False)
53
- logging.info("Account proxy matched '%s'", proxy)
54
- matched_proxy.append(proxy)
69
+ matched_proxy = self.get_proxy_list()
55
70
  if matched_proxy:
56
71
  if len(matched_proxy) > 1:
57
72
  random.seed(random.seed(time.time_ns() ^ int.from_bytes(os.urandom(len(matched_proxy)), "big")))
@@ -62,12 +77,32 @@ class AccountSelector(object):
62
77
  prox_choice = random.choice(matched_proxy)
63
78
  # saving chosen proxy for history
64
79
  self.accounts_meta_data["last_proxy"] = prox_choice
80
+ self.current_proxy = prox_choice
65
81
  logging.info("Chosen proxy: '%s'", prox_choice)
66
82
  return prox_choice
67
83
  except Exception as e:
68
84
  logging.warning("Error on selecting account proxy!")
69
85
  logging.exception(e)
70
86
  return None
87
+
88
+ def next_proxy(self) -> Optional[dict]:
89
+ if not self.proxies:
90
+ return None
91
+ proxy = None
92
+ try:
93
+ matched_proxies = self.get_proxy_list()
94
+ if matched_proxies:
95
+ lit = cycle(matched_proxies)
96
+ proxy = next(lit)
97
+ if proxy.get("dsn", {}) == self.accounts_meta_data["last_proxy"].get("dsn", {}):
98
+ proxy = next(lit)
99
+ self.current_proxy = proxy
100
+ self.accounts_meta_data["last_proxy"] = proxy
101
+ except Exception as e:
102
+ logging.warning("Error on selection next proxy!")
103
+ logging.exception(e)
104
+
105
+ return proxy
71
106
 
72
107
  def load_yt_sessions(self) -> None:
73
108
  if "youtube" not in self.accounts:
@@ -41,6 +41,9 @@ class CaptchaIssue(ScraperError):
41
41
  class AllAccountsFailed(ScraperError):
42
42
  pass
43
43
 
44
+ class BadProxy(ScraperError):
45
+ pass
46
+
44
47
  class UnknownError(ScraperError):
45
48
  pass
46
49
 
@@ -7,7 +7,7 @@ import yt_dlp
7
7
 
8
8
  from warp_beacon.jobs.types import JobType
9
9
  from warp_beacon.scraper.youtube.abstract import YoutubeAbstract
10
- from warp_beacon.scraper.exceptions import NotFound, FileTooBig
10
+ from warp_beacon.scraper.exceptions import NotFound, FileTooBig, Unavailable
11
11
 
12
12
 
13
13
  class YoutubeMusicScraper(YoutubeAbstract):
@@ -18,39 +18,42 @@ class YoutubeMusicScraper(YoutubeAbstract):
18
18
 
19
19
  def _download(self, url: str, session: bool = True, timeout: int = 0) -> list:
20
20
  res = []
21
- thumbnail = None
22
- audio_id = self.get_video_id(url)
21
+ try:
22
+ thumbnail = None
23
+ audio_id = self.get_video_id(url)
23
24
 
24
- if audio_id:
25
- thumbnail = self.download_hndlr(self.download_thumbnail, audio_id)
25
+ if audio_id:
26
+ thumbnail = self.download_hndlr(self.download_thumbnail, audio_id)
26
27
 
27
- yt = self.build_yt(url, session=session)
28
-
29
- stream = yt.streams.get_audio_only()
30
-
31
- if not stream:
32
- raise NotFound("No suitable audio stream found")
33
-
34
- logging.info("Announced audio file size: '%d'", stream.filesize)
35
- if stream.filesize > 2e+9:
36
- logging.warning("Downloading size reported by YouTube is over than 2 GB!")
37
- raise FileTooBig("YouTube file is larger than 2 GB")
38
- logging.info("Operation timeout is '%d'", timeout)
39
- local_file = stream.download(
40
- output_path=self.DOWNLOAD_DIR,
41
- max_retries=0,
42
- timeout=timeout,
43
- skip_existing=False,
44
- filename_prefix='yt_download_'
45
- )
46
- logging.debug("Temp filename: '%s'", local_file)
47
- res.append({
48
- "local_media_path": self.rename_local_file(local_file),
49
- "performer": yt.author,
50
- "thumb": thumbnail,
51
- "canonical_name": stream.title,
52
- "media_type": JobType.AUDIO
53
- })
28
+ yt = self.build_yt(url, session=session)
29
+
30
+ stream = yt.streams.get_audio_only()
31
+
32
+ if not stream:
33
+ raise NotFound("No suitable audio stream found")
34
+
35
+ logging.info("Announced audio file size: '%d'", stream.filesize)
36
+ if stream.filesize > 2e+9:
37
+ logging.warning("Downloading size reported by YouTube is over than 2 GB!")
38
+ raise FileTooBig("YouTube file is larger than 2 GB")
39
+ logging.info("Operation timeout is '%d'", timeout)
40
+ local_file = stream.download(
41
+ output_path=self.DOWNLOAD_DIR,
42
+ max_retries=0,
43
+ timeout=timeout,
44
+ skip_existing=False,
45
+ filename_prefix='yt_download_'
46
+ )
47
+ logging.debug("Temp filename: '%s'", local_file)
48
+ res.append({
49
+ "local_media_path": self.rename_local_file(local_file),
50
+ "performer": yt.author,
51
+ "thumb": thumbnail,
52
+ "canonical_name": stream.title,
53
+ "media_type": JobType.AUDIO
54
+ })
55
+ except Exception:
56
+ raise Unavailable("Content unavailable")
54
57
 
55
58
  return res
56
59
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warp_beacon
3
- Version: 2.3.44
3
+ Version: 2.3.45
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
@@ -1,2 +0,0 @@
1
- __version__ = "2.3.44"
2
-
File without changes
File without changes
File without changes
File without changes
File without changes