eaf-base-api 2.4.8__tar.gz → 2.4.9__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.8/eaf_base_api.egg-info → eaf_base_api-2.4.9}/PKG-INFO +1 -1
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/base.py +72 -32
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9/eaf_base_api.egg-info}/PKG-INFO +1 -1
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/pyproject.toml +1 -1
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/LICENSE +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/MANIFEST.in +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/README.md +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/__init__.py +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/modules/__init__.py +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/modules/config.py +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/modules/errors.py +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/base_api/modules/progress_bars.py +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/eaf_base_api.egg-info/SOURCES.txt +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/eaf_base_api.egg-info/dependency_links.txt +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/eaf_base_api.egg-info/requires.txt +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/eaf_base_api.egg-info/top_level.txt +0 -0
- {eaf_base_api-2.4.8 → eaf_base_api-2.4.9}/setup.cfg +0 -0
|
@@ -7,6 +7,7 @@ import time
|
|
|
7
7
|
import m3u8
|
|
8
8
|
import httpx
|
|
9
9
|
import random
|
|
10
|
+
import certifi
|
|
10
11
|
import logging
|
|
11
12
|
import traceback
|
|
12
13
|
import threading
|
|
@@ -34,6 +35,37 @@ loggers = {}
|
|
|
34
35
|
HEIGHT_FROM_URI = re.compile(r'(?<!\d)(\d{3,4})[pP](?!\d)') # e.g., 1080p, 720P
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def _normalize_quality_value(q) -> Union[str, int]:
|
|
39
|
+
if isinstance(q, int):
|
|
40
|
+
return q
|
|
41
|
+
s = str(q).lower().strip()
|
|
42
|
+
if s in {"best", "half", "worst"}:
|
|
43
|
+
return s
|
|
44
|
+
m = re.search(r'(\d{3,4})', s)
|
|
45
|
+
if m:
|
|
46
|
+
return int(m.group(1))
|
|
47
|
+
raise ValueError(f"Invalid quality: {q}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _choose_quality_from_list(available: List[str | int], target: Union[str, int]):
|
|
51
|
+
# available like ["240", "360", "480", "720", "1080"]
|
|
52
|
+
av = sorted({int(x) for x in available})
|
|
53
|
+
if isinstance(target, str):
|
|
54
|
+
if target == "best":
|
|
55
|
+
return av[-1]
|
|
56
|
+
if target == "worst":
|
|
57
|
+
return av[0]
|
|
58
|
+
if target == "half":
|
|
59
|
+
return av[len(av) // 2]
|
|
60
|
+
raise ValueError("Invalid label.")
|
|
61
|
+
# numeric: highest ≤ target, else closest
|
|
62
|
+
le = [h for h in av if h <= target]
|
|
63
|
+
if le:
|
|
64
|
+
return le[-1]
|
|
65
|
+
# fallback closest (ties -> higher)
|
|
66
|
+
return min(av, key=lambda h: (abs(h - target), -h))
|
|
67
|
+
|
|
68
|
+
|
|
37
69
|
class ErrorVideo:
|
|
38
70
|
"""
|
|
39
71
|
Why?:
|
|
@@ -696,7 +728,7 @@ class BaseCore:
|
|
|
696
728
|
max_keepalive_connections=getattr(self.config, "max_keepalive", 4),
|
|
697
729
|
keepalive_expiry=10,
|
|
698
730
|
)
|
|
699
|
-
ctx = ssl.create_default_context()
|
|
731
|
+
ctx = ssl.create_default_context(cafile=certifi.where())
|
|
700
732
|
if not self.config.verify_ssl:
|
|
701
733
|
ctx.check_hostname = False
|
|
702
734
|
ctx.verify_mode = ssl.CERT_NONE
|
|
@@ -1209,41 +1241,49 @@ class BaseCore:
|
|
|
1209
1241
|
with open(tmp_path, "wb") as out_fp:
|
|
1210
1242
|
# Submit all downloads
|
|
1211
1243
|
with ThreadPoolExecutor(max_workers=workers) as ex:
|
|
1212
|
-
|
|
1244
|
+
# submit all segments
|
|
1245
|
+
pending = {ex.submit(self.download_segment, url, timeout): i
|
|
1246
|
+
for i, url in enumerate(segments)}
|
|
1213
1247
|
|
|
1214
|
-
# Optional: per-segment retry budget (on top of fetch() retries)
|
|
1215
1248
|
seg_retries = [0] * n
|
|
1216
1249
|
max_seg_retries = 2
|
|
1217
1250
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1251
|
+
parts: List[Optional[bytes]] = [None] * n
|
|
1252
|
+
next_to_write = 0
|
|
1253
|
+
progressed = 0 # how many segments have finished (success or final failure)
|
|
1254
|
+
|
|
1255
|
+
while pending:
|
|
1256
|
+
done, _ = wait(pending, return_when=FIRST_COMPLETED)
|
|
1257
|
+
|
|
1258
|
+
for fut in done:
|
|
1259
|
+
i = pending.pop(fut)
|
|
1260
|
+
try:
|
|
1261
|
+
_, data, success = fut.result()
|
|
1262
|
+
except Exception as e:
|
|
1263
|
+
self.logger.error(f"Worker exception for segment {i}: {e}")
|
|
1264
|
+
success, data = False, b""
|
|
1265
|
+
|
|
1266
|
+
if success and data:
|
|
1267
|
+
parts[i] = data
|
|
1268
|
+
# count immediately: a segment finished successfully
|
|
1269
|
+
progressed += 1
|
|
1270
|
+
callback(progressed, n)
|
|
1271
|
+
else:
|
|
1272
|
+
# retry a couple times
|
|
1273
|
+
if seg_retries[i] < max_seg_retries:
|
|
1274
|
+
seg_retries[i] += 1
|
|
1275
|
+
pending[ex.submit(self.download_segment, segments[i], timeout)] = i
|
|
1276
|
+
continue
|
|
1277
|
+
# give up: mark placeholder and still advance progress
|
|
1278
|
+
parts[i] = b""
|
|
1279
|
+
progressed += 1
|
|
1280
|
+
callback(progressed, n)
|
|
1281
|
+
|
|
1282
|
+
# now try to flush any contiguous ready parts
|
|
1283
|
+
while next_to_write < n and parts[next_to_write] is not None:
|
|
1284
|
+
if parts[next_to_write]:
|
|
1285
|
+
out_fp.write(parts[next_to_write])
|
|
1286
|
+
next_to_write += 1
|
|
1247
1287
|
|
|
1248
1288
|
if failures:
|
|
1249
1289
|
# Clean temp file to avoid leaving corrupted output
|
|
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
|