eaf-base-api 2.2.6__tar.gz → 2.2.8__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.
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/PKG-INFO +3 -2
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/base_api/base.py +147 -64
- eaf_base_api-2.2.8/base_api/modules/config.py +36 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/eaf_base_api.egg-info/PKG-INFO +3 -2
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/eaf_base_api.egg-info/SOURCES.txt +1 -1
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/setup.py +1 -1
- eaf_base_api-2.2.6/base_api/modules/consts.py +0 -16
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/LICENSE +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/README.md +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/base_api/__init__.py +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/base_api/modules/__init__.py +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/base_api/modules/progress_bars.py +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/eaf_base_api.egg-info/dependency_links.txt +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/eaf_base_api.egg-info/requires.txt +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/eaf_base_api.egg-info/top_level.txt +0 -0
- {eaf_base_api-2.2.6 → eaf_base_api-2.2.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: eaf_base_api
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.8
|
|
4
4
|
Summary: A base API for EchterAlsFake's Porn APIs
|
|
5
5
|
Home-page: https://github.com/EchterAlsFake/eaf_base_api
|
|
6
6
|
Author: Johannes Habel
|
|
@@ -20,6 +20,7 @@ Dynamic: description
|
|
|
20
20
|
Dynamic: description-content-type
|
|
21
21
|
Dynamic: home-page
|
|
22
22
|
Dynamic: license
|
|
23
|
+
Dynamic: license-file
|
|
23
24
|
Dynamic: requires-dist
|
|
24
25
|
Dynamic: summary
|
|
25
26
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import os
|
|
2
3
|
import sys
|
|
3
4
|
import time
|
|
4
5
|
import m3u8
|
|
6
|
+
import json
|
|
5
7
|
import httpx
|
|
6
|
-
import random
|
|
7
8
|
import logging
|
|
8
9
|
import traceback
|
|
9
10
|
import threading
|
|
11
|
+
import http.client
|
|
10
12
|
|
|
11
13
|
from typing import Union
|
|
12
14
|
from functools import lru_cache
|
|
@@ -15,11 +17,11 @@ from ffmpeg_progress_yield import FfmpegProgress
|
|
|
15
17
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
16
18
|
|
|
17
19
|
try:
|
|
18
|
-
from modules import
|
|
20
|
+
from modules import config
|
|
19
21
|
from modules.progress_bars import Callback
|
|
20
22
|
|
|
21
23
|
except (ModuleNotFoundError, ImportError):
|
|
22
|
-
from .modules import
|
|
24
|
+
from .modules import config
|
|
23
25
|
from .modules.progress_bars import Callback
|
|
24
26
|
|
|
25
27
|
loggers = {}
|
|
@@ -56,19 +58,52 @@ class ColoredFormatter(logging.Formatter):
|
|
|
56
58
|
return f"{color}{log_message}{RESET_COLOR}"
|
|
57
59
|
|
|
58
60
|
|
|
59
|
-
def
|
|
61
|
+
def send_log_message(ip, port, message):
|
|
62
|
+
"""Sends a log message to a remote server via HTTP."""
|
|
63
|
+
try:
|
|
64
|
+
conn = http.client.HTTPConnection(ip, port, timeout=2)
|
|
65
|
+
headers = {'Content-Type': 'application/json'}
|
|
66
|
+
data = json.dumps({"message": message})
|
|
67
|
+
conn.request("POST", "/log", body=data, headers=headers)
|
|
68
|
+
conn.getresponse() # Read response to ensure completion
|
|
69
|
+
conn.close()
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(f"Failed to send log to {ip}:{port} - {e}", file=sys.stderr)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class HTTPLogHandler(logging.Handler):
|
|
75
|
+
"""Custom log handler that sends logs to a remote HTTP server."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, ip, port):
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.ip = ip
|
|
80
|
+
self.port = port
|
|
81
|
+
|
|
82
|
+
def emit(self, record):
|
|
83
|
+
log_entry = self.format(record)
|
|
84
|
+
send_log_message(self.ip, self.port, log_entry)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def setup_logger(name, log_file=None, level=logging.CRITICAL, http_ip=None, http_port=None):
|
|
60
88
|
"""Creates or updates a logger for a specific module."""
|
|
61
89
|
if name in loggers:
|
|
62
90
|
logger = loggers[name]
|
|
63
91
|
logger.setLevel(level)
|
|
64
92
|
|
|
65
93
|
file_handler_exists = any(isinstance(h, logging.FileHandler) for h in logger.handlers)
|
|
94
|
+
http_handler_exists = any(isinstance(h, HTTPLogHandler) for h in logger.handlers)
|
|
95
|
+
|
|
66
96
|
if log_file and not file_handler_exists:
|
|
67
97
|
log_file = get_log_file_path(log_file)
|
|
68
98
|
fh = logging.FileHandler(log_file, mode='a')
|
|
69
99
|
fh.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
70
100
|
logger.addHandler(fh)
|
|
71
101
|
|
|
102
|
+
if http_ip and http_port and not http_handler_exists:
|
|
103
|
+
http_handler = HTTPLogHandler(http_ip, http_port)
|
|
104
|
+
http_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
105
|
+
logger.addHandler(http_handler)
|
|
106
|
+
|
|
72
107
|
return logger
|
|
73
108
|
|
|
74
109
|
logger = logging.getLogger(name)
|
|
@@ -76,10 +111,9 @@ def setup_logger(name, log_file=None, level=logging.CRITICAL):
|
|
|
76
111
|
|
|
77
112
|
if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
|
|
78
113
|
ch = logging.StreamHandler()
|
|
79
|
-
ch.setFormatter(
|
|
114
|
+
ch.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
80
115
|
logger.addHandler(ch)
|
|
81
116
|
|
|
82
|
-
# Ensure the log file is overwritten only on the first program start
|
|
83
117
|
if log_file:
|
|
84
118
|
log_file = get_log_file_path(log_file)
|
|
85
119
|
if not hasattr(sys, '_first_run'):
|
|
@@ -87,11 +121,15 @@ def setup_logger(name, log_file=None, level=logging.CRITICAL):
|
|
|
87
121
|
file_mode = 'w'
|
|
88
122
|
else:
|
|
89
123
|
file_mode = 'a'
|
|
90
|
-
|
|
91
124
|
fh = logging.FileHandler(log_file, mode=file_mode)
|
|
92
125
|
fh.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
93
126
|
logger.addHandler(fh)
|
|
94
127
|
|
|
128
|
+
if http_ip and http_port:
|
|
129
|
+
http_handler = HTTPLogHandler(http_ip, http_port)
|
|
130
|
+
http_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
131
|
+
logger.addHandler(http_handler)
|
|
132
|
+
|
|
95
133
|
loggers[name] = logger
|
|
96
134
|
return logger
|
|
97
135
|
|
|
@@ -100,10 +138,11 @@ class Cache:
|
|
|
100
138
|
Caches content from network requests
|
|
101
139
|
"""
|
|
102
140
|
|
|
103
|
-
def __init__(self):
|
|
141
|
+
def __init__(self, config):
|
|
104
142
|
self.cache_dictionary = {}
|
|
105
143
|
self.lock = threading.Lock()
|
|
106
144
|
self.logger = setup_logger("BASE API - [Cache]", level=logging.CRITICAL)
|
|
145
|
+
self.config = config
|
|
107
146
|
|
|
108
147
|
def enable_logging(self, log_file=None, level=logging.DEBUG):
|
|
109
148
|
"""
|
|
@@ -121,7 +160,7 @@ class Cache:
|
|
|
121
160
|
|
|
122
161
|
def save_cache(self, url, content):
|
|
123
162
|
with self.lock:
|
|
124
|
-
if len(self.cache_dictionary.keys()) >=
|
|
163
|
+
if len(self.cache_dictionary.keys()) >= self.config.max_cache_items:
|
|
125
164
|
first_key = next(iter(self.cache_dictionary))
|
|
126
165
|
# Delete the first item
|
|
127
166
|
del self.cache_dictionary[first_key]
|
|
@@ -133,11 +172,13 @@ class BaseCore:
|
|
|
133
172
|
"""
|
|
134
173
|
The base class which has all necessary functions for other API packages
|
|
135
174
|
"""
|
|
136
|
-
def __init__(self):
|
|
175
|
+
def __init__(self, config=config.config):
|
|
137
176
|
self.last_request_time = time.time()
|
|
138
177
|
self.total_requests = 0 # Tracks how many requests have been made
|
|
139
178
|
self.session = None
|
|
140
|
-
self.
|
|
179
|
+
self.kill_switch = False
|
|
180
|
+
self.config = config
|
|
181
|
+
self.cache = Cache(self.config)
|
|
141
182
|
self.initialize_session()
|
|
142
183
|
self.logger = setup_logger("BASE API - [BaseCore]", log_file=False, level=logging.ERROR)
|
|
143
184
|
|
|
@@ -150,23 +191,58 @@ class BaseCore:
|
|
|
150
191
|
|
|
151
192
|
def update_user_agent(self):
|
|
152
193
|
"""Updates the User-Agent"""
|
|
153
|
-
self.
|
|
194
|
+
self.config.rotate_user_agent()
|
|
195
|
+
self.session.headers.update({"User-Agent": self.config.headers["User-Agent"]})
|
|
196
|
+
|
|
197
|
+
def enable_kill_switch(self):
|
|
198
|
+
"""This is a function that will check and verify if your proxy is working before every request.
|
|
199
|
+
As soon as there's a mismatch in the actual response IP and the proxy IP, the program will exit."""
|
|
200
|
+
self.kill_switch = True
|
|
201
|
+
|
|
202
|
+
def check_kill_switch(self):
|
|
203
|
+
proxy_ip = self.config.proxy
|
|
204
|
+
pattern = re.compile(
|
|
205
|
+
r'^(?P<scheme>http|socks5)://'
|
|
206
|
+
r'(?:(?:\w+:\w+)@)?' # optional user:pass@
|
|
207
|
+
r'(?P<host>[a-zA-Z0-9.-]+)'
|
|
208
|
+
r':(?P<port>\d{1,5})$'
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
match = pattern.match(proxy_ip
|
|
212
|
+
)
|
|
213
|
+
if match:
|
|
214
|
+
self.logger.info(f"Proxy has valid scheme: {match.group('scheme')} ")
|
|
154
215
|
|
|
155
|
-
|
|
156
|
-
|
|
216
|
+
else:
|
|
217
|
+
self.logger.critical("Proxy is INVALID! Exiting.")
|
|
157
218
|
|
|
158
|
-
if
|
|
159
|
-
|
|
219
|
+
self.logger.info("Checking if proxy is working...")
|
|
220
|
+
self.logger.info("Doing request to httpbin.org to get your real IP to compare it with another request with activated proxy")
|
|
160
221
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
222
|
+
your_ip = httpx.Client().get("https://httpbin.org/ip").json()["origin"]
|
|
223
|
+
self.logger.info(f"Your IP is: {your_ip}")
|
|
224
|
+
|
|
225
|
+
self.logger.info("Doing request with Proxy on...")
|
|
226
|
+
proxy_ip_ = self.fetch("https://httpbin.org/ip", bypass_kill_switch=True, get_response=True).json()["origin"]
|
|
227
|
+
self.logger.info(f"Proxy IP is: {proxy_ip_}")
|
|
228
|
+
|
|
229
|
+
if your_ip == proxy_ip_:
|
|
230
|
+
self.logger.critical("IP is the same on both requests... Proxy is not working, exiting!")
|
|
231
|
+
raise "CRITICAL PROXY ERROR, CHECK LOGS!"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def initialize_session(self, verify=True): # Disable SSL verification only if you really need it....
|
|
235
|
+
self.session = httpx.Client(
|
|
236
|
+
proxy=self.config.proxy,
|
|
237
|
+
headers=self.config.headers,
|
|
238
|
+
timeout=self.config.timeout,
|
|
239
|
+
follow_redirects=True,
|
|
240
|
+
verify=verify
|
|
241
|
+
)
|
|
166
242
|
|
|
167
243
|
def enforce_delay(self):
|
|
168
244
|
"""Enforces the specified delay in consts.REQUEST_DELAY"""
|
|
169
|
-
delay =
|
|
245
|
+
delay = self.config.request_delay
|
|
170
246
|
if delay > 0:
|
|
171
247
|
time_since_last_request = time.time() - self.last_request_time
|
|
172
248
|
self.logger.debug(f"Time since last request: {time_since_last_request:.2f} seconds.")
|
|
@@ -179,78 +255,86 @@ class BaseCore:
|
|
|
179
255
|
self,
|
|
180
256
|
url: str,
|
|
181
257
|
get_bytes: bool = False,
|
|
182
|
-
timeout: int =
|
|
258
|
+
timeout: int = None,
|
|
183
259
|
get_response: bool = False,
|
|
184
260
|
save_cache: bool = True,
|
|
185
261
|
cookies: dict = None,
|
|
186
262
|
allow_redirects: bool = True,
|
|
263
|
+
bypass_kill_switch: bool = False # prevents infinite loop
|
|
187
264
|
) -> Union[bytes, str, httpx.Response, None]:
|
|
188
265
|
"""
|
|
189
266
|
Fetches content in UTF-8 Text, Bytes, or as a stream using multiple request attempts,
|
|
190
267
|
support for proxies and custom timeout.
|
|
191
268
|
"""
|
|
192
269
|
# Check cache first
|
|
270
|
+
|
|
271
|
+
if timeout is None:
|
|
272
|
+
timeout = self.config.timeout # pls don't ask thanks
|
|
273
|
+
|
|
193
274
|
content = self.cache.handle_cache(url)
|
|
194
|
-
if content
|
|
275
|
+
if content:
|
|
195
276
|
self.logger.info(f"Fetched content for: {url} from cache!")
|
|
196
277
|
return content
|
|
197
278
|
|
|
198
|
-
for attempt in range(1,
|
|
279
|
+
for attempt in range(1, self.config.max_retries + 1):
|
|
199
280
|
if attempt != 1:
|
|
200
281
|
time.sleep(1.5) # Sleeping for 1.5 seconds to minimize site overload when doing a lot of requests
|
|
201
282
|
|
|
202
283
|
try:
|
|
203
284
|
# Update user agent periodically
|
|
204
|
-
if self.total_requests %
|
|
285
|
+
if self.total_requests % 30 == 0: # Changing all 30 attempts
|
|
205
286
|
self.update_user_agent()
|
|
206
287
|
|
|
207
288
|
self.enforce_delay()
|
|
208
289
|
|
|
290
|
+
if self.kill_switch and not bypass_kill_switch:
|
|
291
|
+
self.check_kill_switch()
|
|
292
|
+
|
|
209
293
|
# Perform the request with stream handling
|
|
210
|
-
|
|
211
|
-
follow_redirects=allow_redirects)
|
|
212
|
-
|
|
294
|
+
response = self.session.get(url, timeout=timeout, cookies=cookies,
|
|
295
|
+
follow_redirects=allow_redirects)
|
|
296
|
+
self.total_requests += 1
|
|
213
297
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
298
|
+
# Log and handle non-200 status codes
|
|
299
|
+
if response.status_code != 200:
|
|
300
|
+
self.logger.warning(
|
|
301
|
+
f"Attempt {attempt}: Unexpected status code {response.status_code} for URL: {url}")
|
|
218
302
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
303
|
+
if response.status_code == 404:
|
|
304
|
+
self.logger.error("Resource not found (404). This may indicate the content is unavailable.")
|
|
305
|
+
return None # Return None for unavailable resources
|
|
222
306
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
307
|
+
elif response.status_code == 403 and attempt >= 2:
|
|
308
|
+
self.logger.error(f"The website rejected access after {attempt} tries. Aborting!")
|
|
309
|
+
return None # Return None for forbidden resources
|
|
226
310
|
|
|
227
|
-
|
|
311
|
+
continue # Retry for other non-200 status codes
|
|
228
312
|
|
|
229
|
-
|
|
313
|
+
self.logger.debug(f"Attempt {attempt}: Successfully fetched URL: {url}")
|
|
230
314
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
315
|
+
# Return response if requested
|
|
316
|
+
if get_response:
|
|
317
|
+
return response
|
|
234
318
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
319
|
+
# Process and return content
|
|
320
|
+
# Collecting all chunks before processing because some sites cause issues with real-time decoding
|
|
321
|
+
raw_content = response.content
|
|
238
322
|
|
|
239
|
-
|
|
240
|
-
|
|
323
|
+
if get_bytes:
|
|
324
|
+
content = raw_content
|
|
241
325
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
326
|
+
else:
|
|
327
|
+
try:
|
|
328
|
+
content = raw_content.decode("utf-8")
|
|
245
329
|
|
|
246
|
-
|
|
247
|
-
|
|
330
|
+
except UnicodeDecodeError:
|
|
331
|
+
content = raw_content.decode("latin1") # Fallback, hope that works somehow idk
|
|
248
332
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
333
|
+
if save_cache and not get_bytes:
|
|
334
|
+
self.logger.debug(f"Saving content of {url} to local cache.")
|
|
335
|
+
self.cache.save_cache(url, content)
|
|
252
336
|
|
|
253
|
-
|
|
337
|
+
return content
|
|
254
338
|
|
|
255
339
|
except httpx.RequestError as e:
|
|
256
340
|
self.logger.error(f"Attempt {attempt}: Request error for URL {url}: {e}")
|
|
@@ -258,9 +342,9 @@ class BaseCore:
|
|
|
258
342
|
except Exception:
|
|
259
343
|
self.logger.error(f"Attempt {attempt}: Unexpected error for URL {url}: {traceback.format_exc()}")
|
|
260
344
|
|
|
261
|
-
self.logger.info(f"Retrying ({attempt}/{
|
|
345
|
+
self.logger.info(f"Retrying ({attempt}/{self.config.max_retries}) for URL: {url}")
|
|
262
346
|
|
|
263
|
-
self.logger.error(f"Failed to fetch URL {url} after {
|
|
347
|
+
self.logger.error(f"Failed to fetch URL {url} after {self.config.max_retries} attempts.")
|
|
264
348
|
return None # Return None if all attempts fail
|
|
265
349
|
|
|
266
350
|
@classmethod
|
|
@@ -352,14 +436,13 @@ class BaseCore:
|
|
|
352
436
|
elif downloader == "FFMPEG":
|
|
353
437
|
self.FFMPEG(video=video, quality=quality, path=path, callback=callback)
|
|
354
438
|
|
|
355
|
-
|
|
356
|
-
def download_segment(cls, url: str, timeout: int) -> tuple[str, bytes, bool]:
|
|
439
|
+
def download_segment(self, url: str, timeout: int) -> tuple[str, bytes, bool]:
|
|
357
440
|
"""
|
|
358
441
|
Attempt to download a single segment, retrying on failure.
|
|
359
442
|
Returns a tuple of the URL, content (empty if failed after retries), and a success flag.
|
|
360
443
|
"""
|
|
361
444
|
|
|
362
|
-
content =
|
|
445
|
+
content = self.fetch(url, timeout=timeout, get_bytes=True)
|
|
363
446
|
return url, content, True # Success
|
|
364
447
|
|
|
365
448
|
def threaded(self, max_workers: int = 20, timeout: int = 10):
|
|
@@ -437,7 +520,7 @@ class BaseCore:
|
|
|
437
520
|
|
|
438
521
|
# Build the command for FFMPEG as a list directly
|
|
439
522
|
command = [
|
|
440
|
-
|
|
523
|
+
self.config.FFMPEG_PATH,
|
|
441
524
|
"-i", new_url, # Input URL
|
|
442
525
|
"-bsf:a", "aac_adtstoasc",
|
|
443
526
|
"-y", # Overwrite output files without asking
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
class RuntimeConfig:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.max_cache_items = 200
|
|
6
|
+
self.max_retries = 4
|
|
7
|
+
self.request_delay = 0
|
|
8
|
+
self.timeout = 20
|
|
9
|
+
self.ffmpeg_path = "ffmpeg"
|
|
10
|
+
self.proxy = None
|
|
11
|
+
self.verify_ssl = True
|
|
12
|
+
self.user_agents = [
|
|
13
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
14
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.1",
|
|
15
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.3",
|
|
16
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.3",
|
|
17
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0."
|
|
18
|
+
]
|
|
19
|
+
self.cookies = None
|
|
20
|
+
self.headers = {
|
|
21
|
+
"User-Agent": random.choice(self.user_agents)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def rotate_user_agent(self):
|
|
25
|
+
self.headers["User-Agent"] = random.choice(self.user_agents)
|
|
26
|
+
|
|
27
|
+
def as_httpx_kwargs(self):
|
|
28
|
+
return {
|
|
29
|
+
"proxies": self.proxy,
|
|
30
|
+
"verify": self.verify_ssl,
|
|
31
|
+
"headers": self.headers,
|
|
32
|
+
"timeout": self.timeout
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Singleton instance needed for my Porn Fetch project
|
|
36
|
+
config = RuntimeConfig()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: eaf_base_api
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.8
|
|
4
4
|
Summary: A base API for EchterAlsFake's Porn APIs
|
|
5
5
|
Home-page: https://github.com/EchterAlsFake/eaf_base_api
|
|
6
6
|
Author: Johannes Habel
|
|
@@ -20,6 +20,7 @@ Dynamic: description
|
|
|
20
20
|
Dynamic: description-content-type
|
|
21
21
|
Dynamic: home-page
|
|
22
22
|
Dynamic: license
|
|
23
|
+
Dynamic: license-file
|
|
23
24
|
Dynamic: requires-dist
|
|
24
25
|
Dynamic: summary
|
|
25
26
|
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import random
|
|
2
|
-
|
|
3
|
-
MAX_CACHE_ITEMS = 200 # Lower this value if you are on a low-ram system (<=4 GB)
|
|
4
|
-
MAX_RETRIES = 4
|
|
5
|
-
REQUEST_DELAY = 0
|
|
6
|
-
TIMEOUT = 20 # Maximal request timeout
|
|
7
|
-
FFMPEG_PATH = "ffmpeg"
|
|
8
|
-
PROXY = None
|
|
9
|
-
USER_AGENTS = ["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
10
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.1",
|
|
11
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.3",
|
|
12
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.3",
|
|
13
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0."]
|
|
14
|
-
|
|
15
|
-
HEADERS = {"User-Agent": random.choice(USER_AGENTS)}
|
|
16
|
-
COOKIES = None
|
|
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
|