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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eaf_base_api
3
- Version: 2.4.9
3
+ Version: 2.5.0
4
4
  Summary: A base API for EchterAlsFake's Porn APIs
5
5
  Author-email: Johannes Habel <EchterAlsFake@proton.me>
6
6
  License: LGPLv3
@@ -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
- raise ResourceGone(f"Resource: {url} is Gone (410).")
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...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eaf_base_api
3
- Version: 2.4.9
3
+ Version: 2.5.0
4
4
  Summary: A base API for EchterAlsFake's Porn APIs
5
5
  Author-email: Johannes Habel <EchterAlsFake@proton.me>
6
6
  License: LGPLv3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "eaf_base_api"
7
- version = "2.4.9"
7
+ version = "2.5.0"
8
8
  description = "A base API for EchterAlsFake's Porn APIs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"
@@ -1,5 +0,0 @@
1
- __all__ = ["BaseCore", "Callback", "config"]
2
-
3
- from base_api.base import BaseCore
4
- from base_api.modules.progress_bars import Callback
5
- from base_api.modules.config import config
File without changes
File without changes
File without changes
File without changes