Backfill same-day snapshots for newly added or restored videos, preserve successful fan snapshots on resume, persist incomplete state before forced re-runs, and split collection completion from backup completion so failed backups can retry without API calls. Reject missing data payloads instead of treating them as empty folders, guard against mass-unfav on suspicious empty remote folders, rebuild Bilibili clients when credentials change, recompute primary folder ownership from active relations with per-folder fav_time, preserve original titles for dropped invalid videos, poll config every 60 seconds, and fix favsort verify interval and mixed resource types.
39 lines
1.2 KiB
Python
39 lines
1.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""按账号角色提供 B 站客户端,并在凭据变化时自动重建。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Callable
|
|
|
|
from ..config import ConfigStore
|
|
from .client import BilibiliClient
|
|
|
|
|
|
class ClientProvider:
|
|
"""缓存客户端;仅当 ``mid`` / ``cookie`` / ``csrf`` 变化时重建。
|
|
|
|
``factory`` 负责固化该角色的限速与重试策略,因此重建不会丢失这些设置。
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
store: ConfigStore,
|
|
role: str,
|
|
factory: Callable[[Any], BilibiliClient],
|
|
) -> None:
|
|
if role not in {"read", "write"}:
|
|
raise ValueError(f"未知账号角色: {role}")
|
|
self.store = store
|
|
self.role = role
|
|
self.factory = factory
|
|
self._key: tuple[int, str, str] | None = None
|
|
self._client: BilibiliClient | None = None
|
|
|
|
def get(self) -> BilibiliClient:
|
|
credentials = getattr(self.store.get().bilibili, self.role)
|
|
key = (credentials.mid, credentials.cookie, credentials.csrf)
|
|
if self._client is None or key != self._key:
|
|
self._client = self.factory(credentials)
|
|
self._key = key
|
|
return self._client
|