mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-03-10 08:29:00 +00:00
Add select-titles
This commit is contained in:
@@ -65,6 +65,7 @@ from unshackle.core.utils import tags
|
|||||||
from unshackle.core.utils.click_types import (AUDIO_CODEC_LIST, LANGUAGE_RANGE, QUALITY_LIST, SEASON_RANGE,
|
from unshackle.core.utils.click_types import (AUDIO_CODEC_LIST, LANGUAGE_RANGE, QUALITY_LIST, SEASON_RANGE,
|
||||||
ContextData, MultipleChoice, SubtitleCodecChoice, VideoCodecChoice)
|
ContextData, MultipleChoice, SubtitleCodecChoice, VideoCodecChoice)
|
||||||
from unshackle.core.utils.collections import merge_dict
|
from unshackle.core.utils.collections import merge_dict
|
||||||
|
from unshackle.core.utils.selector import select_multiple
|
||||||
from unshackle.core.utils.subprocess import ffprobe
|
from unshackle.core.utils.subprocess import ffprobe
|
||||||
from unshackle.core.vaults import Vaults
|
from unshackle.core.vaults import Vaults
|
||||||
|
|
||||||
@@ -346,6 +347,12 @@ class dl:
|
|||||||
default=None,
|
default=None,
|
||||||
help="Create separate output files per audio codec instead of merging all audio.",
|
help="Create separate output files per audio codec instead of merging all audio.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--select-titles",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Interactively select downloads from a list. Only use with Series to select Episodes",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-w",
|
"-w",
|
||||||
"--wanted",
|
"--wanted",
|
||||||
@@ -859,6 +866,7 @@ class dl:
|
|||||||
range_: list[Video.Range],
|
range_: list[Video.Range],
|
||||||
channels: float,
|
channels: float,
|
||||||
no_atmos: bool,
|
no_atmos: bool,
|
||||||
|
select_titles: bool,
|
||||||
wanted: list[str],
|
wanted: list[str],
|
||||||
latest_episode: bool,
|
latest_episode: bool,
|
||||||
lang: list[str],
|
lang: list[str],
|
||||||
@@ -1047,6 +1055,84 @@ class dl:
|
|||||||
if list_titles:
|
if list_titles:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Enables manual selection for Series when --select-titles is set
|
||||||
|
if select_titles and type(titles) == Series:
|
||||||
|
console.print(Padding(Rule(f"[rule.text]Select Titles"), (1, 2)))
|
||||||
|
|
||||||
|
selection_titles = []
|
||||||
|
dependencies = {}
|
||||||
|
original_indices = {}
|
||||||
|
|
||||||
|
current_season = None
|
||||||
|
current_season_header_idx = -1
|
||||||
|
|
||||||
|
unique_seasons = {t.season for t in titles}
|
||||||
|
multiple_seasons = len(unique_seasons) > 1
|
||||||
|
|
||||||
|
# Build selection options
|
||||||
|
for i, t in enumerate(titles):
|
||||||
|
# Insert season header only if multiple seasons exist
|
||||||
|
if multiple_seasons and t.season != current_season:
|
||||||
|
current_season = t.season
|
||||||
|
header_text = f"Season {t.season}"
|
||||||
|
selection_titles.append(header_text)
|
||||||
|
current_season_header_idx = len(selection_titles) - 1
|
||||||
|
dependencies[current_season_header_idx] = []
|
||||||
|
# Note: Headers are not mapped to actual title indices
|
||||||
|
|
||||||
|
# Format display name
|
||||||
|
display_name = ((t.name[:35].rstrip() + "…") if len(t.name) > 35 else t.name) if t.name else None
|
||||||
|
|
||||||
|
# Apply indentation only for multiple seasons
|
||||||
|
prefix = " " if multiple_seasons else ""
|
||||||
|
option_text = (
|
||||||
|
f"{prefix}{t.number}" + (f". {display_name}" if t.name else "")
|
||||||
|
)
|
||||||
|
|
||||||
|
selection_titles.append(option_text)
|
||||||
|
current_ui_idx = len(selection_titles) - 1
|
||||||
|
|
||||||
|
# Map UI index to actual title index
|
||||||
|
original_indices[current_ui_idx] = i
|
||||||
|
|
||||||
|
# Link episode to season header for group selection
|
||||||
|
if current_season_header_idx != -1:
|
||||||
|
dependencies[current_season_header_idx].append(current_ui_idx)
|
||||||
|
|
||||||
|
selection_start = time.time()
|
||||||
|
|
||||||
|
# Execute selector with dependencies (headers select all children)
|
||||||
|
selected_ui_idx = select_multiple(
|
||||||
|
selection_titles,
|
||||||
|
minimal_count=1,
|
||||||
|
page_size=8,
|
||||||
|
return_indices=True,
|
||||||
|
dependencies=dependencies
|
||||||
|
)
|
||||||
|
|
||||||
|
selection_end = time.time()
|
||||||
|
start_time += (selection_end - selection_start)
|
||||||
|
|
||||||
|
# Map UI indices back to title indices (excluding headers)
|
||||||
|
selected_idx = []
|
||||||
|
for idx in selected_ui_idx:
|
||||||
|
if idx in original_indices:
|
||||||
|
selected_idx.append(original_indices[idx])
|
||||||
|
|
||||||
|
# Ensure indices are unique and ordered
|
||||||
|
selected_idx = sorted(set(selected_idx))
|
||||||
|
keep = set(selected_idx)
|
||||||
|
|
||||||
|
# In-place filter: remove unselected items (iterate backwards)
|
||||||
|
for i in range(len(titles) - 1, -1, -1):
|
||||||
|
if i not in keep:
|
||||||
|
del titles[i]
|
||||||
|
|
||||||
|
# Show selected count
|
||||||
|
if titles:
|
||||||
|
count = len(titles)
|
||||||
|
console.print(Padding(f"[text]Total selected: {count}[/]", (0, 5)))
|
||||||
|
|
||||||
# Determine the latest episode if --latest-episode is set
|
# Determine the latest episode if --latest-episode is set
|
||||||
latest_episode_id = None
|
latest_episode_id = None
|
||||||
if latest_episode and isinstance(titles, Series) and len(titles) > 0:
|
if latest_episode and isinstance(titles, Series) and len(titles) > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user