This commit is contained in:
Tony Snearly 2025-11-05 00:30:53 -05:00 committed by GitHub
commit 2df8e138be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -36,6 +36,7 @@ import functools
import io import io
import math import math
import os import os
import re
import requests import requests
from collections import defaultdict from collections import defaultdict
from plexapi.server import PlexServer from plexapi.server import PlexServer
@ -68,6 +69,8 @@ def group_episodes(plex, library, show, renumber, composite_thumb):
for season in show.seasons(): for season in show.seasons():
groups = defaultdict(list) groups = defaultdict(list)
titles_toMerge = []
titlesSort_toMerge = []
startIndex = None startIndex = None
for episode in season.episodes(): for episode in season.episodes():
@ -76,19 +79,22 @@ def group_episodes(plex, library, show, renumber, composite_thumb):
startIndex = episode.index startIndex = episode.index
for index, (first, *episodes) in enumerate(groups.values(), start=startIndex): for index, (first, *episodes) in enumerate(groups.values(), start=startIndex):
title = first.title + ' / ' titles_toMerge = [first.title]
titleSort = first.titleSort + ' / ' titlesSort_toMerge = [first.titleSort]
summary = first.summary + '\n\n' summary = [first.summary]
writers = [] writers = []
directors = [] directors = []
for episode in episodes: for episode in episodes:
title += episode.title + ' / ' titles_toMerge.append(episode.title)
titleSort += episode.titleSort + ' / ' titlesSort_toMerge.append(episode.titleSort)
summary += episode.summary + '\n\n' summary.append(episode.summary)
writers.extend([writer.tag for writer in episode.writers]) writers.extend([writer.tag for writer in episode.writers])
directors.extend([director.tag for director in episode.directors]) directors.extend([director.tag for director in episode.directors])
writers = list(set(writers))
directors = list(set(directors))
if episodes: if episodes:
if composite_thumb: if composite_thumb:
firstImgFile = download_image( firstImgFile = download_image(
@ -103,9 +109,9 @@ def group_episodes(plex, library, show, renumber, composite_thumb):
merge(first, episodes) merge(first, episodes)
first.batchEdits() \ first.batchEdits() \
.editTitle(title[:-3]) \ .editTitle(merge_titles(titles_toMerge)) \
.editSortTitle(titleSort[:-3]) \ .editSortTitle(merge_titles(titlesSort_toMerge)) \
.editSummary(summary[:-2]) \ .editSummary('\n\n'.join(summary) \
.editContentRating(first.contentRating) \ .editContentRating(first.contentRating) \
.editOriginallyAvailable(first.originallyAvailableAt) \ .editOriginallyAvailable(first.originallyAvailableAt) \
.addWriter(writers) \ .addWriter(writers) \
@ -118,7 +124,41 @@ def group_episodes(plex, library, show, renumber, composite_thumb):
first.saveEdits() 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): def merge(first, episodes):
if not episodes:
return
key = '%s/merge?ids=%s' % (first.key, ','.join([str(r.ratingKey) for r in episodes])) key = '%s/merge?ids=%s' % (first.key, ','.join([str(r.ratingKey) for r in episodes]))
first._server.query(key, method=first._server._session.put) 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('--library', required=True)
parser.add_argument('--show', required=True) parser.add_argument('--show', required=True)
parser.add_argument('--renumber', action='store_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() opts = parser.parse_args()
if opts.composite_thumb and not hasPIL: if opts.composite_thumb and not hasPIL:
print('PIL is not installed. Please install `pillow` to create composite thumbnails.') print('PIL is not installed. Please install `pillow` to create composite thumbnails.')
exit(1) 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) plex = PlexServer(PLEX_URL, PLEX_TOKEN)
group_episodes(plex, **vars(opts)) group_episodes(plex, **vars(opts))