From 831fa10ce50958d1a91a28e8cc54aa8fd90c61b7 Mon Sep 17 00:00:00 2001 From: kenzuyaa Date: Tue, 26 Aug 2025 21:35:22 +0700 Subject: [PATCH] feat(dl): enhance episode folder structure and rename series folders - Create nested folder structure for episodes: {title}/Season {season:02}/{filename} - Keep existing folder naming for songs unchanged - Modify episode folder naming to only show title for main folder - Adjust episode file naming format to include separators and sanitization - Add get_season_folder() method returning 'Season XX' for episodes - Disable series year inclusion in folder and episode naming by default in config - Comment out additional service and audio language tags in episode naming code --- unshackle/commands/dl.py | 8 +++++++- unshackle/core/titles/episode.py | 34 +++++++++++++++++++------------- unshackle/unshackle.yaml | 2 +- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/unshackle/commands/dl.py b/unshackle/commands/dl.py index 72752fc..91300ca 100644 --- a/unshackle/commands/dl.py +++ b/unshackle/commands/dl.py @@ -1159,7 +1159,13 @@ class dl: final_filename = title.get_filename(media_info, show_service=not no_source) if not no_folder and isinstance(title, (Episode, Song)): - final_dir /= title.get_filename(media_info, show_service=not no_source, folder=True) + if isinstance(title, Episode): + # Create nested structure: {title}/Season {season:02}/{filename} + final_dir /= title.get_filename(media_info, show_service=not no_source, folder=True) + final_dir /= title.get_season_folder() + else: + # For Song, use existing logic + final_dir /= title.get_filename(media_info, show_service=not no_source, folder=True) final_dir.mkdir(parents=True, exist_ok=True) final_path = final_dir / f"{final_filename}{muxed_path.suffix}" diff --git a/unshackle/core/titles/episode.py b/unshackle/core/titles/episode.py index f66bcb7..ec69499 100644 --- a/unshackle/core/titles/episode.py +++ b/unshackle/core/titles/episode.py @@ -92,14 +92,14 @@ class Episode(Title): primary_audio_track = next(iter(media_info.audio_tracks), None) unique_audio_languages = len({x.language.split("-")[0] for x in media_info.audio_tracks if x.language}) - # Title [Year] SXXEXX Name (or Title [Year] SXX if folder) + # Title [Year] SXXEXX Name (or just Title for main folder) if folder: name = f"{self.title}" if self.year and config.series_year: - name += f" {self.year}" - name += f" S{self.season:02}" + name += f" ({self.year})" + return name else: - name = "{title}{year} S{season:02}E{number:02} {name}".format( + name = "{title}{year} S{season:02}E{number:02} - {name} -".format( title=self.title.replace("$", "S"), # e.g., Arli$$ year=f" {self.year}" if self.year and config.series_year else "", season=self.season, @@ -128,19 +128,19 @@ class Episode(Title): name += f" {resolution}p" # Service - if show_service: - name += f" {self.service.__name__}" + # if show_service: + # name += f" {self.service.__name__}" - # 'WEB-DL' - name += " WEB-DL" + # # 'WEB-DL' + # name += " WEB-DL" - # DUAL - if unique_audio_languages == 2: - name += " DUAL" + # # DUAL + # if unique_audio_languages == 2: + # name += " DUAL" - # MULTi - if unique_audio_languages > 2: - name += " MULTi" + # # MULTi + # if unique_audio_languages > 2: + # name += " MULTi" # Audio Codec + Channels (+ feature) if primary_audio_track: @@ -189,6 +189,12 @@ class Episode(Title): # Simple naming style without technical details - use spaces instead of dots return sanitize_filename(name, " ") + def get_season_folder(self) -> str: + """ + Get the season folder name in the format 'Season XX'. + """ + return f"Season {self.season:02d}" + class Series(SortedKeyList, ABC): def __init__(self, iterable: Optional[Iterable] = None): diff --git a/unshackle/unshackle.yaml b/unshackle/unshackle.yaml index a4c0e6d..cb719da 100644 --- a/unshackle/unshackle.yaml +++ b/unshackle/unshackle.yaml @@ -18,7 +18,7 @@ scene_naming: true # Whether to include the year in series names for episodes and folders (default: true) # true for style - Show Name (2023) S01E01 Episode Name # false for style - Show Name S01E01 Episode Name -series_year: true +series_year: false # Check for updates from GitHub repository on startup (default: true) update_checks: true