From 24d94306c1342c1bea803975104200397d6e5ed6 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Thu, 25 Sep 2025 19:12:37 -0400 Subject: [PATCH] fix: remove file lock --- api/routes.py | 37 ++++++++++++- core/nfo_manager.py | 123 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 133 insertions(+), 27 deletions(-) diff --git a/api/routes.py b/api/routes.py index 74274a3..4d40f85 100644 --- a/api/routes.py +++ b/api/routes.py @@ -320,4 +320,39 @@ def register_routes( } except Exception as e: _log("ERROR", f"Optimized scan failed: {e}") - raise HTTPException(status_code=500, detail=f"Optimized scan failed: {str(e)}") \ No newline at end of file + raise HTTPException(status_code=500, detail=f"Optimized scan failed: {str(e)}") + + @app.post("/nfo/strip-lockdata") + async def strip_lockdata_from_library(background_tasks: BackgroundTasks): + """Strip lockdata elements from all NFO files for Emby compatibility""" + try: + _log("INFO", "Lockdata stripping initiated via API") + + def strip_lockdata_task(): + total_processed = 0 + + # Process TV libraries + for tv_path in config.tv_paths: + if tv_path.exists(): + _log("INFO", f"Stripping lockdata from TV library: {tv_path}") + processed = tv_processor.nfo_manager.strip_lockdata_from_directory(tv_path) + total_processed += processed + + # Process Movie libraries + for movie_path in config.movie_paths: + if movie_path.exists(): + _log("INFO", f"Stripping lockdata from movie library: {movie_path}") + processed = movie_processor.nfo_manager.strip_lockdata_from_directory(movie_path) + total_processed += processed + + _log("INFO", f"Lockdata stripping complete: {total_processed} NFO files processed") + + background_tasks.add_task(strip_lockdata_task) + return { + "status": "started", + "message": "Lockdata stripping initiated for Emby compatibility", + "note": "This will remove true from all NFO files" + } + except Exception as e: + _log("ERROR", f"Lockdata stripping failed: {e}") + raise HTTPException(status_code=500, detail=f"Lockdata stripping failed: {str(e)}") \ No newline at end of file diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 6e47cf2..9a187ba 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -138,11 +138,26 @@ class NFOManager: # Look for NFOGuard fields dateadded_elem = root.find('.//dateadded') premiered_elem = root.find('.//premiered') - lockdata_elem = root.find('.//lockdata') + # Strip any existing lockdata elements for Emby compatibility + lockdata_elements = root.findall('.//lockdata') + if lockdata_elements: + modified = False + for lockdata_elem in lockdata_elements: + # Find parent and remove the element + for parent in root.iter(): + if lockdata_elem in parent: + parent.remove(lockdata_elem) + modified = True + _log("DEBUG", f"Stripped lockdata element from {nfo_path.name}") + break + if modified: + # Write back the cleaned NFO + tree = ET.ElementTree(root) + ET.indent(tree, space=" ", level=0) + tree.write(nfo_path, encoding="utf-8", xml_declaration=True) - # Only consider it NFOGuard-managed if it has dateadded and lockdata - if (dateadded_elem is not None and dateadded_elem.text and - lockdata_elem is not None and lockdata_elem.text == "true"): + # Only consider it NFOGuard-managed if it has dateadded (lockdata no longer required) + if (dateadded_elem is not None and dateadded_elem.text): result = { "dateadded": dateadded_elem.text.strip(), @@ -176,11 +191,26 @@ class NFOManager: # Look for NFOGuard fields in episode NFO dateadded_elem = root.find('.//dateadded') aired_elem = root.find('.//aired') - lockdata_elem = root.find('.//lockdata') + # Strip any existing lockdata elements for Emby compatibility + lockdata_elements = root.findall('.//lockdata') + if lockdata_elements: + modified = False + for lockdata_elem in lockdata_elements: + # Find parent and remove the element + for parent in root.iter(): + if lockdata_elem in parent: + parent.remove(lockdata_elem) + modified = True + _log("DEBUG", f"Stripped lockdata element from {nfo_path.name}") + break + if modified: + # Write back the cleaned NFO + tree = ET.ElementTree(root) + ET.indent(tree, space=" ", level=0) + tree.write(nfo_path, encoding="utf-8", xml_declaration=True) - # Only consider it NFOGuard-managed if it has dateadded and lockdata - if (dateadded_elem is not None and dateadded_elem.text and - lockdata_elem is not None and lockdata_elem.text == "true"): + # Only consider it NFOGuard-managed if it has dateadded (lockdata no longer required) + if (dateadded_elem is not None and dateadded_elem.text): result = { "dateadded": dateadded_elem.text.strip(), @@ -350,7 +380,7 @@ class NFOManager: # Remove existing NFOGuard-managed elements to avoid duplicates # These will be re-added at the very bottom - nfoguard_fields = ["dateadded", "lockdata", "premiered", "year"] + nfoguard_fields = ["dateadded", "premiered", "year"] for tag in nfoguard_fields: existing = movie.find(tag) if existing is not None: @@ -410,11 +440,7 @@ class NFOManager: movie.append(dateadded_elem) print(f"✅ Added dateadded to NFO: {dateadded}") - # Add lockdata at the very end - if lock_metadata: - lockdata = ET.Element("lockdata") - lockdata.text = "true" - movie.append(lockdata) + # Note: lockdata element removed for Emby compatibility # Write file with proper formatting tree = ET.ElementTree(movie) @@ -476,9 +502,7 @@ class NFOManager: tvdb_uniqueid = ET.SubElement(tvshow, "uniqueid", type="tvdb") tvdb_uniqueid.text = tvdb_id - # Add lockdata at the very end - lockdata = ET.SubElement(tvshow, "lockdata") - lockdata.text = "true" + # Note: lockdata element removed for Emby compatibility # Add NFOGuard comment at the beginning comment_text = f" Created by {self.manager_brand} " @@ -521,7 +545,7 @@ class NFOManager: raise ValueError("Root element is not ") # Remove existing NFOGuard-managed elements - for tag in ["seasonnumber", "lockdata"]: + for tag in ["seasonnumber"]: existing = season.find(tag) if existing is not None: season.remove(existing) @@ -538,9 +562,7 @@ class NFOManager: seasonnumber = ET.SubElement(season, "seasonnumber") seasonnumber.text = str(season_number) - # Add lockdata at the end - lockdata = ET.SubElement(season, "lockdata") - lockdata.text = "true" + # Note: lockdata element removed for Emby compatibility # Add NFOGuard comment at the beginning comment_text = f" Created by {self.manager_brand} " @@ -653,7 +675,7 @@ class NFOManager: preserved_values = {} # Extract and preserve NFOGuard-managed fields (we'll re-add these at the bottom) - nfoguard_fields = ["aired", "dateadded", "lockdata", "season", "episode"] + nfoguard_fields = ["aired", "dateadded", "season", "episode"] for tag in nfoguard_fields: existing = episode.find(tag) if existing is not None: @@ -737,10 +759,7 @@ class NFOManager: dateadded_elem = ET.SubElement(episode, "dateadded") dateadded_elem.text = dateadded - # Add lockdata at the very end - if lock_metadata: - lockdata = ET.SubElement(episode, "lockdata") - lockdata.text = "true" + # Note: lockdata element removed for Emby compatibility # Add NFOGuard comment at the bottom with other NFOGuard fields nfoguard_comment = ET.Comment(f" Created by {self.manager_brand} - Source: {source} ") @@ -803,4 +822,56 @@ class NFOManager: print(f"✅ Updated {len(updated_files)} video file timestamps in {movie_dir.name}") else: print(f"⚠️ No video files found to update in {movie_dir.name}") + + def strip_lockdata_from_nfo(self, nfo_path: Path) -> bool: + """Strip existing lockdata elements from NFO file for Emby compatibility""" + if not nfo_path.exists(): + return False + + try: + tree = ET.parse(nfo_path) + root = tree.getroot() + + # Find and remove all lockdata elements + lockdata_elements = root.findall('.//lockdata') + if not lockdata_elements: + return False # No lockdata found, nothing to remove + + removed_count = 0 + for lockdata_elem in lockdata_elements: + # Find parent and remove the element + for parent in root.iter(): + if lockdata_elem in parent: + parent.remove(lockdata_elem) + removed_count += 1 + break + + if removed_count > 0: + # Write back the modified NFO + ET.indent(tree, space=" ", level=0) + tree.write(nfo_path, encoding="utf-8", xml_declaration=True) + _log("INFO", f"Removed {removed_count} lockdata elements from {nfo_path.name}") + return True + + except Exception as e: + _log("ERROR", f"Error stripping lockdata from {nfo_path}: {e}") + + return False + + def strip_lockdata_from_directory(self, directory_path: Path) -> int: + """Strip lockdata from all NFO files in a directory""" + if not directory_path.exists(): + return 0 + + processed_count = 0 + nfo_files = list(directory_path.glob("**/*.nfo")) + + _log("INFO", f"Scanning {len(nfo_files)} NFO files for lockdata removal in {directory_path}") + + for nfo_file in nfo_files: + if self.strip_lockdata_from_nfo(nfo_file): + processed_count += 1 + + _log("INFO", f"Stripped lockdata from {processed_count} NFO files in {directory_path}") + return processed_count