182 lines
6.3 KiB
Python
182 lines
6.3 KiB
Python
import os
|
|
|
|
import yaml
|
|
|
|
from utils.file_utils import iterate_json_files
|
|
from utils.strings import get_name
|
|
|
|
|
|
regex_patterns = {
|
|
"by_name": {},
|
|
"by_pattern": {},
|
|
}
|
|
|
|
|
|
def _update_existing_pattern_for_service(existing_data, service, output_dir):
|
|
"""Update an existing pattern file to support multiple services."""
|
|
existing_path = existing_data["file_path"]
|
|
if not os.path.exists(existing_path):
|
|
raise FileNotFoundError(f"Expected pattern file not found: {existing_path}")
|
|
|
|
# Use the existing pattern name to preserve casing
|
|
existing_name = existing_data["name"]
|
|
|
|
# Remove service prefix if present (e.g., "Radarr - " or "Sonarr - ")
|
|
# to get the base pattern name with original casing
|
|
for svc in ["Radarr", "Sonarr"]:
|
|
prefix = f"{svc} - "
|
|
if existing_name.startswith(prefix):
|
|
existing_name = existing_name[len(prefix):]
|
|
break
|
|
|
|
# Use the existing (original) name with preserved casing
|
|
safe_name = existing_name
|
|
new_path = os.path.join(output_dir, f"{safe_name}.yml")
|
|
|
|
# Rename file if needed (from service-specific to generic name)
|
|
if existing_path != new_path and not os.path.exists(new_path):
|
|
os.rename(existing_path, new_path)
|
|
# Update tracking data
|
|
old_name = existing_data["name"]
|
|
existing_data["file_path"] = new_path
|
|
existing_data["name"] = safe_name
|
|
# Update by_name dict with normalized keys
|
|
old_key = old_name.lower()
|
|
if old_key in regex_patterns["by_name"]:
|
|
regex_patterns["by_name"].pop(old_key)
|
|
regex_patterns["by_name"][safe_name.lower()] = existing_data
|
|
|
|
# Update the file to add the new service tag
|
|
with open(new_path, "r+", encoding="utf-8") as f:
|
|
yml_data = yaml.safe_load(f)
|
|
if "tags" not in yml_data:
|
|
yml_data["tags"] = []
|
|
if service.capitalize() not in yml_data["tags"]:
|
|
yml_data["tags"].append(service.capitalize())
|
|
yml_data["name"] = safe_name
|
|
f.seek(0)
|
|
yaml.dump(yml_data, f, sort_keys=False, allow_unicode=True)
|
|
f.truncate()
|
|
|
|
# Update services list in tracking
|
|
if service.capitalize() not in existing_data["services"]:
|
|
existing_data["services"].append(service.capitalize())
|
|
|
|
# Update name in tracking data
|
|
existing_data["name"] = safe_name
|
|
|
|
print(f"Updated pattern for multiple services: {new_path}")
|
|
|
|
|
|
def _generate_unique_pattern_name(initial_name, pattern, output_dir):
|
|
"""Generate a unique pattern name if there are collisions."""
|
|
final_name = initial_name
|
|
counter = 1
|
|
# Use lowercase for case-insensitive comparison
|
|
normalized_key = final_name.lower()
|
|
|
|
while normalized_key in regex_patterns["by_name"]:
|
|
existing_pattern_data = regex_patterns["by_name"][normalized_key]
|
|
if existing_pattern_data["pattern"] == pattern:
|
|
print(f"Pattern with same name and pattern already exists: {final_name}")
|
|
return None
|
|
final_name = f"{initial_name} ({counter})"
|
|
normalized_key = final_name.lower()
|
|
counter += 1
|
|
|
|
# Also check for case-insensitive file system collisions
|
|
while _case_insensitive_file_exists(output_dir, f"{final_name}.yml"):
|
|
final_name = f"{initial_name} ({counter})"
|
|
normalized_key = final_name.lower()
|
|
counter += 1
|
|
|
|
return final_name
|
|
|
|
|
|
def _case_insensitive_file_exists(directory, filename):
|
|
"""Check if a file exists with case-insensitive matching."""
|
|
if not os.path.exists(directory):
|
|
return False
|
|
|
|
filename_lower = filename.lower()
|
|
for existing_file in os.listdir(directory):
|
|
if existing_file.lower() == filename_lower:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _create_new_pattern_file(service, pattern, final_name, output_dir):
|
|
"""Create a new pattern file."""
|
|
yml_data = {
|
|
"name": final_name,
|
|
"pattern": pattern,
|
|
"description": "",
|
|
"tags": [service.capitalize()],
|
|
"tests": [],
|
|
}
|
|
|
|
output_path = os.path.join(output_dir, f"{final_name}.yml")
|
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
yaml.dump(yml_data, f, sort_keys=False, allow_unicode=True)
|
|
|
|
# Store in dict (twice - by name and by pattern)
|
|
# Use lowercase keys for case-insensitive lookups
|
|
pattern_data = {
|
|
"name": final_name,
|
|
"pattern": pattern,
|
|
"services": [service.capitalize()],
|
|
"file_path": output_path,
|
|
}
|
|
regex_patterns["by_name"][final_name.lower()] = pattern_data
|
|
regex_patterns["by_pattern"][pattern] = pattern_data
|
|
|
|
print(f"Generated: {output_path}")
|
|
return True
|
|
|
|
|
|
def _collect_regex_pattern(service, file_name, input_json, output_dir):
|
|
"""Extract and collect regex patterns from specifications."""
|
|
for spec in input_json.get("specifications", []):
|
|
implementation = spec.get("implementation")
|
|
if implementation not in [
|
|
"ReleaseTitleSpecification",
|
|
"ReleaseGroupSpecification",
|
|
]:
|
|
continue
|
|
|
|
pattern = spec.get("fields", {}).get("value")
|
|
if not pattern:
|
|
print(f"No pattern found in {file_name} for {implementation}")
|
|
continue
|
|
|
|
spec_name = spec.get("name", "")
|
|
|
|
# Check if this exact pattern was already seen
|
|
if pattern in regex_patterns["by_pattern"]:
|
|
existing_data = regex_patterns["by_pattern"][pattern]
|
|
|
|
# If previously seen for the same service with the same regex - Skip
|
|
if service.capitalize() in existing_data.get("services", []):
|
|
print(f"Pattern already exists for {service}: {spec_name}")
|
|
continue
|
|
|
|
# If previously seen for a different service - update to support both
|
|
_update_existing_pattern_for_service(
|
|
existing_data, service, output_dir
|
|
)
|
|
continue
|
|
|
|
# Pattern not seen before - check for name collisions
|
|
initial_name = get_name(service, spec_name, remove_not=True)
|
|
final_name = _generate_unique_pattern_name(initial_name, pattern, output_dir)
|
|
|
|
if final_name:
|
|
_create_new_pattern_file(service, pattern, final_name, output_dir)
|
|
|
|
|
|
def collect_regex_patterns(service, input_dir, output_dir):
|
|
for _, file_stem, data in iterate_json_files(input_dir):
|
|
_collect_regex_pattern(service, file_stem, data, output_dir)
|
|
|
|
return regex_patterns
|