warp-beacon 2.3.51__tar.gz → 2.4.1__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.3.51/warp_beacon.egg-info → warp_beacon-2.4.1}/PKG-INFO +1 -1
- warp_beacon-2.4.1/warp_beacon/__version__.py +2 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/__init__.py +5 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/account_selector.py +12 -1
- warp_beacon-2.4.1/warp_beacon/yt_auth.py +197 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1/warp_beacon.egg-info}/PKG-INFO +1 -1
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon.egg-info/SOURCES.txt +1 -0
- warp_beacon-2.3.51/warp_beacon/__version__.py +0 -2
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/LICENSE +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/MANIFEST.in +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/README.md +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/assets/placeholder.gif +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/etc/.gitignore +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/etc/accounts.json +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/etc/proxies.json +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/etc/warp_beacon.conf +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/etc/warp_beacon.service +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/pyproject.toml +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/setup.cfg +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/setup.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/compress/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/compress/video.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/jobs/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/jobs/abstract.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/jobs/download_job.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/jobs/types.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/jobs/upload_job.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/mediainfo/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/mediainfo/abstract.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/mediainfo/audio.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/mediainfo/silencer.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/mediainfo/video.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scheduler/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scheduler/instagram_human.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scheduler/scheduler.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/abstract.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/exceptions.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/fail_handler.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/instagram/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/instagram/instagram.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/link_resolver.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/youtube/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/youtube/abstract.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/youtube/music.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/youtube/shorts.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/scraper/youtube/youtube.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/storage/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/storage/mongo.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/bot.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/caption_shortener.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/handlers.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/placeholder_message.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/telegram/utils.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/uploader/__init__.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon/warp_beacon.py +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon.egg-info/dependency_links.txt +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon.egg-info/entry_points.txt +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon.egg-info/requires.txt +0 -0
- {warp_beacon-2.3.51 → warp_beacon-2.4.1}/warp_beacon.egg-info/top_level.txt +0 -0
@@ -121,6 +121,11 @@ class AsyncDownloader(object):
|
|
121
121
|
if job.job_origin is Origin.INSTAGRAM:
|
122
122
|
from warp_beacon.scraper.instagram.instagram import InstagramScraper
|
123
123
|
actor = InstagramScraper(selector.get_current(), proxy)
|
124
|
+
selector.inc_ig_request_count()
|
125
|
+
if selector.get_ig_request_count() >= int(os.environ.get("IG_REQUESTS_PER_ACCOUNT", default="20")):
|
126
|
+
logging.info("The account request limit has been reached. Selecting the next account.")
|
127
|
+
selector.reset_ig_request_count()
|
128
|
+
selector.next()
|
124
129
|
elif job.job_origin is Origin.YT_SHORTS:
|
125
130
|
from warp_beacon.scraper.youtube.shorts import YoutubeShortsScraper
|
126
131
|
actor = YoutubeShortsScraper(selector.get_current(), proxy)
|
@@ -23,6 +23,7 @@ class AccountSelector(object):
|
|
23
23
|
manager = None
|
24
24
|
account_index = {}
|
25
25
|
current_proxy = None
|
26
|
+
ig_request_count = None
|
26
27
|
|
27
28
|
def __init__(self, manager: multiprocessing.managers.SyncManager, acc_file_path: str, proxy_file_path: str=None) -> None:
|
28
29
|
self.manager = manager
|
@@ -31,6 +32,7 @@ class AccountSelector(object):
|
|
31
32
|
with open(acc_file_path, 'r', encoding="utf-8") as f:
|
32
33
|
self.accounts = json.loads(f.read())
|
33
34
|
if self.accounts:
|
35
|
+
self.ig_request_count = self.manager.Value('i', 0)
|
34
36
|
self.__init_meta_data()
|
35
37
|
#self.load_yt_sessions()
|
36
38
|
for acc_type, _ in self.accounts.items():
|
@@ -170,4 +172,13 @@ class AccountSelector(object):
|
|
170
172
|
module_name = 'youtube' if next((s for s in ("yt", "youtube", "youtu_be") if s in mod_name.value), None) else 'instagram'
|
171
173
|
if module_name not in self.accounts:
|
172
174
|
return 0
|
173
|
-
return len(self.accounts[module_name])
|
175
|
+
return len(self.accounts[module_name])
|
176
|
+
|
177
|
+
def inc_ig_request_count(self) -> None:
|
178
|
+
self.ig_request_count.value += 1
|
179
|
+
|
180
|
+
def reset_ig_request_count(self) -> None:
|
181
|
+
self.ig_request_count.value = 0
|
182
|
+
|
183
|
+
def get_ig_request_count(self) -> int:
|
184
|
+
return self.ig_request_count.value
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
|
4
|
+
import logging
|
5
|
+
|
6
|
+
import json
|
7
|
+
import requests
|
8
|
+
|
9
|
+
class YtAuth(object):
|
10
|
+
TV_CLIENT_ID = "861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com"
|
11
|
+
TV_CLIENT_SECRET = "SboVhoG9s0rNafixCSGGKXAT"
|
12
|
+
YT_SESSION_FILE_TPL = '/var/warp_beacon/yt_session_%d.json'
|
13
|
+
|
14
|
+
process_start_time = 0
|
15
|
+
account_index = 0
|
16
|
+
yt_session_file = ""
|
17
|
+
|
18
|
+
def __init__(self, account_index: int) -> None:
|
19
|
+
self.account_index = account_index
|
20
|
+
self.yt_session_file = self.YT_SESSION_FILE_TPL % account_index
|
21
|
+
|
22
|
+
def fetch_token(self) -> dict:
|
23
|
+
result = {"user_code": "", "device_code": "", "verification_url": ""}
|
24
|
+
http_code = 0
|
25
|
+
response_text = ''
|
26
|
+
try:
|
27
|
+
logging.info("Fetching YT token ...")
|
28
|
+
self.process_start_time = 0
|
29
|
+
# Subtracting 30 seconds is arbitrary to avoid potential time discrepencies
|
30
|
+
self.process_start_time = int(time.time() - 30)
|
31
|
+
data = {
|
32
|
+
'client_id': self.TV_CLIENT_ID,
|
33
|
+
'scope': 'https://www.googleapis.com/auth/youtube'
|
34
|
+
}
|
35
|
+
response = requests.post(
|
36
|
+
url='https://oauth2.googleapis.com/device/code',
|
37
|
+
headers={
|
38
|
+
"User-Agent": "Mozilla/5.0",
|
39
|
+
"accept-language": "en-US,en",
|
40
|
+
"Content-Type": "application/json"
|
41
|
+
},
|
42
|
+
json=data,
|
43
|
+
timeout=int(os.environ.get("YT_TIMEOUT", "30"))
|
44
|
+
)
|
45
|
+
|
46
|
+
http_code = response.status_code
|
47
|
+
response_text = response.text
|
48
|
+
|
49
|
+
if http_code != 200:
|
50
|
+
logging.error("Invalid YT HTTP code: '%d'", http_code)
|
51
|
+
logging.info("Request dump: '%s'", str(response.__dict__))
|
52
|
+
else:
|
53
|
+
response_data = response.json()
|
54
|
+
result["verification_url"] = response_data['verification_url']
|
55
|
+
result["user_code"] = response_data['user_code']
|
56
|
+
result["device_code"] = response_data['device_code']
|
57
|
+
logging.info("Fetched YT url '%s' and input code '%s'", result["verification_url"], result['user_code'])
|
58
|
+
except Exception as e:
|
59
|
+
logging.error("Youtube authorization failed!")
|
60
|
+
logging.exception(e)
|
61
|
+
|
62
|
+
if http_code != 200:
|
63
|
+
raise ValueError(f"Youtube HTTP response code is {http_code}: {response_text}")
|
64
|
+
|
65
|
+
return result
|
66
|
+
|
67
|
+
def confirm_token(self, device_code: str) -> dict:
|
68
|
+
response_data = {}
|
69
|
+
http_code = 0
|
70
|
+
response_text = ''
|
71
|
+
try:
|
72
|
+
logging.info("Confirming YT auth token ...")
|
73
|
+
self.process_start_time = int(time.time()) - self.process_start_time - 20
|
74
|
+
data = {
|
75
|
+
'client_id': self.TV_CLIENT_ID,
|
76
|
+
'client_secret': self.TV_CLIENT_SECRET,
|
77
|
+
'device_code': device_code,
|
78
|
+
'grant_type': 'urn:ietf:params:oauth:grant-type:device_code'
|
79
|
+
}
|
80
|
+
response = requests.post(
|
81
|
+
url='https://oauth2.googleapis.com/token',
|
82
|
+
headers={
|
83
|
+
"User-Agent": "Mozilla/5.0",
|
84
|
+
"accept-language": "en-US,en",
|
85
|
+
"Content-Type": "application/json"
|
86
|
+
},
|
87
|
+
json=data,
|
88
|
+
timeout=int(os.environ.get("YT_TIMEOUT", "30"))
|
89
|
+
)
|
90
|
+
|
91
|
+
http_code = response.status_code
|
92
|
+
response_text = response.text
|
93
|
+
|
94
|
+
if http_code != 200:
|
95
|
+
logging.error("Invalid YT HTTP code: '%d'", http_code)
|
96
|
+
logging.info("Request dump: '%s'", str(response.__dict__))
|
97
|
+
else:
|
98
|
+
response_data = response.json()
|
99
|
+
response_data["expires"] = self.process_start_time + int(response_data["expires_in"])
|
100
|
+
except Exception as e:
|
101
|
+
logging.error("Failed to confirm token!")
|
102
|
+
logging.exception(e)
|
103
|
+
|
104
|
+
if http_code != 200:
|
105
|
+
raise ValueError(f"Youtube HTTP response code is {http_code}: {response_text}")
|
106
|
+
|
107
|
+
return response_data
|
108
|
+
|
109
|
+
def refresh_token(self, refresh_token: str) -> dict:
|
110
|
+
response_data = {}
|
111
|
+
http_code = 0
|
112
|
+
response_text = ''
|
113
|
+
try:
|
114
|
+
logging.info("Refreshing YT token ...")
|
115
|
+
start_time = int(time.time() - 30)
|
116
|
+
data = {
|
117
|
+
'client_id': self.TV_CLIENT_ID,
|
118
|
+
'client_secret': self.TV_CLIENT_SECRET,
|
119
|
+
'grant_type': 'refresh_token',
|
120
|
+
'refresh_token': refresh_token
|
121
|
+
}
|
122
|
+
response = requests.post(
|
123
|
+
url='https://oauth2.googleapis.com/token',
|
124
|
+
headers={
|
125
|
+
"User-Agent": "Mozilla/5.0",
|
126
|
+
"accept-language": "en-US,en",
|
127
|
+
"Content-Type": "application/json"
|
128
|
+
},
|
129
|
+
json=data,
|
130
|
+
timeout=int(os.environ.get("YT_TIMEOUT", "30"))
|
131
|
+
)
|
132
|
+
|
133
|
+
http_code = response.status_code
|
134
|
+
response_text = response.text
|
135
|
+
|
136
|
+
if http_code != 200:
|
137
|
+
logging.error("Invalid YT HTTP code: '%d'", http_code)
|
138
|
+
logging.info("Request dump: '%s'", str(response.__dict__))
|
139
|
+
else:
|
140
|
+
response_data = response.json()
|
141
|
+
response_data["expires"] = start_time + int(response_data["expires_in"])
|
142
|
+
except Exception as e:
|
143
|
+
logging.error("Failed to refresh YT token")
|
144
|
+
logging.exception(e)
|
145
|
+
|
146
|
+
if http_code != 200:
|
147
|
+
raise ValueError(f"Youtube HTTP response code is {http_code}: {response_text}")
|
148
|
+
|
149
|
+
return response_data
|
150
|
+
|
151
|
+
def safe_write_session(self, token_data: dict) -> bool:
|
152
|
+
try:
|
153
|
+
tmp_filename = f"{self.yt_session_file}~"
|
154
|
+
|
155
|
+
if os.path.exists(tmp_filename):
|
156
|
+
os.unlink(tmp_filename)
|
157
|
+
|
158
|
+
with open(tmp_filename, "w+", encoding="utf-8") as f:
|
159
|
+
f.write(json.dumps(token_data))
|
160
|
+
|
161
|
+
if os.path.exists(tmp_filename):
|
162
|
+
if os.path.exists(self.yt_session_file):
|
163
|
+
os.unlink(self.yt_session_file)
|
164
|
+
os.rename(src=tmp_filename, dst=self.yt_session_file)
|
165
|
+
return True
|
166
|
+
except Exception as e:
|
167
|
+
logging.error("Failed to write token!")
|
168
|
+
logging.exception(e)
|
169
|
+
|
170
|
+
return False
|
171
|
+
|
172
|
+
def store_device_code(self, device_code: str) -> bool:
|
173
|
+
try:
|
174
|
+
device_code_file = f"/tmp/yt_device_code_acc_{self.account_index}"
|
175
|
+
logging.info("Storing device code in file '%s'", device_code_file)
|
176
|
+
with open(device_code_file, "w+", encoding="utf-8") as f:
|
177
|
+
f.write(device_code.strip())
|
178
|
+
except Exception as e:
|
179
|
+
logging.error("Failed to store device code!")
|
180
|
+
logging.exception(e)
|
181
|
+
return False
|
182
|
+
|
183
|
+
return True
|
184
|
+
|
185
|
+
def load_device_code(self) -> str:
|
186
|
+
device_code = ''
|
187
|
+
try:
|
188
|
+
device_code_file = f"/tmp/yt_device_code_acc_{self.account_index}"
|
189
|
+
logging.info("Loading device code from file '%s'", device_code_file)
|
190
|
+
with open(device_code_file, 'r', encoding="utf-8") as f:
|
191
|
+
device_code = f.read().strip()
|
192
|
+
os.unlink(device_code_file)
|
193
|
+
except Exception as e:
|
194
|
+
logging.error("Failed to load device code for account #%d", self.account_index)
|
195
|
+
logging.exception(e)
|
196
|
+
|
197
|
+
return device_code
|
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
|
File without changes
|