warp-beacon 2.3.44__py3-none-any.whl → 2.3.46__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.
- warp_beacon/__version__.py +1 -1
- warp_beacon/jobs/abstract.py +2 -0
- warp_beacon/scraper/__init__.py +25 -17
- warp_beacon/scraper/account_selector.py +47 -12
- warp_beacon/scraper/exceptions.py +3 -0
- warp_beacon/scraper/youtube/abstract.py +2 -1
- warp_beacon/scraper/youtube/music.py +35 -32
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/METADATA +1 -1
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/RECORD +13 -13
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/WHEEL +0 -0
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/entry_points.txt +0 -0
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/licenses/LICENSE +0 -0
- {warp_beacon-2.3.44.dist-info → warp_beacon-2.3.46.dist-info}/top_level.txt +0 -0
warp_beacon/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
__version__ = "2.3.
|
1
|
+
__version__ = "2.3.46"
|
2
2
|
|
warp_beacon/jobs/abstract.py
CHANGED
@@ -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
|
warp_beacon/scraper/__init__.py
CHANGED
@@ -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,
|
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(),
|
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(),
|
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(),
|
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(),
|
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
|
-
|
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 >
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
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
|
-
|
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:
|
@@ -218,7 +218,8 @@ class YoutubeAbstract(ScraperAbstract):
|
|
218
218
|
http.client.HTTPException,
|
219
219
|
requests.RequestException,
|
220
220
|
urllib.error.URLError,
|
221
|
-
urllib.error.HTTPError
|
221
|
+
urllib.error.HTTPError,
|
222
|
+
KeyError) as e:
|
222
223
|
if hasattr(e, "code") and (int(e.code) == 403 or int(e.code) == 400):
|
223
224
|
raise Unavailable(extract_exception_message(e))
|
224
225
|
logging.warning("Youtube read timeout! Retrying in '%d' seconds ...", pause_secs)
|
@@ -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
|
-
|
22
|
-
|
21
|
+
try:
|
22
|
+
thumbnail = None
|
23
|
+
audio_id = self.get_video_id(url)
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
if audio_id:
|
26
|
+
thumbnail = self.download_hndlr(self.download_thumbnail, audio_id)
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
|
@@ -4,12 +4,12 @@ var/warp_beacon/accounts.json,sha256=OsXdncs6h88xrF_AP6_WDCK1waGBn9SR-uYdIeK37GM
|
|
4
4
|
var/warp_beacon/placeholder.gif,sha256=cE5CGJVaop4Sx21zx6j4AyoHU0ncmvQuS2o6hJfEH88,6064
|
5
5
|
var/warp_beacon/proxies.json,sha256=VnjlQDXumOEq72ZFjbh6IqHS1TEHqn8HPYAZqWCeSIA,95
|
6
6
|
warp_beacon/__init__.py,sha256=_rThNODmz0nDp_n4mWo_HKaNFE5jk1_7cRhHyYaencI,163
|
7
|
-
warp_beacon/__version__.py,sha256=
|
7
|
+
warp_beacon/__version__.py,sha256=WwmzdMPCUOj0exfQEPh8jozMYxz4FGxv_V9AJozlyKM,24
|
8
8
|
warp_beacon/warp_beacon.py,sha256=7KEtZDj-pdhtl6m-zFLsSojs1ZR4o7L0xbqtdmYPvfE,342
|
9
9
|
warp_beacon/compress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
warp_beacon/compress/video.py,sha256=_PDMVYCyzLYxHv1uZmmzGcG_8rjaZr7BTXsXTTy_oS4,2846
|
11
11
|
warp_beacon/jobs/__init__.py,sha256=ED8_tPle4iL4kqNW0apAVkgNQtRRTnYfAJwBjO1g0JY,180
|
12
|
-
warp_beacon/jobs/abstract.py,sha256=
|
12
|
+
warp_beacon/jobs/abstract.py,sha256=IF2bKx2DuuH_AP07YAvFxxqnuxjIwg2Glsr8TpWX6Z0,3179
|
13
13
|
warp_beacon/jobs/download_job.py,sha256=5HiPcnJppFMhO14___3eSkoMygM3y-vhpGkMAuNhK7s,854
|
14
14
|
warp_beacon/jobs/types.py,sha256=Ae8zINgbs7cOcYkYoOCOACA7duyhnIGMQAJ_SJB1QRQ,176
|
15
15
|
warp_beacon/jobs/upload_job.py,sha256=_ul4psPej1jLEs-BMcMR80GbXDSmm38jE9yoZtecclY,741
|
@@ -21,17 +21,17 @@ warp_beacon/mediainfo/video.py,sha256=UBZrhTN5IDI-aYu6tsJEILo9nFkjHhkldGVFmvV7tE
|
|
21
21
|
warp_beacon/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
warp_beacon/scheduler/instagram_human.py,sha256=K3S14uav0UBZidtREYwgCOCDKyDfWbay5GH-DWMK_JI,5020
|
23
23
|
warp_beacon/scheduler/scheduler.py,sha256=ddPhExSgj_RGR1eSDlIm0hVgTVM_76UEMEl9i-eYuQ8,2915
|
24
|
-
warp_beacon/scraper/__init__.py,sha256=
|
24
|
+
warp_beacon/scraper/__init__.py,sha256=cxRbhXFW69KbAWE8b2-j9pfUBSMSh95aNyf0GFluyig,17188
|
25
25
|
warp_beacon/scraper/abstract.py,sha256=28a0aBKZpi8IKptLWdB6RuVbOkrUbrhT7LSZX7QRQtg,2725
|
26
|
-
warp_beacon/scraper/account_selector.py,sha256=
|
27
|
-
warp_beacon/scraper/exceptions.py,sha256=
|
26
|
+
warp_beacon/scraper/account_selector.py,sha256=FyoZ0-YGuxqWzepIqgwJmPn0AC9u13RQC3F7Cl-BVoc,6284
|
27
|
+
warp_beacon/scraper/exceptions.py,sha256=L4UCZqDOLKu5-TPUcTErIbNDLrAQJghkMaPqzTfsm1g,1376
|
28
28
|
warp_beacon/scraper/fail_handler.py,sha256=_blvckfTZ4xWVancQKVRXH5ClKGwfrBxMwvXIFZh1qA,975
|
29
29
|
warp_beacon/scraper/link_resolver.py,sha256=Rc9ZuMyOo3iPywDHwjngy-WRQ2SXhJwxcg-5ripx7tM,2447
|
30
30
|
warp_beacon/scraper/instagram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
31
|
warp_beacon/scraper/instagram/instagram.py,sha256=TpIBS9S3zcLbeGax4CENVo6WP75EE6oIttc-MjWYVjs,13815
|
32
32
|
warp_beacon/scraper/youtube/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
-
warp_beacon/scraper/youtube/abstract.py,sha256=
|
34
|
-
warp_beacon/scraper/youtube/music.py,sha256
|
33
|
+
warp_beacon/scraper/youtube/abstract.py,sha256=gGtWjsFCcJ8kYDXD4BtWCQ04uYW0jdIwWexJ84p8wFU,11788
|
34
|
+
warp_beacon/scraper/youtube/music.py,sha256=-R6Iys8IpglH5UL7F640UQ3ieYHitvGXA0A2GhSemVA,2927
|
35
35
|
warp_beacon/scraper/youtube/shorts.py,sha256=TVQP-Dm4ZV3bHd7u25yOpDHRzoXL3jUAszDb6ZW8p0U,1269
|
36
36
|
warp_beacon/scraper/youtube/youtube.py,sha256=B1NBz6vv0-zIuf3MDmT2QB7eX2qO8k9orj7-4wU6MBk,2839
|
37
37
|
warp_beacon/storage/__init__.py,sha256=0Vajd0oITKJfu2vmNx5uQSt3-L6vwIvUYWJo8HZCjco,3398
|
@@ -43,9 +43,9 @@ warp_beacon/telegram/handlers.py,sha256=XXIfdV_RCj7tyZMPXchuKmGoDdweOaR08ADDaBPW
|
|
43
43
|
warp_beacon/telegram/placeholder_message.py,sha256=wN9-BRiyrtHG-EvXtZkGJHt2CX71munQ57ITttjt0mw,6400
|
44
44
|
warp_beacon/telegram/utils.py,sha256=9uebX53G16mV7ER7WgfdWBLFHHw14S8HBt9URrIskg0,4440
|
45
45
|
warp_beacon/uploader/__init__.py,sha256=E9rlZIf7xlQz6MutMOwJ8S5Vm2uheR5nv23Kv8duRQg,5427
|
46
|
-
warp_beacon-2.3.
|
47
|
-
warp_beacon-2.3.
|
48
|
-
warp_beacon-2.3.
|
49
|
-
warp_beacon-2.3.
|
50
|
-
warp_beacon-2.3.
|
51
|
-
warp_beacon-2.3.
|
46
|
+
warp_beacon-2.3.46.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
47
|
+
warp_beacon-2.3.46.dist-info/METADATA,sha256=p7yMyzJosIJhR3oM0REGyQzBej8DhsXIrMdKqAPg9HY,22626
|
48
|
+
warp_beacon-2.3.46.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
49
|
+
warp_beacon-2.3.46.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
|
50
|
+
warp_beacon-2.3.46.dist-info/top_level.txt,sha256=qGjHVVfyf6lTmbdSA-fQq0rHS1YVS4HoJT3rag5xgPE,1141
|
51
|
+
warp_beacon-2.3.46.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|