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