Fix Simple Mapping Windows path handling with double backslashes
- Normalize Windows paths by replacing \\\\ with \\ before pattern matching - Escape backslashes in replacement strings for post-processing - Add debug logging to help diagnose path matching issues Root cause: UI stored escaped paths (\\\\Koha9-Main\\\\Music) but playlist content uses single backslashes (\\Koha9-Main\\Music). Now normalizes paths before compiling regex patterns. Co-authored-by: Koha9 <36852125+Koha9@users.noreply.github.com>
This commit is contained in:
@@ -683,34 +683,58 @@ def _compile_simple_mapping_rules(simple_mappings: list[dict]) -> CompiledRegexR
|
|||||||
logger.warning(f"Skipping mapping with missing cloud path: {mapping}")
|
logger.warning(f"Skipping mapping with missing cloud path: {mapping}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Normalize Windows paths: Replace double backslashes with single backslashes
|
||||||
|
# This handles cases where users enter escaped paths like \\Koha9-Main\\Music
|
||||||
|
# when the actual playlist content uses \Koha9-Main\Music
|
||||||
|
original_local = local_path
|
||||||
|
original_cloud = cloud_path
|
||||||
|
local_path = local_path.replace("\\\\", "\\")
|
||||||
|
cloud_path = cloud_path.replace("\\\\", "\\")
|
||||||
|
|
||||||
|
if local_path != original_local or cloud_path != original_cloud:
|
||||||
|
logger.info(f"Normalized Windows paths:")
|
||||||
|
logger.info(f" Local: {repr(original_local)} -> {repr(local_path)}")
|
||||||
|
logger.info(f" Cloud: {repr(original_cloud)} -> {repr(cloud_path)}")
|
||||||
|
|
||||||
# Create a unique placeholder using the validated UUID
|
# Create a unique placeholder using the validated UUID
|
||||||
# Using special markers to prevent conflicts with actual paths
|
# Using special markers to prevent conflicts with actual paths
|
||||||
placeholder = f"__MAPPING__{mapping_id}__"
|
placeholder = f"__MAPPING__{mapping_id}__"
|
||||||
|
|
||||||
|
# Debug logging for path mapping
|
||||||
|
logger.debug(f"Simple mapping pair:")
|
||||||
|
logger.debug(f" Local path (search): {repr(local_path)}")
|
||||||
|
logger.debug(f" Cloud path (replace): {repr(cloud_path)}")
|
||||||
|
logger.debug(f" Placeholder: {placeholder}")
|
||||||
|
|
||||||
# Pre-processing rules (use re.escape to treat paths as literal strings)
|
# Pre-processing rules (use re.escape to treat paths as literal strings)
|
||||||
# local_pre: Replace local path with placeholder
|
# local_pre: Replace local path with placeholder
|
||||||
|
local_pattern = re.escape(local_path)
|
||||||
|
logger.debug(f" Local pre pattern: {repr(local_pattern)}")
|
||||||
local_pre_rules.append({
|
local_pre_rules.append({
|
||||||
"pattern": re.escape(local_path),
|
"pattern": local_pattern,
|
||||||
"replacement": placeholder
|
"replacement": placeholder
|
||||||
})
|
})
|
||||||
|
|
||||||
# remote_pre: Replace cloud path with placeholder
|
# remote_pre: Replace cloud path with placeholder
|
||||||
|
remote_pattern = re.escape(cloud_path)
|
||||||
|
logger.debug(f" Remote pre pattern: {repr(remote_pattern)}")
|
||||||
remote_pre_rules.append({
|
remote_pre_rules.append({
|
||||||
"pattern": re.escape(cloud_path),
|
"pattern": remote_pattern,
|
||||||
"replacement": placeholder
|
"replacement": placeholder
|
||||||
})
|
})
|
||||||
|
|
||||||
# Post-processing rules
|
# Post-processing rules
|
||||||
# local_post: Replace placeholder with local path
|
# local_post: Replace placeholder with local path
|
||||||
|
# Note: In regex replacement, backslashes need to be escaped
|
||||||
local_post_rules.append({
|
local_post_rules.append({
|
||||||
"pattern": re.escape(placeholder),
|
"pattern": re.escape(placeholder),
|
||||||
"replacement": local_path
|
"replacement": local_path.replace("\\", "\\\\")
|
||||||
})
|
})
|
||||||
|
|
||||||
# remote_post: Replace placeholder with cloud path
|
# remote_post: Replace placeholder with cloud path
|
||||||
remote_post_rules.append({
|
remote_post_rules.append({
|
||||||
"pattern": re.escape(placeholder),
|
"pattern": re.escape(placeholder),
|
||||||
"replacement": cloud_path
|
"replacement": cloud_path.replace("\\", "\\\\")
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info(f"Compiled {len(local_pre_rules)} simple mapping pairs into rules")
|
logger.info(f"Compiled {len(local_pre_rules)} simple mapping pairs into rules")
|
||||||
@@ -803,13 +827,19 @@ def sync_all_playlists(
|
|||||||
# Apply pre-processing rules for REGEX or SIMPLE mode
|
# Apply pre-processing rules for REGEX or SIMPLE mode
|
||||||
# base_text doesn't need pre-processing as it's the normalized state
|
# base_text doesn't need pre-processing as it's the normalized state
|
||||||
if local_text is not None and compiled_rules.local_pre:
|
if local_text is not None and compiled_rules.local_pre:
|
||||||
|
logger.debug(f"Applying local_pre rules to playlist: {playlist}")
|
||||||
|
logger.debug(f" Before preprocessing (first 200 chars): {repr(local_text[:200])}")
|
||||||
local_text = preprocess_playlist_text(
|
local_text = preprocess_playlist_text(
|
||||||
local_text, [], compiled_rules.local_pre
|
local_text, [], compiled_rules.local_pre
|
||||||
)
|
)
|
||||||
|
logger.debug(f" After preprocessing (first 200 chars): {repr(local_text[:200])}")
|
||||||
if remote_text and compiled_rules.remote_pre:
|
if remote_text and compiled_rules.remote_pre:
|
||||||
|
logger.debug(f"Applying remote_pre rules to playlist: {playlist}")
|
||||||
|
logger.debug(f" Before preprocessing (first 200 chars): {repr(remote_text[:200])}")
|
||||||
remote_text = preprocess_playlist_text(
|
remote_text = preprocess_playlist_text(
|
||||||
remote_text, [], compiled_rules.remote_pre
|
remote_text, [], compiled_rules.remote_pre
|
||||||
)
|
)
|
||||||
|
logger.debug(f" After preprocessing (first 200 chars): {repr(remote_text[:200])}")
|
||||||
elif legacy_compiled_rules:
|
elif legacy_compiled_rules:
|
||||||
# Use legacy preprocessing for all texts
|
# Use legacy preprocessing for all texts
|
||||||
base_text = preprocess_playlist_text(
|
base_text = preprocess_playlist_text(
|
||||||
|
|||||||
Reference in New Issue
Block a user