From f1420cac5823d53abd1bdb03f92b7e93e620c6e7 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Thu, 25 Sep 2025 09:40:26 -0400 Subject: [PATCH] fix: Add title extraction directly to create_episode_nfo method - Add _extract_title_from_filename method to NFOManager class - Integrate title extraction fallback in create_episode_nfo method - Extract titles when NFO lacks title element or has empty title - Support both .mkv and .mp4 files with comprehensive logging - Fix applied to actual NFO creation method being used by episodes Resolves title extraction by working in the correct code path rather than relying on metadata processing that wasn't being called. --- core/nfo_manager.py | 70 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 94ade70..d16e213 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -248,6 +248,59 @@ class NFOManager: print(f"⚠️ Error enhancing NFO with title: {e}") return False + def _extract_title_from_filename(self, season_dir: Path, season_num: int, episode_num: int) -> Optional[str]: + """Extract episode title from video filename as fallback when metadata doesn't provide it""" + try: + import re + # Look for video files matching this episode + season_pattern = f"S{season_num:02d}E{episode_num:02d}" + print(f"🔍 Searching for title in files for {season_pattern} in directory: {season_dir}") + + for video_file in season_dir.glob("*.mkv"): + filename = video_file.name + print(f"🔍 Checking file: {filename}") + if season_pattern in filename: + print(f"✅ Found matching file: {filename}") + # Pattern: SeriesName-S01E01-Episode Title[WEBDL-1080p][AAC2.0][h264].mkv + # Extract the part between season/episode and the first bracket + pattern = rf'{season_pattern}-(.*?)\[' + print(f"🔍 Using regex pattern: {pattern}") + match = re.search(pattern, filename) + if match: + title = match.group(1).strip() + print(f"🔍 Raw extracted title: '{title}'") + # Clean up common encoding artifacts and separators + title = title.replace('-', ' ').strip() + if title: + print(f"✅ Extracted title from filename: '{title}' for {season_pattern}") + return title + else: + print(f"⚠️ Title was empty after cleanup") + else: + print(f"⚠️ Regex pattern didn't match filename") + + # Also check .mp4 files + for video_file in season_dir.glob("*.mp4"): + filename = video_file.name + print(f"🔍 Checking .mp4 file: {filename}") + if season_pattern in filename: + print(f"✅ Found matching .mp4 file: {filename}") + pattern = rf'{season_pattern}-(.*?)\[' + match = re.search(pattern, filename) + if match: + title = match.group(1).strip() + print(f"🔍 Raw extracted title from .mp4: '{title}'") + title = title.replace('-', ' ').strip() + if title: + print(f"✅ Extracted title from .mp4 filename: '{title}' for {season_pattern}") + return title + + except Exception as e: + print(f"⚠️ Error extracting title from filename for S{season_num:02d}E{episode_num:02d}: {e}") + + print(f"⚠️ No title found in filenames for {season_pattern}") + return None + def _parse_nfo_with_tolerance(self, nfo_path: Path): """Parse NFO file with tolerance for URLs appended after XML""" try: @@ -643,6 +696,23 @@ class NFOManager: runtime_elem = ET.SubElement(episode, "runtime") runtime_elem.text = str(enhanced_metadata["runtime"]) + # Fallback: Extract title from filename if no title present + if not episode.find("title") or not episode.find("title").text: + print(f"🔍 No title found in NFO for S{season_num:02d}E{episode_num:02d}, attempting filename extraction") + filename_title = self._extract_title_from_filename(season_dir, season_num, episode_num) + if filename_title: + # Remove existing empty title element if present + existing_title = episode.find("title") + if existing_title is not None: + episode.remove(existing_title) + + # Add new title element + title_elem = ET.SubElement(episode, "title") + title_elem.text = filename_title + print(f"✅ Added filename-extracted title to NFO: S{season_num:02d}E{episode_num:02d} - '{filename_title}'") + else: + print(f"⚠️ Could not extract title from filename for S{season_num:02d}E{episode_num:02d}") + # Add NFOGuard fields at the bottom # Basic episode info at the end