Release v1.0.10: Jellyfin multi-episode file naming (S01E01-E02).
Match TheTVDB combined-order titles and rename to official aired episode ranges for files that contain two episodes in one video. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+56
-8
@@ -339,6 +339,7 @@ class _TvdbMatchWorker(QThread):
|
||||
season_type: str,
|
||||
filenames: list[str],
|
||||
all_seasons: bool,
|
||||
multi_episode: bool,
|
||||
):
|
||||
super().__init__()
|
||||
self.series_id = series_id
|
||||
@@ -346,30 +347,60 @@ class _TvdbMatchWorker(QThread):
|
||||
self.season_type = season_type
|
||||
self.filenames = filenames
|
||||
self.all_seasons = all_seasons
|
||||
self.multi_episode = multi_episode
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
client = TvdbClient(TVDB_API_KEY)
|
||||
if self.all_seasons:
|
||||
official = client.get_all_episodes(self.series_id, season_type="official")
|
||||
season_filter = 0
|
||||
else:
|
||||
official = client.get_season_episodes(
|
||||
self.series_id,
|
||||
self.season,
|
||||
season_type="official",
|
||||
)
|
||||
season_filter = self.season
|
||||
|
||||
if self.all_seasons:
|
||||
episodes = client.get_all_episodes(
|
||||
self.series_id,
|
||||
season_type=self.season_type,
|
||||
)
|
||||
season_filter = 0
|
||||
else:
|
||||
episodes = client.get_season_episodes(
|
||||
self.series_id,
|
||||
self.season,
|
||||
season_type=self.season_type,
|
||||
)
|
||||
season_filter = self.season
|
||||
if not episodes:
|
||||
|
||||
combined = None
|
||||
if self.multi_episode or self.season_type == "alternate":
|
||||
if self.all_seasons:
|
||||
combined = client.get_all_episodes(
|
||||
self.series_id,
|
||||
season_type="alternate",
|
||||
)
|
||||
else:
|
||||
combined = client.get_season_episodes(
|
||||
self.series_id,
|
||||
self.season,
|
||||
season_type="alternate",
|
||||
)
|
||||
|
||||
if self.season_type == "alternate":
|
||||
episodes = official
|
||||
|
||||
if not episodes and not combined:
|
||||
label = "all seasons" if self.all_seasons else f"season {self.season}"
|
||||
raise TvdbError(f"No episodes found for {label}")
|
||||
mapping, unmatched, notes = match_filenames_to_episodes(
|
||||
self.filenames,
|
||||
episodes,
|
||||
season_filter=season_filter,
|
||||
official_episodes=official,
|
||||
combined_episodes=combined,
|
||||
)
|
||||
self.finished.emit(mapping, unmatched, notes)
|
||||
except TvdbError as e:
|
||||
@@ -434,6 +465,15 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
|
||||
self.padding.valueChanged.connect(self._emit)
|
||||
layout.addRow("Zero-pad width:", self.padding)
|
||||
|
||||
self.multi_episode_cb = QCheckBox("Multi-episode files (Jellyfin S01E01-E02)")
|
||||
self.multi_episode_cb.setChecked(True)
|
||||
self.multi_episode_cb.setToolTip(
|
||||
"Match combined-order titles from TheTVDB and rename using aired episode ranges, "
|
||||
"e.g. two episodes in one file becomes S01E01-E02."
|
||||
)
|
||||
self.multi_episode_cb.toggled.connect(self._emit)
|
||||
layout.addRow(self.multi_episode_cb)
|
||||
|
||||
self.match_btn = QPushButton("Match titles from file list")
|
||||
self.match_btn.clicked.connect(self._match_titles)
|
||||
layout.addRow(self.match_btn)
|
||||
@@ -443,7 +483,8 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
|
||||
layout.addRow(self.status)
|
||||
|
||||
info = QLabel(
|
||||
"Reads titles from S01E05 - Episode Title or Show 04x01 Episode Title filenames. "
|
||||
"Reads titles from S01E05 - Episode Title, S01E05-E06 (multi-episode), "
|
||||
"or Show 04x01 Episode Title filenames. "
|
||||
"Use All seasons to match across every season and fix wrong season numbers too."
|
||||
)
|
||||
info.setWordWrap(True)
|
||||
@@ -519,6 +560,7 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
|
||||
self._order_value(),
|
||||
self._file_names,
|
||||
all_seasons,
|
||||
self.multi_episode_cb.isChecked(),
|
||||
)
|
||||
self._match_worker.finished.connect(self._on_match_finished)
|
||||
self._match_worker.failed.connect(self._on_match_failed)
|
||||
@@ -542,11 +584,17 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
|
||||
self.status.setText(f"Match failed: {message}")
|
||||
|
||||
def getRule(self) -> TvdbEpisodeRenumberRule:
|
||||
mapping: dict = {}
|
||||
for k, v in self._episode_mapping.items():
|
||||
if hasattr(v, "season"):
|
||||
if getattr(v, "episode_end", None) and v.episode_end > v.episode:
|
||||
mapping[k] = (v.season, v.episode, v.episode_end)
|
||||
else:
|
||||
mapping[k] = (v.season, v.episode)
|
||||
else:
|
||||
mapping[k] = v
|
||||
r = TvdbEpisodeRenumberRule(
|
||||
episode_mapping={
|
||||
k: (v.season, v.episode) if hasattr(v, "season") else v
|
||||
for k, v in self._episode_mapping.items()
|
||||
},
|
||||
episode_mapping=mapping,
|
||||
padding=self.padding.value(),
|
||||
)
|
||||
r.enabled = self.enabled_cb.isChecked()
|
||||
|
||||
Reference in New Issue
Block a user