| """ |
| Path Validation Module |
| Validates and suggests safe alternatives for problematic file paths |
| """ |
|
|
| import re |
| from pathlib import Path |
| from typing import Tuple, List |
|
|
| def detect_problematic_characters(path_name: str) -> List[str]: |
| """Detect problematic characters in a path name""" |
| problematic_chars = [] |
| |
| |
| if "'" in path_name: |
| problematic_chars.append("apostrophe (')") |
| |
| |
| if '"' in path_name: |
| problematic_chars.append('double quote (")') |
| |
| |
| problematic_patterns = { |
| r'[<>|?*]': 'invalid filesystem characters', |
| r'[\x00-\x1f]': 'control characters', |
| r'\\': 'backslashes (Windows path separators)', |
| } |
| |
| for pattern, description in problematic_patterns.items(): |
| if re.search(pattern, path_name): |
| problematic_chars.append(description) |
| |
| return problematic_chars |
|
|
| def suggest_safe_path(path_name: str) -> str: |
| """Suggest a safe alternative for a problematic path name""" |
| from modules.file_manager import sanitize_filename |
| return sanitize_filename(path_name) |
|
|
| def validate_book_path(book_name: str) -> Tuple[bool, str, str]: |
| """ |
| Validate a book name for path safety |
| |
| Returns: |
| (is_safe, warning_message, suggested_name) |
| """ |
| problematic_chars = detect_problematic_characters(book_name) |
| |
| if not problematic_chars: |
| return True, "", book_name |
| |
| suggested_name = suggest_safe_path(book_name) |
| |
| |
| char_list = ", ".join(problematic_chars) |
| warning_msg = f"⚠️ Path contains problematic characters: {char_list}\n\n" |
| warning_msg += f"This may cause:\n" |
| warning_msg += f"• FFmpeg concatenation failures\n" |
| warning_msg += f"• File system compatibility issues\n" |
| warning_msg += f"• Audio processing errors\n\n" |
| warning_msg += f"Suggested safe name: '{suggested_name}'" |
| |
| return False, warning_msg, suggested_name |
|
|
| def validate_and_create_audiobook_path(book_name: str, force_safe: bool = False) -> Tuple[Path, str]: |
| """ |
| Validate book name and create safe audiobook path |
| |
| Args: |
| book_name: Original book name from user input |
| force_safe: If True, automatically use safe name without asking |
| |
| Returns: |
| (safe_audiobook_path, actual_name_used) |
| """ |
| from config.config import AUDIOBOOK_ROOT |
| |
| is_safe, warning, suggested_name = validate_book_path(book_name) |
| |
| if is_safe or force_safe: |
| final_name = suggested_name if force_safe and not is_safe else book_name |
| audiobook_path = AUDIOBOOK_ROOT / final_name |
| return audiobook_path, final_name |
| else: |
| |
| suggested_path = AUDIOBOOK_ROOT / suggested_name |
| return suggested_path, suggested_name |
|
|
| def check_existing_audiobook_paths() -> List[Tuple[str, str, str]]: |
| """ |
| Check existing audiobook directories for problematic paths |
| |
| Returns: |
| List of (original_name, suggested_name, issues) tuples |
| """ |
| from config.config import AUDIOBOOK_ROOT |
| |
| problematic_books = [] |
| |
| if not AUDIOBOOK_ROOT.exists(): |
| return problematic_books |
| |
| for book_dir in AUDIOBOOK_ROOT.iterdir(): |
| if book_dir.is_dir(): |
| book_name = book_dir.name |
| problematic_chars = detect_problematic_characters(book_name) |
| |
| if problematic_chars: |
| suggested_name = suggest_safe_path(book_name) |
| issues = ", ".join(problematic_chars) |
| problematic_books.append((book_name, suggested_name, issues)) |
| |
| return problematic_books |
|
|
| |
| def format_path_warning_html(book_name: str) -> str: |
| """Format path validation warning as HTML for Gradio""" |
| is_safe, warning, suggested = validate_book_path(book_name) |
| |
| if is_safe: |
| return f'<span style="color: green;">✅ Path is safe: "{book_name}"</span>' |
| else: |
| html = f'<div style="color: orange; background: #fff3cd; padding: 10px; border-radius: 5px; border: 1px solid #ffeaa7;">' |
| html += f'<strong>⚠️ Problematic Path Detected</strong><br>' |
| html += f'<strong>Original:</strong> "{book_name}"<br>' |
| html += f'<strong>Suggested:</strong> "{suggested}"<br><br>' |
| html += f'<strong>Issues Found:</strong><br>' |
| |
| for char in detect_problematic_characters(book_name): |
| html += f'• {char}<br>' |
| |
| html += f'<br><em>Tip: Use the suggested name to avoid audio processing errors.</em>' |
| html += f'</div>' |
| return html |
|
|
| def format_path_warning_text(book_name: str) -> str: |
| """Format path validation warning as plain text""" |
| is_safe, warning, suggested = validate_book_path(book_name) |
| |
| if is_safe: |
| return f'✅ Path is safe: "{book_name}"' |
| else: |
| return f'⚠️ PROBLEMATIC PATH: "{book_name}"\nSUGGESTED: "{suggested}"\n\n{warning}' |