fix(media): a series is the name before the episode marker

Keyed on the whole episode title, every episode of a box set became a group of
one and never reached the five-episode size that calls something a series, so
the live run filed 124 Monk episodes as 124 movie folders, each named after its
own episode. ep_of now returns the series — the name before SxxEyy / 1x04 /
Part N — and the planner groups and names the quarantine pile by that series,
so one show is one pile: 'Monk - S05E03 - Mr. Monk and the Big Game' is series
monk. The threshold keeps its job: a lone film named Part 2 stays a film.

Re-planning the live census with this moves nothing differently except the 124
Monk files, which now join the TV pile: 168 episode files across 3 series.
This commit is contained in:
Frank committed 2026-10-30 20:12:05 -07:00
1 parent 1cb28095a7
commit 73f77a13c3
4 files changed
+84 -20

No files matched your search

+8 -9
View File
@@ -222,8 +222,9 @@ eps = collections.defaultdict(list)
for r in erows:
if r["path"] in marks | extras:
continue
if eng.ep_of(r["path"]):
eps[eng.key_of(r["title"])].append(r)
ep = eng.ep_of(r["path"])
if ep:
eps[ep[0]].append(r) # keyed by the SERIES, never the episode title
tv_keys = {k for k, v in eps.items() if len(v) >= 5}
tv_set = {r["path"] for k, v in eps.items() if k in tv_keys for r in v}
# A folder INSIDE a movie's folder is bonus territory even when its own name is
@@ -776,19 +777,17 @@ readmes.append(f"README in {ROOT}: this library was relaid out into Jellyfin's "
"one-folder-per-movie layout; `_release-group watermarks/`, "
"`_unsorted files/` and `_unsorted bonus/` hold files no film claims.")
for key in tv_keys:
nm = "_TV needs a TV library - " + collections.Counter(
r["title"] for r in eps[key]).most_common(1)[0][0]
readmes.append(f"README in {ROOT}/{sanitize(nm)}: these are TV episodes found in "
"the MOVIES library; move the folder to a TV library source, else "
"Jellyfin groups them wrongly as one movie.")
readmes.append(f"README in {ROOT}/{sanitize('_TV needs a TV library - ' + key)}: "
"these are TV episodes found in the MOVIES library; move the folder "
"to a TV library source, else Jellyfin groups them wrongly as one "
"movie.")
# --------------------------------------------------------------------- TV
for key in tv_keys:
items = eps[key]
title = collections.Counter(r["title"] for r in items).most_common(1)[0][0]
year = collections.Counter(r["year"] for r in items if r["year"] != "?")
yr = year.most_common(1)[0][0] if year else None
base = f"_TV needs a TV library - {sanitize(title)}" + (f" ({yr})" if yr else "")
base = f"_TV needs a TV library - {sanitize(key)}" + (f" ({yr})" if yr else "")
tops = {parts_of(r["path"])[0] for r in items}
members = sorted([r["path"] for r in items]
+ [q for q in extras | marks if len(parts_of(q)) > 1
+12 -3
View File
@@ -322,13 +322,22 @@ def key_of(title):
def ep_of(path):
"""(show, episode-token) when the basename carries SxxEyy / 1x04 / Part N."""
"""(series, episode-token) when the basename carries SxxEyy / 1x04 / Part N.
The series is the name BEFORE the marker. The box-set convention writes
"Monk - S05E03 - Mr. Monk and the Big Game", and the episode title that
follows the marker is not the show's name: keying on the whole title gave
every episode of one series its own group, so a 124-episode box set never
reached the size that calls something a series and each episode was filed
as its own film. A file named only by its marker has nothing before it, so
its whole title stands in."""
name = os.path.splitext(os.path.basename(path))[0]
m = EP_RE.search(name)
if not m:
return None
show, _ = title_of(path)
return show, norm(m.group(1)).replace(" ", "")
head = name[:m.start()].strip(" ._\u2013-")
series = title_of(head)[0] if head else None
return series or title_of(path)[0], norm(m.group(1)).replace(" ", "")
def base_words(title, key):