warp-beacon 2.6.31__tar.gz → 2.6.33__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.
- {warp_beacon-2.6.31/warp_beacon.egg-info → warp_beacon-2.6.33}/PKG-INFO +1 -1
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/setup.py +1 -0
- warp_beacon-2.6.33/warp_beacon/__version__.py +2 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/__init__.py +10 -3
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/account_selector.py +8 -1
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/instagram/instagram.py +6 -2
- warp_beacon-2.6.33/warp_beacon/scraper/utils.py +40 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33/warp_beacon.egg-info}/PKG-INFO +1 -1
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon.egg-info/SOURCES.txt +1 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon.egg-info/top_level.txt +1 -0
- warp_beacon-2.6.31/warp_beacon/__version__.py +0 -2
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/LICENSE +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/MANIFEST.in +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/README.md +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/assets/placeholder.gif +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/etc/.gitignore +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/etc/accounts.json +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/etc/proxies.json +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/etc/warp_beacon.conf +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/etc/warp_beacon.service +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/pyproject.toml +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/setup.cfg +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/compress/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/compress/video.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/jobs/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/jobs/abstract.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/jobs/download_job.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/jobs/types.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/jobs/upload_job.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/mediainfo/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/mediainfo/abstract.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/mediainfo/audio.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/mediainfo/silencer.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/mediainfo/video.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scheduler/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scheduler/instagram_human.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scheduler/scheduler.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/abstract.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/exceptions.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/fail_handler.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/instagram/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/instagram/captcha.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/link_resolver.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/youtube/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/youtube/abstract.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/youtube/music.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/youtube/shorts.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/scraper/youtube/youtube.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/storage/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/storage/mongo.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/bot.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/caption_shortener.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/handlers.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/placeholder_message.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/telegram/utils.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/uploader/__init__.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/warp_beacon.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon/yt_auth.py +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon.egg-info/dependency_links.txt +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon.egg-info/entry_points.txt +0 -0
- {warp_beacon-2.6.31 → warp_beacon-2.6.33}/warp_beacon.egg-info/requires.txt +0 -0
@@ -3,10 +3,12 @@ import os
|
|
3
3
|
import time
|
4
4
|
from typing import Optional
|
5
5
|
import multiprocessing
|
6
|
+
from multiprocessing.managers import Namespace
|
6
7
|
from queue import Empty
|
7
8
|
|
8
9
|
import logging
|
9
10
|
|
11
|
+
from warp_beacon.scraper.utils import Utils
|
10
12
|
from warp_beacon.scraper.exceptions import NotFound, UnknownError, TimeOut, Unavailable, FileTooBig, YoutubeLiveError, \
|
11
13
|
YotubeAgeRestrictedError, IGRateLimitOccurred, CaptchaIssue, AllAccountsFailed, BadProxy
|
12
14
|
from warp_beacon.mediainfo.video import VideoInfo
|
@@ -39,12 +41,15 @@ class AsyncDownloader(object):
|
|
39
41
|
acc_selector = None
|
40
42
|
scheduler = None
|
41
43
|
scrolling_now = None
|
44
|
+
process_context = None
|
42
45
|
|
43
46
|
def __init__(self, uploader: AsyncUploader, workers_count: int) -> None:
|
44
47
|
self.workers = []
|
45
48
|
self.job_queue = multiprocessing.Queue()
|
46
49
|
self.auth_event = multiprocessing.Event()
|
47
50
|
self.manager = multiprocessing.Manager()
|
51
|
+
self.process_context = self.manager.Namespace()
|
52
|
+
self.process_context.ig_session_client_id = Utils.get_ig_session_id()
|
48
53
|
self.allow_loop = self.manager.Value('i', 1)
|
49
54
|
self.scrolling_now = self.manager.Value('i', 0)
|
50
55
|
self.acc_selector = AccountSelector(self.manager, ACC_FILE, PROXY_FILE)
|
@@ -58,7 +63,7 @@ class AsyncDownloader(object):
|
|
58
63
|
|
59
64
|
def start(self) -> None:
|
60
65
|
for _ in range(self.workers_count):
|
61
|
-
proc = multiprocessing.Process(target=self.do_work, args=(self.acc_selector,))
|
66
|
+
proc = multiprocessing.Process(target=self.do_work, args=(self.acc_selector, self.process_context))
|
62
67
|
self.workers.append(proc)
|
63
68
|
proc.start()
|
64
69
|
|
@@ -95,7 +100,7 @@ class AsyncDownloader(object):
|
|
95
100
|
job.account_switches += 1
|
96
101
|
selector.reset_ig_request_count()
|
97
102
|
|
98
|
-
def do_work(self, selector: AccountSelector) -> None:
|
103
|
+
def do_work(self, selector: AccountSelector, context: Namespace) -> None:
|
99
104
|
logging.info("download worker started")
|
100
105
|
# pymongo is not fork-safe so new connect to DB required
|
101
106
|
fail_handler = FailHandler(DBClient())
|
@@ -136,7 +141,8 @@ class AsyncDownloader(object):
|
|
136
141
|
proxy = selector.get_current_proxy()
|
137
142
|
if job.job_origin is Origin.INSTAGRAM:
|
138
143
|
from warp_beacon.scraper.instagram.instagram import InstagramScraper
|
139
|
-
|
144
|
+
Utils.maybe_rotate_ig_client_session(context)
|
145
|
+
actor = InstagramScraper(client_session_id=context.ig_session_client_id, account=selector.get_current(), proxy=proxy)
|
140
146
|
selector.inc_ig_request_count()
|
141
147
|
if not job.scroll_content and selector.get_ig_request_count() >= int(os.environ.get("IG_REQUESTS_PER_ACCOUNT", default="10")):
|
142
148
|
logging.info("The account request limit has been reached. Selecting the next account.")
|
@@ -429,6 +435,7 @@ class AsyncDownloader(object):
|
|
429
435
|
def stop_all(self) -> None:
|
430
436
|
self.allow_loop.value = 0
|
431
437
|
self.acc_selector.save_ig_request_count()
|
438
|
+
Utils.save_ig_session_id(self.process_context.ig_session_client_id)
|
432
439
|
for proc in self.workers:
|
433
440
|
if proc.is_alive():
|
434
441
|
logging.info("stopping process #%d", proc.pid)
|
@@ -161,7 +161,11 @@ class AccountSelector(object):
|
|
161
161
|
module_name = self.get_module_name(module_origin)
|
162
162
|
self.current_module_name = module_name
|
163
163
|
if self.current is None:
|
164
|
-
|
164
|
+
idx = self.account_index[self.current_module_name].value
|
165
|
+
self.current = self.accounts[self.current_module_name][idx]
|
166
|
+
if not self.current.get("enabled", True):
|
167
|
+
logging.info("Account '%d' is disabled. Probing next ...", idx)
|
168
|
+
self.next()
|
165
169
|
self.current_proxy = self.get_random_account_proxy()
|
166
170
|
|
167
171
|
def next(self) -> dict:
|
@@ -171,6 +175,9 @@ class AccountSelector(object):
|
|
171
175
|
idx = 0
|
172
176
|
self.account_index[self.current_module_name].value = idx
|
173
177
|
self.current = self.accounts[self.current_module_name][idx]
|
178
|
+
if not self.current.get("enabled", True):
|
179
|
+
logging.info("Account '%d' is disabled. Probing next ...", idx)
|
180
|
+
return self.next()
|
174
181
|
logging.info("Selected account index is '%d'", idx)
|
175
182
|
return self.current
|
176
183
|
|
@@ -36,8 +36,10 @@ class InstagramScraper(ScraperAbstract):
|
|
36
36
|
cl = None
|
37
37
|
inst_session_file = ""
|
38
38
|
timeline_cursor = {}
|
39
|
+
client_session_id = ""
|
39
40
|
|
40
|
-
def __init__(self, account: tuple, proxy: dict=None) -> None:
|
41
|
+
def __init__(self, client_session_id: str, account: tuple, proxy: dict=None) -> None:
|
42
|
+
self.client_session_id = client_session_id
|
41
43
|
super().__init__(account, proxy)
|
42
44
|
#
|
43
45
|
self.inst_session_file = INST_SESSION_FILE_TPL % self.account_index
|
@@ -53,6 +55,8 @@ class InstagramScraper(ScraperAbstract):
|
|
53
55
|
self.cl.change_password_handler = self.change_password_handler
|
54
56
|
|
55
57
|
def setup_device(self) -> None:
|
58
|
+
if not self.client_session_id:
|
59
|
+
self.client_session_id = self.cl.generate_uuid()
|
56
60
|
details = self.account.get("auth_details", {})
|
57
61
|
self.cl.delay_range = details.get("delay_range", [1, 3])
|
58
62
|
self.cl.set_country_code(details.get("country_code", 1))
|
@@ -76,7 +80,7 @@ class InstagramScraper(ScraperAbstract):
|
|
76
80
|
self.cl.set_uuids({
|
77
81
|
"phone_id": uuids.get("phone_id", self.cl.generate_uuid()),
|
78
82
|
"uuid": uuids.get("uuid", self.cl.generate_uuid()),
|
79
|
-
"client_session_id":
|
83
|
+
"client_session_id": self.client_session_id,
|
80
84
|
"advertising_id": uuids.get("advertising_id", self.cl.generate_uuid()),
|
81
85
|
"device_id": uuids.get("device_id", self.cl.generate_uuid())
|
82
86
|
})
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import os
|
2
|
+
from multiprocessing.managers import Namespace
|
3
|
+
import random
|
4
|
+
import uuid
|
5
|
+
import logging
|
6
|
+
|
7
|
+
class Utils(object):
|
8
|
+
session_dir = "/var/warp_beacon"
|
9
|
+
|
10
|
+
@staticmethod
|
11
|
+
def get_ig_session_id() -> str:
|
12
|
+
ig_session_client_id = ""
|
13
|
+
try:
|
14
|
+
sess_file = f"{Utils.session_dir}/ig_session_client_id"
|
15
|
+
if os.path.exists(sess_file):
|
16
|
+
with open(sess_file, 'r', encoding="utf-8") as f:
|
17
|
+
ig_session_client_id = f.read().strip()
|
18
|
+
except Exception as e:
|
19
|
+
logging.warning("Failed to read session ig_session_client_id!")
|
20
|
+
logging.exception(e)
|
21
|
+
|
22
|
+
if not ig_session_client_id:
|
23
|
+
ig_session_client_id = str(uuid.uuid4())
|
24
|
+
|
25
|
+
return ig_session_client_id
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def save_ig_session_id(ig_session_client_id: str) -> None:
|
29
|
+
try:
|
30
|
+
with open(f"{Utils.session_dir}/ig_session_client_id", "w+", encoding="utf-8") as f:
|
31
|
+
f.write(ig_session_client_id)
|
32
|
+
except Exception as e:
|
33
|
+
logging.warning("Failed to save session ig_session_client_id!")
|
34
|
+
logging.exception(e)
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def maybe_rotate_ig_client_session(context: Namespace) -> None:
|
38
|
+
if random.random() > 0.95:
|
39
|
+
context.ig_session_client_id = str(uuid.uuid4())
|
40
|
+
logging.info("Rotated client_session_id — simulating app restart")
|
@@ -41,6 +41,7 @@ warp_beacon/scraper/account_selector.py
|
|
41
41
|
warp_beacon/scraper/exceptions.py
|
42
42
|
warp_beacon/scraper/fail_handler.py
|
43
43
|
warp_beacon/scraper/link_resolver.py
|
44
|
+
warp_beacon/scraper/utils.py
|
44
45
|
warp_beacon/scraper/instagram/__init__.py
|
45
46
|
warp_beacon/scraper/instagram/captcha.py
|
46
47
|
warp_beacon/scraper/instagram/instagram.py
|
@@ -23,6 +23,7 @@ warp_beacon/scraper/instagram
|
|
23
23
|
warp_beacon/scraper/instagram/instagram
|
24
24
|
warp_beacon/scraper/link_resolver
|
25
25
|
warp_beacon/scraper/types
|
26
|
+
warp_beacon/scraper/utils
|
26
27
|
warp_beacon/scraper/youtube
|
27
28
|
warp_beacon/scraper/youtube/abstract
|
28
29
|
warp_beacon/scraper/youtube/music
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|