diff --git a/utility/merge_multiepisodes.py b/utility/merge_multiepisodes.py index 7ae93b4..608920d 100644 --- a/utility/merge_multiepisodes.py +++ b/utility/merge_multiepisodes.py @@ -36,6 +36,7 @@ import functools import io import math import os +import re import requests from collections import defaultdict from plexapi.server import PlexServer @@ -68,6 +69,8 @@ def group_episodes(plex, library, show, renumber, composite_thumb): for season in show.seasons(): groups = defaultdict(list) + titles_toMerge = [] + titlesSort_toMerge = [] startIndex = None for episode in season.episodes(): @@ -76,19 +79,22 @@ def group_episodes(plex, library, show, renumber, composite_thumb): startIndex = episode.index for index, (first, *episodes) in enumerate(groups.values(), start=startIndex): - title = first.title + ' / ' - titleSort = first.titleSort + ' / ' - summary = first.summary + '\n\n' + titles_toMerge = [first.title] + titlesSort_toMerge = [first.titleSort] + summary = [first.summary] writers = [] directors = [] for episode in episodes: - title += episode.title + ' / ' - titleSort += episode.titleSort + ' / ' - summary += episode.summary + '\n\n' + titles_toMerge.append(episode.title) + titlesSort_toMerge.append(episode.titleSort) + summary.append(episode.summary) writers.extend([writer.tag for writer in episode.writers]) directors.extend([director.tag for director in episode.directors]) + writers = list(set(writers)) + directors = list(set(directors)) + if episodes: if composite_thumb: firstImgFile = download_image( @@ -103,9 +109,9 @@ def group_episodes(plex, library, show, renumber, composite_thumb): merge(first, episodes) first.batchEdits() \ - .editTitle(title[:-3]) \ - .editSortTitle(titleSort[:-3]) \ - .editSummary(summary[:-2]) \ + .editTitle(merge_titles(titles_toMerge)) \ + .editSortTitle(merge_titles(titlesSort_toMerge)) \ + .editSummary('\n\n'.join(summary) \ .editContentRating(first.contentRating) \ .editOriginallyAvailable(first.originallyAvailableAt) \ .addWriter(writers) \ @@ -118,7 +124,41 @@ def group_episodes(plex, library, show, renumber, composite_thumb): first.saveEdits() +# Regex pattern to match episode part indicators in titles. +# Matches: +# - partX, ptX, cdX, discX, diskX, dvdX (where X is a number, with optional space) +# - (X) (where X is a number in parentheses) +MERGE_TITLE_PATTERN = r'(\b(part|pt|cd|disc|disk|dvd)\s?\d+|\(\d+\))' + +def merge_titles(titles): + merged_titles = [] + base_title = None + + for title in titles: + # Check if the title contains any of the specified patterns (case insensitive) + match = re.search(MERGE_TITLE_PATTERN, title, re.IGNORECASE) + + if match: + # If base title is not yet set, extract it from the first part (ignore case) + if base_title is None: + base_title = re.sub(MERGE_TITLE_PATTERN, '', title, flags=re.IGNORECASE).strip() + else: + # If the current title doesn't match part patterns and we have a base title, merge it + if base_title: + merged_titles.append(base_title) # Append base title once + base_title = None # Reset base title for next potential title + merged_titles.append(title) + + # If the last title was part of a merged series, add the base title at the end + if base_title: + merged_titles.append(base_title) + + return " | ".join(merged_titles) + + def merge(first, episodes): + if not episodes: + return key = '%s/merge?ids=%s' % (first.key, ','.join([str(r.ratingKey) for r in episodes])) first._server.query(key, method=first._server._session.put) @@ -205,12 +245,16 @@ if __name__ == '__main__': parser.add_argument('--library', required=True) parser.add_argument('--show', required=True) parser.add_argument('--renumber', action='store_true') - parser.add_argument('--composite_thumb', action='store_true') + parser.add_argument('--composite-thumb', action='store_true') opts = parser.parse_args() if opts.composite_thumb and not hasPIL: print('PIL is not installed. Please install `pillow` to create composite thumbnails.') exit(1) + if not PLEX_URL or not PLEX_TOKEN: + print('Please set PLEX_URL and PLEX_TOKEN environment variables or edit the script to include your Plex server URL and token.') + exit(1) + plex = PlexServer(PLEX_URL, PLEX_TOKEN) group_episodes(plex, **vars(opts))