mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2025-10-23 15:11:08 +00:00
feat(dl): add --latest-episode option to download only the most recent episode
Adds a new CLI option `-le, --latest-episode` that automatically selects and downloads only the single most recent episode from a series, regardless of which season it's in. Fixes #28
This commit is contained in:
@@ -152,6 +152,13 @@ class dl:
|
|||||||
default=None,
|
default=None,
|
||||||
help="Wanted episodes, e.g. `S01-S05,S07`, `S01E01-S02E03`, `S02-S02E03`, e.t.c, defaults to all.",
|
help="Wanted episodes, e.g. `S01-S05,S07`, `S01E01-S02E03`, `S02-S02E03`, e.t.c, defaults to all.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"-le",
|
||||||
|
"--latest-episode",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Download only the single most recent episode available.",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-l",
|
"-l",
|
||||||
"--lang",
|
"--lang",
|
||||||
@@ -322,11 +329,7 @@ class dl:
|
|||||||
debug_log_path = config.directories.logs / config.filenames.debug_log.format_map(
|
debug_log_path = config.directories.logs / config.filenames.debug_log.format_map(
|
||||||
defaultdict(str, service=self.service, time=datetime.now().strftime("%Y%m%d-%H%M%S"))
|
defaultdict(str, service=self.service, time=datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||||
)
|
)
|
||||||
init_debug_logger(
|
init_debug_logger(log_path=debug_log_path, enabled=True, log_keys=config.debug_keys)
|
||||||
log_path=debug_log_path,
|
|
||||||
enabled=True,
|
|
||||||
log_keys=config.debug_keys
|
|
||||||
)
|
|
||||||
self.debug_logger = get_debug_logger()
|
self.debug_logger = get_debug_logger()
|
||||||
|
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
@@ -342,8 +345,12 @@ class dl:
|
|||||||
"tmdb_id": tmdb_id,
|
"tmdb_id": tmdb_id,
|
||||||
"tmdb_name": tmdb_name,
|
"tmdb_name": tmdb_name,
|
||||||
"tmdb_year": tmdb_year,
|
"tmdb_year": tmdb_year,
|
||||||
"cli_params": {k: v for k, v in ctx.params.items() if k not in ['profile', 'proxy', 'tag', 'tmdb_id', 'tmdb_name', 'tmdb_year']}
|
"cli_params": {
|
||||||
}
|
k: v
|
||||||
|
for k, v in ctx.params.items()
|
||||||
|
if k not in ["profile", "proxy", "tag", "tmdb_id", "tmdb_name", "tmdb_year"]
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.debug_logger = None
|
self.debug_logger = None
|
||||||
@@ -361,7 +368,7 @@ class dl:
|
|||||||
level="DEBUG",
|
level="DEBUG",
|
||||||
operation="load_service_config",
|
operation="load_service_config",
|
||||||
service=self.service,
|
service=self.service,
|
||||||
context={"config_path": str(service_config_path), "config": self.service_config}
|
context={"config_path": str(service_config_path), "config": self.service_config},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.service_config = {}
|
self.service_config = {}
|
||||||
@@ -438,19 +445,25 @@ class dl:
|
|||||||
cdm_info = {"type": "DecryptLabs", "drm_type": drm_type, "security_level": self.cdm.security_level}
|
cdm_info = {"type": "DecryptLabs", "drm_type": drm_type, "security_level": self.cdm.security_level}
|
||||||
elif hasattr(self.cdm, "device_type") and self.cdm.device_type.name in ["ANDROID", "CHROME"]:
|
elif hasattr(self.cdm, "device_type") and self.cdm.device_type.name in ["ANDROID", "CHROME"]:
|
||||||
self.log.info(f"Loaded Widevine CDM: {self.cdm.system_id} (L{self.cdm.security_level})")
|
self.log.info(f"Loaded Widevine CDM: {self.cdm.system_id} (L{self.cdm.security_level})")
|
||||||
cdm_info = {"type": "Widevine", "system_id": self.cdm.system_id, "security_level": self.cdm.security_level, "device_type": self.cdm.device_type.name}
|
cdm_info = {
|
||||||
|
"type": "Widevine",
|
||||||
|
"system_id": self.cdm.system_id,
|
||||||
|
"security_level": self.cdm.security_level,
|
||||||
|
"device_type": self.cdm.device_type.name,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Loaded PlayReady CDM: {self.cdm.certificate_chain.get_name()} (L{self.cdm.security_level})"
|
f"Loaded PlayReady CDM: {self.cdm.certificate_chain.get_name()} (L{self.cdm.security_level})"
|
||||||
)
|
)
|
||||||
cdm_info = {"type": "PlayReady", "certificate": self.cdm.certificate_chain.get_name(), "security_level": self.cdm.security_level}
|
cdm_info = {
|
||||||
|
"type": "PlayReady",
|
||||||
|
"certificate": self.cdm.certificate_chain.get_name(),
|
||||||
|
"security_level": self.cdm.security_level,
|
||||||
|
}
|
||||||
|
|
||||||
if self.debug_logger and cdm_info:
|
if self.debug_logger and cdm_info:
|
||||||
self.debug_logger.log(
|
self.debug_logger.log(
|
||||||
level="INFO",
|
level="INFO", operation="load_cdm", service=self.service, context={"cdm": cdm_info}
|
||||||
operation="load_cdm",
|
|
||||||
service=self.service,
|
|
||||||
context={"cdm": cdm_info}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.proxy_providers = []
|
self.proxy_providers = []
|
||||||
@@ -526,6 +539,7 @@ class dl:
|
|||||||
channels: float,
|
channels: float,
|
||||||
no_atmos: bool,
|
no_atmos: bool,
|
||||||
wanted: list[str],
|
wanted: list[str],
|
||||||
|
latest_episode: bool,
|
||||||
lang: list[str],
|
lang: list[str],
|
||||||
v_lang: list[str],
|
v_lang: list[str],
|
||||||
a_lang: list[str],
|
a_lang: list[str],
|
||||||
@@ -587,8 +601,8 @@ class dl:
|
|||||||
context={
|
context={
|
||||||
"cdm_only": cdm_only,
|
"cdm_only": cdm_only,
|
||||||
"vaults_only": vaults_only,
|
"vaults_only": vaults_only,
|
||||||
"mode": "CDM only" if cdm_only else ("Vaults only" if vaults_only else "Both CDM and Vaults")
|
"mode": "CDM only" if cdm_only else ("Vaults only" if vaults_only else "Both CDM and Vaults"),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with console.status("Authenticating with Service...", spinner="dots"):
|
with console.status("Authenticating with Service...", spinner="dots"):
|
||||||
@@ -606,16 +620,13 @@ class dl:
|
|||||||
context={
|
context={
|
||||||
"has_cookies": bool(cookies),
|
"has_cookies": bool(cookies),
|
||||||
"has_credentials": bool(credential),
|
"has_credentials": bool(credential),
|
||||||
"profile": self.profile
|
"profile": self.profile,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
self.debug_logger.log_error(
|
self.debug_logger.log_error(
|
||||||
"authenticate",
|
"authenticate", e, service=self.service, context={"profile": self.profile}
|
||||||
e,
|
|
||||||
service=self.service,
|
|
||||||
context={"profile": self.profile}
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -630,31 +641,24 @@ class dl:
|
|||||||
operation="get_titles",
|
operation="get_titles",
|
||||||
service=self.service,
|
service=self.service,
|
||||||
message="No titles returned from service",
|
message="No titles returned from service",
|
||||||
success=False
|
success=False,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
self.debug_logger.log_error(
|
self.debug_logger.log_error("get_titles", e, service=self.service)
|
||||||
"get_titles",
|
|
||||||
e,
|
|
||||||
service=self.service
|
|
||||||
)
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
titles_info = {
|
titles_info = {
|
||||||
"type": titles.__class__.__name__,
|
"type": titles.__class__.__name__,
|
||||||
"count": len(titles) if hasattr(titles, "__len__") else 1,
|
"count": len(titles) if hasattr(titles, "__len__") else 1,
|
||||||
"title": str(titles)
|
"title": str(titles),
|
||||||
}
|
}
|
||||||
if hasattr(titles, "seasons"):
|
if hasattr(titles, "seasons"):
|
||||||
titles_info["seasons"] = len(titles.seasons) if hasattr(titles, "seasons") else 0
|
titles_info["seasons"] = len(titles.seasons) if hasattr(titles, "seasons") else 0
|
||||||
self.debug_logger.log(
|
self.debug_logger.log(
|
||||||
level="INFO",
|
level="INFO", operation="get_titles", service=self.service, context={"titles": titles_info}
|
||||||
operation="get_titles",
|
|
||||||
service=self.service,
|
|
||||||
context={"titles": titles_info}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.tmdb_year and self.tmdb_id:
|
if self.tmdb_year and self.tmdb_id:
|
||||||
@@ -674,8 +678,21 @@ class dl:
|
|||||||
if list_titles:
|
if list_titles:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Determine the latest episode if --latest-episode is set
|
||||||
|
latest_episode_id = None
|
||||||
|
if latest_episode and isinstance(titles, Series) and len(titles) > 0:
|
||||||
|
# Series is already sorted by (season, number, year)
|
||||||
|
# The last episode in the sorted list is the latest
|
||||||
|
latest_ep = titles[-1]
|
||||||
|
latest_episode_id = f"{latest_ep.season}x{latest_ep.number}"
|
||||||
|
self.log.info(f"Latest episode mode: Selecting S{latest_ep.season:02}E{latest_ep.number:02}")
|
||||||
|
|
||||||
for i, title in enumerate(titles):
|
for i, title in enumerate(titles):
|
||||||
if isinstance(title, Episode) and wanted and f"{title.season}x{title.number}" not in wanted:
|
if isinstance(title, Episode) and latest_episode and latest_episode_id:
|
||||||
|
# If --latest-episode is set, only process the latest episode
|
||||||
|
if f"{title.season}x{title.number}" != latest_episode_id:
|
||||||
|
continue
|
||||||
|
elif isinstance(title, Episode) and wanted and f"{title.season}x{title.number}" not in wanted:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
console.print(Padding(Rule(f"[rule.text]{title}"), (1, 2)))
|
console.print(Padding(Rule(f"[rule.text]{title}"), (1, 2)))
|
||||||
@@ -750,10 +767,7 @@ class dl:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
self.debug_logger.log_error(
|
self.debug_logger.log_error(
|
||||||
"get_tracks",
|
"get_tracks", e, service=self.service, context={"title": str(title)}
|
||||||
e,
|
|
||||||
service=self.service,
|
|
||||||
context={"title": str(title)}
|
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -764,34 +778,40 @@ class dl:
|
|||||||
"audio_tracks": len(title.tracks.audio),
|
"audio_tracks": len(title.tracks.audio),
|
||||||
"subtitle_tracks": len(title.tracks.subtitles),
|
"subtitle_tracks": len(title.tracks.subtitles),
|
||||||
"has_chapters": bool(title.tracks.chapters),
|
"has_chapters": bool(title.tracks.chapters),
|
||||||
"videos": [{
|
"videos": [
|
||||||
|
{
|
||||||
"codec": str(v.codec),
|
"codec": str(v.codec),
|
||||||
"resolution": f"{v.width}x{v.height}" if v.width and v.height else "unknown",
|
"resolution": f"{v.width}x{v.height}" if v.width and v.height else "unknown",
|
||||||
"bitrate": v.bitrate,
|
"bitrate": v.bitrate,
|
||||||
"range": str(v.range),
|
"range": str(v.range),
|
||||||
"language": str(v.language) if v.language else None,
|
"language": str(v.language) if v.language else None,
|
||||||
"drm": [str(type(d).__name__) for d in v.drm] if v.drm else []
|
"drm": [str(type(d).__name__) for d in v.drm] if v.drm else [],
|
||||||
} for v in title.tracks.videos],
|
}
|
||||||
"audio": [{
|
for v in title.tracks.videos
|
||||||
|
],
|
||||||
|
"audio": [
|
||||||
|
{
|
||||||
"codec": str(a.codec),
|
"codec": str(a.codec),
|
||||||
"bitrate": a.bitrate,
|
"bitrate": a.bitrate,
|
||||||
"channels": a.channels,
|
"channels": a.channels,
|
||||||
"language": str(a.language) if a.language else None,
|
"language": str(a.language) if a.language else None,
|
||||||
"descriptive": a.descriptive,
|
"descriptive": a.descriptive,
|
||||||
"drm": [str(type(d).__name__) for d in a.drm] if a.drm else []
|
"drm": [str(type(d).__name__) for d in a.drm] if a.drm else [],
|
||||||
} for a in title.tracks.audio],
|
}
|
||||||
"subtitles": [{
|
for a in title.tracks.audio
|
||||||
|
],
|
||||||
|
"subtitles": [
|
||||||
|
{
|
||||||
"codec": str(s.codec),
|
"codec": str(s.codec),
|
||||||
"language": str(s.language) if s.language else None,
|
"language": str(s.language) if s.language else None,
|
||||||
"forced": s.forced,
|
"forced": s.forced,
|
||||||
"sdh": s.sdh
|
"sdh": s.sdh,
|
||||||
} for s in title.tracks.subtitles]
|
}
|
||||||
|
for s in title.tracks.subtitles
|
||||||
|
],
|
||||||
}
|
}
|
||||||
self.debug_logger.log(
|
self.debug_logger.log(
|
||||||
level="INFO",
|
level="INFO", operation="get_tracks", service=self.service, context=tracks_info
|
||||||
operation="get_tracks",
|
|
||||||
service=self.service,
|
|
||||||
context=tracks_info
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# strip SDH subs to non-SDH if no equivalent same-lang non-SDH is available
|
# strip SDH subs to non-SDH if no equivalent same-lang non-SDH is available
|
||||||
@@ -1185,7 +1205,7 @@ class dl:
|
|||||||
operation="download_tracks",
|
operation="download_tracks",
|
||||||
service=self.service,
|
service=self.service,
|
||||||
message="Download cancelled by user",
|
message="Download cancelled by user",
|
||||||
context={"title": str(title)}
|
context={"title": str(title)},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
except Exception as e: # noqa
|
except Exception as e: # noqa
|
||||||
@@ -1219,8 +1239,8 @@ class dl:
|
|||||||
"title": str(title),
|
"title": str(title),
|
||||||
"error_type": type(e).__name__,
|
"error_type": type(e).__name__,
|
||||||
"tracks_count": len(title.tracks),
|
"tracks_count": len(title.tracks),
|
||||||
"returncode": getattr(e, "returncode", None)
|
"returncode": getattr(e, "returncode", None),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1475,9 +1495,13 @@ class dl:
|
|||||||
if not no_folder and isinstance(title, (Episode, Song)):
|
if not no_folder and isinstance(title, (Episode, Song)):
|
||||||
# Create folder based on title
|
# Create folder based on title
|
||||||
# Use first available track for filename generation
|
# Use first available track for filename generation
|
||||||
sample_track = title.tracks.videos[0] if title.tracks.videos else (
|
sample_track = (
|
||||||
title.tracks.audio[0] if title.tracks.audio else (
|
title.tracks.videos[0]
|
||||||
title.tracks.subtitles[0] if title.tracks.subtitles else None
|
if title.tracks.videos
|
||||||
|
else (
|
||||||
|
title.tracks.audio[0]
|
||||||
|
if title.tracks.audio
|
||||||
|
else (title.tracks.subtitles[0] if title.tracks.subtitles else None)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if sample_track and sample_track.path:
|
if sample_track and sample_track.path:
|
||||||
@@ -1498,7 +1522,9 @@ class dl:
|
|||||||
track_suffix = f".{track.codec.name if hasattr(track.codec, 'name') else 'video'}"
|
track_suffix = f".{track.codec.name if hasattr(track.codec, 'name') else 'video'}"
|
||||||
elif isinstance(track, Audio):
|
elif isinstance(track, Audio):
|
||||||
lang_suffix = f".{track.language}" if track.language else ""
|
lang_suffix = f".{track.language}" if track.language else ""
|
||||||
track_suffix = f"{lang_suffix}.{track.codec.name if hasattr(track.codec, 'name') else 'audio'}"
|
track_suffix = (
|
||||||
|
f"{lang_suffix}.{track.codec.name if hasattr(track.codec, 'name') else 'audio'}"
|
||||||
|
)
|
||||||
elif isinstance(track, Subtitle):
|
elif isinstance(track, Subtitle):
|
||||||
lang_suffix = f".{track.language}" if track.language else ""
|
lang_suffix = f".{track.language}" if track.language else ""
|
||||||
forced_suffix = ".forced" if track.forced else ""
|
forced_suffix = ".forced" if track.forced else ""
|
||||||
@@ -1595,8 +1621,8 @@ class dl:
|
|||||||
"title": str(title),
|
"title": str(title),
|
||||||
"pssh": drm.pssh.dumps() if drm.pssh else None,
|
"pssh": drm.pssh.dumps() if drm.pssh else None,
|
||||||
"kids": [k.hex for k in drm.kids],
|
"kids": [k.hex for k in drm.kids],
|
||||||
"track_kid": track_kid.hex if track_kid else None
|
"track_kid": track_kid.hex if track_kid else None,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with self.DRM_TABLE_LOCK:
|
with self.DRM_TABLE_LOCK:
|
||||||
@@ -1637,8 +1663,8 @@ class dl:
|
|||||||
"kid": kid.hex,
|
"kid": kid.hex,
|
||||||
"content_key": content_key,
|
"content_key": content_key,
|
||||||
"track": str(track),
|
"track": str(track),
|
||||||
"from_cache": True
|
"from_cache": True,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
elif vaults_only:
|
elif vaults_only:
|
||||||
msg = f"No Vault has a Key for {kid.hex} and --vaults-only was used"
|
msg = f"No Vault has a Key for {kid.hex} and --vaults-only was used"
|
||||||
@@ -1651,7 +1677,7 @@ class dl:
|
|||||||
operation="vault_key_not_found",
|
operation="vault_key_not_found",
|
||||||
service=self.service,
|
service=self.service,
|
||||||
message=msg,
|
message=msg,
|
||||||
context={"kid": kid.hex, "track": str(track)}
|
context={"kid": kid.hex, "track": str(track)},
|
||||||
)
|
)
|
||||||
raise Widevine.Exceptions.CEKNotFound(msg)
|
raise Widevine.Exceptions.CEKNotFound(msg)
|
||||||
else:
|
else:
|
||||||
@@ -1671,8 +1697,8 @@ class dl:
|
|||||||
message="Requesting Widevine license from service",
|
message="Requesting Widevine license from service",
|
||||||
context={
|
context={
|
||||||
"track": str(track),
|
"track": str(track),
|
||||||
"kids_needed": [k.hex for k in all_kids if k not in drm.content_keys]
|
"kids_needed": [k.hex for k in all_kids if k not in drm.content_keys],
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1693,10 +1719,7 @@ class dl:
|
|||||||
"get_license",
|
"get_license",
|
||||||
e,
|
e,
|
||||||
service=self.service,
|
service=self.service,
|
||||||
context={
|
context={"track": str(track), "exception_type": type(e).__name__},
|
||||||
"track": str(track),
|
|
||||||
"exception_type": type(e).__name__
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@@ -1708,8 +1731,8 @@ class dl:
|
|||||||
context={
|
context={
|
||||||
"track": str(track),
|
"track": str(track),
|
||||||
"keys_count": len(drm.content_keys),
|
"keys_count": len(drm.content_keys),
|
||||||
"kids": [k.hex for k in drm.content_keys.keys()]
|
"kids": [k.hex for k in drm.content_keys.keys()],
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
for kid_, key in drm.content_keys.items():
|
for kid_, key in drm.content_keys.items():
|
||||||
@@ -1767,8 +1790,8 @@ class dl:
|
|||||||
"title": str(title),
|
"title": str(title),
|
||||||
"pssh": drm.pssh_b64 or "",
|
"pssh": drm.pssh_b64 or "",
|
||||||
"kids": [k.hex for k in drm.kids],
|
"kids": [k.hex for k in drm.kids],
|
||||||
"track_kid": track_kid.hex if track_kid else None
|
"track_kid": track_kid.hex if track_kid else None,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with self.DRM_TABLE_LOCK:
|
with self.DRM_TABLE_LOCK:
|
||||||
@@ -1816,8 +1839,8 @@ class dl:
|
|||||||
"content_key": content_key,
|
"content_key": content_key,
|
||||||
"track": str(track),
|
"track": str(track),
|
||||||
"from_cache": True,
|
"from_cache": True,
|
||||||
"drm_type": "PlayReady"
|
"drm_type": "PlayReady",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
elif vaults_only:
|
elif vaults_only:
|
||||||
msg = f"No Vault has a Key for {kid.hex} and --vaults-only was used"
|
msg = f"No Vault has a Key for {kid.hex} and --vaults-only was used"
|
||||||
@@ -1830,7 +1853,7 @@ class dl:
|
|||||||
operation="vault_key_not_found",
|
operation="vault_key_not_found",
|
||||||
service=self.service,
|
service=self.service,
|
||||||
message=msg,
|
message=msg,
|
||||||
context={"kid": kid.hex, "track": str(track), "drm_type": "PlayReady"}
|
context={"kid": kid.hex, "track": str(track), "drm_type": "PlayReady"},
|
||||||
)
|
)
|
||||||
raise PlayReady.Exceptions.CEKNotFound(msg)
|
raise PlayReady.Exceptions.CEKNotFound(msg)
|
||||||
else:
|
else:
|
||||||
@@ -1860,8 +1883,8 @@ class dl:
|
|||||||
context={
|
context={
|
||||||
"track": str(track),
|
"track": str(track),
|
||||||
"exception_type": type(e).__name__,
|
"exception_type": type(e).__name__,
|
||||||
"drm_type": "PlayReady"
|
"drm_type": "PlayReady",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@@ -1937,7 +1960,7 @@ class dl:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save_cookies(path: Path, cookies: CookieJar):
|
def save_cookies(path: Path, cookies: CookieJar):
|
||||||
if hasattr(cookies, 'jar'):
|
if hasattr(cookies, "jar"):
|
||||||
cookies = cookies.jar
|
cookies = cookies.jar
|
||||||
|
|
||||||
cookie_jar = MozillaCookieJar(path)
|
cookie_jar = MozillaCookieJar(path)
|
||||||
@@ -2084,12 +2107,12 @@ class dl:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
return RemoteCdm(
|
return RemoteCdm(
|
||||||
device_type=cdm_api['Device Type'],
|
device_type=cdm_api["Device Type"],
|
||||||
system_id=cdm_api['System ID'],
|
system_id=cdm_api["System ID"],
|
||||||
security_level=cdm_api['Security Level'],
|
security_level=cdm_api["Security Level"],
|
||||||
host=cdm_api['Host'],
|
host=cdm_api["Host"],
|
||||||
secret=cdm_api['Secret'],
|
secret=cdm_api["Secret"],
|
||||||
device_name=cdm_api['Device Name'],
|
device_name=cdm_api["Device Name"],
|
||||||
)
|
)
|
||||||
|
|
||||||
prd_path = config.directories.prds / f"{cdm_name}.prd"
|
prd_path = config.directories.prds / f"{cdm_name}.prd"
|
||||||
|
|||||||
Reference in New Issue
Block a user