feat: enhance download manager with folder notes and improved worker settings
This commit is contained in:
+75
-10
@@ -147,7 +147,7 @@ def load_episodes(token: str, device: DeviceProfile | None, vid: int) -> list[di
|
||||
return merged
|
||||
|
||||
|
||||
def expand_variant(master_url: str) -> tuple[str, str, list[str]]:
|
||||
def expand_variant(master_url: str) -> tuple[str, str, list[dict]]:
|
||||
master_text = http.read_text(master_url)
|
||||
variant_rel = next(
|
||||
(line.strip() for line in master_text.splitlines() if line.strip() and not line.startswith("#")),
|
||||
@@ -155,16 +155,81 @@ def expand_variant(master_url: str) -> tuple[str, str, list[str]]:
|
||||
)
|
||||
if not variant_rel:
|
||||
raise RuntimeError("播放列表没有视频地址")
|
||||
variant_url = urljoin(master_url, variant_rel)
|
||||
media_text = http.read_text(variant_url)
|
||||
segments = [
|
||||
urljoin(variant_url, line.strip())
|
||||
for line in media_text.splitlines()
|
||||
if line.strip() and not line.startswith("#")
|
||||
]
|
||||
if not segments:
|
||||
if _looks_like_segment(variant_rel):
|
||||
variant_url = master_url
|
||||
media_text = master_text
|
||||
else:
|
||||
variant_url = urljoin(master_url, variant_rel)
|
||||
media_text = http.read_text(variant_url)
|
||||
localized, items = localize_media(media_text, variant_url)
|
||||
if not items:
|
||||
raise RuntimeError("播放列表没有分片")
|
||||
return master_text, media_text, segments
|
||||
return master_text, localized, items
|
||||
|
||||
|
||||
def localize_media(text: str, base: str) -> tuple[str, list[dict]]:
|
||||
urls = _resource_urls(text, base)
|
||||
names: list[str] = []
|
||||
items: list[dict] = []
|
||||
used: set[str] = set()
|
||||
known: dict[str, str] = {}
|
||||
for index, url in enumerate(urls, start=1):
|
||||
name = known.get(url)
|
||||
if name is None:
|
||||
name = _resource_name(url, index, used)
|
||||
known[url] = name
|
||||
items.append({"name": name, "url": url})
|
||||
names.append(name)
|
||||
return _apply_local_names(text, names), items
|
||||
|
||||
|
||||
def _resource_urls(text: str, base: str) -> list[str]:
|
||||
urls = []
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
if stripped.startswith("#"):
|
||||
urls.extend(urljoin(base, match) for match in re.findall(r'URI="([^"]*)"', stripped))
|
||||
continue
|
||||
urls.append(urljoin(base, stripped))
|
||||
return urls
|
||||
|
||||
|
||||
def _resource_name(url: str, index: int, used: set[str]) -> str:
|
||||
raw = Path(url.split("?", 1)[0].rstrip("/")).name
|
||||
raw = re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", raw).strip(" .") or f"part-{index}.ts"
|
||||
candidate = raw
|
||||
sequence = index
|
||||
while candidate in used:
|
||||
suffix = Path(raw).suffix
|
||||
stem = Path(raw).stem
|
||||
candidate = f"{stem}-{sequence}{suffix}"
|
||||
sequence += 1
|
||||
used.add(candidate)
|
||||
return candidate
|
||||
|
||||
|
||||
def _apply_local_names(text: str, names: list[str]) -> str:
|
||||
index = 0
|
||||
|
||||
def take() -> str:
|
||||
nonlocal index
|
||||
name = names[index] if index < len(names) else ""
|
||||
index += 1
|
||||
return name
|
||||
|
||||
lines = []
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
lines.append(line)
|
||||
continue
|
||||
if stripped.startswith("#"):
|
||||
lines.append(re.sub(r'URI="([^"]*)"', lambda _match: f'URI="{take()}"', line))
|
||||
continue
|
||||
lines.append(take() or stripped)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def episode_src(token: str, device: DeviceProfile | None, vid: int, cid: int) -> str:
|
||||
|
||||
Reference in New Issue
Block a user