eaf-base-api 2.4.9__tar.gz → 2.5.0__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.4.9/eaf_base_api.egg-info → eaf_base_api-2.5.0}/PKG-INFO +1 -1
- eaf_base_api-2.5.0/base_api/__init__.py +7 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/base_api/base.py +53 -10
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/base_api/modules/config.py +0 -1
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0/eaf_base_api.egg-info}/PKG-INFO +1 -1
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/eaf_base_api.egg-info/top_level.txt +0 -1
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/pyproject.toml +1 -1
- eaf_base_api-2.4.9/base_api/__init__.py +0 -5
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/LICENSE +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/MANIFEST.in +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/README.md +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/base_api/modules/__init__.py +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/base_api/modules/errors.py +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/base_api/modules/progress_bars.py +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/eaf_base_api.egg-info/SOURCES.txt +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/eaf_base_api.egg-info/dependency_links.txt +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/eaf_base_api.egg-info/requires.txt +0 -0
- {eaf_base_api-2.4.9 → eaf_base_api-2.5.0}/setup.cfg +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
__all__ = ["BaseCore", "Callback", "config", "errors", "setup_logger", "Cache"]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from base_api.modules import errors
|
|
5
|
+
from base_api.modules.config import config
|
|
6
|
+
from base_api.modules.progress_bars import Callback
|
|
7
|
+
from base_api.base import BaseCore, setup_logger, Cache
|
|
@@ -15,10 +15,11 @@ import threading
|
|
|
15
15
|
from itertools import islice
|
|
16
16
|
from collections import deque
|
|
17
17
|
from functools import lru_cache
|
|
18
|
-
from urllib.parse import urljoin
|
|
19
18
|
from email.utils import parsedate_to_datetime
|
|
19
|
+
from datetime import datetime, timezone, timedelta
|
|
20
|
+
from urllib.parse import urljoin, urlparse, parse_qs
|
|
21
|
+
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED
|
|
20
22
|
from typing import Any, Dict, List, Optional, Union, Callable, Tuple, Iterable
|
|
21
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
|
|
22
23
|
|
|
23
24
|
try:
|
|
24
25
|
from modules.errors import *
|
|
@@ -310,6 +311,15 @@ class Cache:
|
|
|
310
311
|
|
|
311
312
|
self.cache_dictionary[url] = content
|
|
312
313
|
|
|
314
|
+
def save_segments_to_cache(self, m3u8_url: str, segments: list):
|
|
315
|
+
with self.lock:
|
|
316
|
+
self.cache_dictionary[m3u8_url] = segments
|
|
317
|
+
|
|
318
|
+
def get_segments_from_cache(self, m3u8_url: str):
|
|
319
|
+
with self.lock:
|
|
320
|
+
segments = self.cache_dictionary.get(m3u8_url, None)
|
|
321
|
+
return segments
|
|
322
|
+
|
|
313
323
|
|
|
314
324
|
class Helper:
|
|
315
325
|
def __init__(
|
|
@@ -723,18 +733,12 @@ class BaseCore:
|
|
|
723
733
|
raise KillSwitch("CRITICAL PROXY ERROR, CHECK LOGS!")
|
|
724
734
|
|
|
725
735
|
def initialize_session(self):
|
|
726
|
-
limits = httpx.Limits(
|
|
727
|
-
max_connections=getattr(self.config, "max_connections", 128),
|
|
728
|
-
max_keepalive_connections=getattr(self.config, "max_keepalive", 4),
|
|
729
|
-
keepalive_expiry=10,
|
|
730
|
-
)
|
|
731
736
|
ctx = ssl.create_default_context(cafile=certifi.where())
|
|
732
737
|
if not self.config.verify_ssl:
|
|
733
738
|
ctx.check_hostname = False
|
|
734
739
|
ctx.verify_mode = ssl.CERT_NONE
|
|
735
740
|
|
|
736
741
|
self.session = httpx.Client(
|
|
737
|
-
limits=limits,
|
|
738
742
|
proxy=self.config.proxy,
|
|
739
743
|
timeout=self.config.timeout, # e.g. 5s on good connections
|
|
740
744
|
http2=self.config.use_http2,
|
|
@@ -929,7 +933,14 @@ class BaseCore:
|
|
|
929
933
|
|
|
930
934
|
# 410: permanent gone
|
|
931
935
|
if status == 410:
|
|
932
|
-
|
|
936
|
+
self.logger.warning(f"""
|
|
937
|
+
The resource: {url} is gone. This can happen because of an expired token. I will try to fix this automatically, by creating
|
|
938
|
+
a new session and getting a fresh m3u8 URL. This fix is very experimental and I don't know if it works. """)
|
|
939
|
+
self.initialize_session()
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
|
|
933
944
|
|
|
934
945
|
# 429: rate limited — respect Retry-After if present, else backoff and retry up to cap
|
|
935
946
|
if status == 429:
|
|
@@ -1119,7 +1130,37 @@ class BaseCore:
|
|
|
1119
1130
|
# Return rank numbers instead of heights if we truly can't infer—kept simple:
|
|
1120
1131
|
return [i for i, _ in enumerate(by_bw, start=1)]
|
|
1121
1132
|
|
|
1133
|
+
def _to_epoch(self, v: str) -> int:
|
|
1134
|
+
n = int(v)
|
|
1135
|
+
return n // 1000 if n > 10 ** 12 else n # handle ms too
|
|
1136
|
+
|
|
1137
|
+
def m3u8_expiry_validto(self, url: str, safety_seconds: int = 60):
|
|
1138
|
+
q = {k: v[0] for k, v in parse_qs(urlparse(url).query).items() if v}
|
|
1139
|
+
vf = q.get("validfrom")
|
|
1140
|
+
vt = q.get("validto")
|
|
1141
|
+
if not vt:
|
|
1142
|
+
return None # not this scheme
|
|
1143
|
+
start_utc = datetime.fromtimestamp(self._to_epoch(vf), tz=timezone.utc) if vf else None
|
|
1144
|
+
expires_utc = datetime.fromtimestamp(self._to_epoch(vt), tz=timezone.utc)
|
|
1145
|
+
refresh_utc = expires_utc - timedelta(seconds=safety_seconds)
|
|
1146
|
+
secs_left = int((expires_utc - datetime.now(timezone.utc)).total_seconds())
|
|
1147
|
+
return {
|
|
1148
|
+
"start_utc": start_utc,
|
|
1149
|
+
"expires_utc": expires_utc,
|
|
1150
|
+
"refresh_utc": refresh_utc,
|
|
1151
|
+
"seconds_left": max(secs_left, 0),
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
# Example
|
|
1155
|
+
|
|
1156
|
+
|
|
1122
1157
|
def get_segments(self, m3u8_url_master: str, quality: Union[str, int]) -> list:
|
|
1158
|
+
_cache_url = f"{m3u8_url_master}{quality}"
|
|
1159
|
+
_segments: list | None = self.cache.get_segments_from_cache(_cache_url)
|
|
1160
|
+
if _segments:
|
|
1161
|
+
self.logger.info(f"Received: {len(_segments)} from cache!")
|
|
1162
|
+
return _segments
|
|
1163
|
+
|
|
1123
1164
|
# Resolve the quality-specific playlist URL (may still be a master in some edge cases)
|
|
1124
1165
|
playlist_url = self.get_m3u8_by_quality(m3u8_url=m3u8_url_master, quality=quality)
|
|
1125
1166
|
self.logger.debug(f"Trying to fetch segment from m3u8 -> {playlist_url}")
|
|
@@ -1165,6 +1206,8 @@ class BaseCore:
|
|
|
1165
1206
|
segments.append(urljoin(base_url, seg.uri))
|
|
1166
1207
|
|
|
1167
1208
|
self.logger.debug(f"Fetched {len(segments)} segments from m3u8 URL (including init if present)")
|
|
1209
|
+
self.logger.info(f"Saving segments to cache....")
|
|
1210
|
+
self.cache.save_segments_to_cache(_cache_url, segments)
|
|
1168
1211
|
return segments
|
|
1169
1212
|
|
|
1170
1213
|
def download_segment(self, url: str, timeout: int) -> tuple[str, bytes, bool]:
|
|
@@ -1585,4 +1628,4 @@ You need to install `ffmpeg-progress-yield` to use the FFmpeg download mode.""")
|
|
|
1585
1628
|
return True
|
|
1586
1629
|
|
|
1587
1630
|
elif value.lower() in ("false", "0", "no"):
|
|
1588
|
-
return False
|
|
1631
|
+
return False
|
|
@@ -7,7 +7,6 @@ class RuntimeConfig:
|
|
|
7
7
|
self.max_bandwidth_mb = None # Set speed limit in megabytes per second e.g, 2.0, 3.5 etc...
|
|
8
8
|
self.ffmpeg_path = "ffmpeg"
|
|
9
9
|
self.proxy = None
|
|
10
|
-
self.raise_bot_protection = False
|
|
11
10
|
self.verify_ssl = True
|
|
12
11
|
self.cookies = None
|
|
13
12
|
self.locale = "en-US,en;q=0.9" # If you override this, it could change regexes and thus make stuff not work...
|
|
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
|