diff --git a/backend/app/arr/manager.py b/backend/app/arr/manager.py index 05cd791..d5d9605 100644 --- a/backend/app/arr/manager.py +++ b/backend/app/arr/manager.py @@ -382,6 +382,7 @@ def run_import_for_config(config_row): arr_type = config_row['type'] arr_server = config_row['arrServer'] api_key = config_row['apiKey'] + import_as_unique = config_row.get('import_as_unique', False) logger.info( f"[Pull Import] Running import for ARR config #{arr_id} ({arr_name})") @@ -396,6 +397,13 @@ def run_import_for_config(config_row): total_attempted = 0 total_successful = 0 + # Log import_as_unique setting + if import_as_unique: + logger.info(f"Unique imports for {arr_name} are on, adjusting names") + else: + logger.info( + f"Unique imports for {arr_name} are off, using original names") + # 1) Import user-selected custom formats if selected_formats: total_attempted += len(selected_formats) @@ -404,8 +412,20 @@ def run_import_for_config(config_row): ) try: from ..importarr.format import import_formats_to_arr + format_names = selected_formats + original_names = format_names.copy() + + # Modify format names if import_as_unique is true + if import_as_unique: + format_names = [ + f"{name} [Dictionarry]" for name in format_names + ] + logger.info( + f"Modified format names for unique import: {format_names}") + format_result = import_formats_to_arr( - format_names=selected_formats, + format_names=format_names, + original_names=original_names, base_url=arr_server, api_key=api_key, arr_type=arr_type) @@ -453,11 +473,22 @@ def run_import_for_config(config_row): total_attempted += len(referenced_cf_names) try: from ..importarr.format import import_formats_to_arr - cf_result = import_formats_to_arr( - format_names=list(referenced_cf_names), - base_url=arr_server, - api_key=api_key, - arr_type=arr_type) + format_names = list(referenced_cf_names) + original_names = format_names.copy() + + # Modify format names if import_as_unique is true + if import_as_unique: + format_names = [ + f"{name} [Dictionarry]" for name in format_names + ] + logger.info( + f"Modified format names for unique import: {format_names}") + + cf_result = import_formats_to_arr(format_names=format_names, + original_names=original_names, + base_url=arr_server, + api_key=api_key, + arr_type=arr_type) if cf_result.get('success'): total_successful += (cf_result.get('added', 0) + @@ -475,12 +506,26 @@ def run_import_for_config(config_row): total_attempted += len(selected_profiles) try: from ..importarr.profile import import_profiles_to_arr + profile_names = selected_profiles + original_names = profile_names.copy() + + # Modify profile names if import_as_unique is true + if import_as_unique: + profile_names = [ + f"{name} [Dictionarry]" for name in profile_names + ] + logger.info( + f"Modified profile names for unique import: {profile_names}" + ) + profile_result = import_profiles_to_arr( - profile_names=selected_profiles, + profile_names=profile_names, + original_names=original_names, base_url=arr_server, api_key=api_key, arr_type=arr_type, - arr_id=arr_id) + arr_id=arr_id, + import_as_unique=import_as_unique) if profile_result.get('success'): total_successful += (profile_result.get('added', 0) + diff --git a/backend/app/compile/profile_compiler.py b/backend/app/compile/profile_compiler.py index fb4b159..a56a61c 100644 --- a/backend/app/compile/profile_compiler.py +++ b/backend/app/compile/profile_compiler.py @@ -34,11 +34,13 @@ class ProfileConverter: target_app: TargetApp, base_url: str = None, api_key: str = None, - format_importer: Callable = None): + format_importer: Callable = None, + import_as_unique: bool = False): self.target_app = target_app self.base_url = base_url self.api_key = api_key self.format_importer = format_importer + self.import_as_unique = import_as_unique self.quality_mappings = ValueResolver.get_qualities(target_app) def _convert_group_id(self, group_id: int) -> int: @@ -60,8 +62,11 @@ class ProfileConverter: }) return qualities - def _process_language_formats(self, behaviour: str, - language: str) -> List[Dict]: + def _process_language_formats( + self, + behaviour: str, + language: str, + import_as_unique: bool = False) -> List[Dict]: if not self.base_url or not self.api_key or not self.format_importer: logger.error("Missing required credentials or format importer") raise ValueError( @@ -80,7 +85,8 @@ class ProfileConverter: for_profile=False) modified_format = base_format.copy() - modified_format['name'] = f"Not {language_data['name']}" + base_name = f"Not {language_data['name']}" + modified_format['name'] = base_name for condition in modified_format['conditions']: if condition.get('type') == 'language': @@ -113,17 +119,24 @@ class ProfileConverter: arr_type = 'radarr' if self.target_app == TargetApp.RADARR else 'sonarr' for format_data in formats_to_import: try: - result = import_format_from_memory(format_data, - self.base_url, - self.api_key, arr_type) + result = import_format_from_memory( + format_data, + self.base_url, + self.api_key, + arr_type, + import_as_unique=self.import_as_unique) if not result.get('success', False): logger.error( f"Format import failed for: {format_data['name']}") raise Exception( f"Failed to import format {format_data['name']}") + format_name = format_data['name'] + if import_as_unique: + format_name = f"{format_name} [Dictionarry]" + format_configs.append({ - 'name': format_data['name'], + 'name': format_name, 'score': -9999 }) @@ -305,9 +318,13 @@ def compile_quality_profile(profile_data: Dict, target_app: TargetApp, base_url: str = None, api_key: str = None, - format_importer: Callable = None) -> List[Dict]: - converter = ProfileConverter(target_app, base_url, api_key, - format_importer) + format_importer: Callable = None, + import_as_unique: bool = False) -> List[Dict]: + converter = ProfileConverter(target_app, + base_url, + api_key, + format_importer, + import_as_unique=import_as_unique) converted = converter.convert_profile(profile_data) output = { diff --git a/backend/app/importarr/format_memory.py b/backend/app/importarr/format_memory.py index 9f1e8af..b135d5f 100644 --- a/backend/app/importarr/format_memory.py +++ b/backend/app/importarr/format_memory.py @@ -81,8 +81,11 @@ def add_format(base_url: str, api_key: str, format_data: Dict) -> bool: return False -def import_format_from_memory(format_data: Dict, base_url: str, api_key: str, - arr_type: str) -> Dict: +def import_format_from_memory(format_data: Dict, + base_url: str, + api_key: str, + arr_type: str, + import_as_unique: bool = False) -> Dict: """ Import a format directly from memory without requiring file loading. @@ -91,6 +94,7 @@ def import_format_from_memory(format_data: Dict, base_url: str, api_key: str, base_url: Arr instance base URL api_key: API key for arr instance arr_type: Type of arr instance (radarr/sonarr) + import_as_unique: Whether to append [Dictionarry] to format names Returns: Dict containing import results @@ -104,6 +108,14 @@ def import_format_from_memory(format_data: Dict, base_url: str, api_key: str, } try: + # Modify format name if import_as_unique is true + original_name = format_data['name'] + if import_as_unique: + format_data['name'] = f"{original_name} [Dictionarry]" + logger.info( + f"Modified format name for unique import: {format_data['name']}" + ) + # Log the received memory-based format data logger.info("Received memory-based format:\n" + json.dumps(format_data, indent=2)) diff --git a/backend/app/importarr/profile.py b/backend/app/importarr/profile.py index cec4ec6..2516038 100644 --- a/backend/app/importarr/profile.py +++ b/backend/app/importarr/profile.py @@ -81,7 +81,7 @@ def import_profiles_to_arr(profile_names: List[str], original_names: List[str], f"Processing tweaks and importing formats for profile '{profile_name}'" ) profile_data = process_tweaks(profile_data, base_url, api_key, - arr_type) + arr_type, import_as_unique) logger.info("Compiling quality profile...") compiled_profiles = compile_quality_profile( @@ -89,7 +89,8 @@ def import_profiles_to_arr(profile_names: List[str], original_names: List[str], target_app=target_app, base_url=base_url, api_key=api_key, - format_importer=import_formats_to_arr) + format_importer=import_formats_to_arr, + import_as_unique=import_as_unique) if not compiled_profiles: raise ValueError("Profile compilation returned no data") @@ -216,8 +217,11 @@ def sync_format_ids(profile_data: Dict, format_id_map: Dict[str, int]) -> Dict: return profile_data -def process_tweaks(profile_data: Dict, base_url: str, api_key: str, - arr_type: str) -> Dict: +def process_tweaks(profile_data: Dict, + base_url: str, + api_key: str, + arr_type: str, + import_as_unique: bool = False) -> Dict: logger.debug(f"Processing tweaks for profile: {profile_data.get('name')}") tweaks = profile_data.get('tweaks', {}) @@ -233,7 +237,8 @@ def process_tweaks(profile_data: Dict, base_url: str, api_key: str, base_url=base_url, api_key=api_key, arr_type=arr_type, - feature_name="freeleech") + feature_name="freeleech", + import_as_unique=import_as_unique) lossless_formats = [ "FLAC", "DTS-X", "DTS-HD MA", "TrueHD", "TrueHD (Missing)" @@ -249,7 +254,8 @@ def process_tweaks(profile_data: Dict, base_url: str, api_key: str, base_url=base_url, api_key=api_key, arr_type=arr_type, - feature_name="lossless audio") + feature_name="lossless audio", + import_as_unique=import_as_unique) dv_formats = ["Dolby Vision (Without Fallback)"] dv_score = 0 if tweaks.get('allowDVNoFallback', False) else -9999 @@ -260,7 +266,8 @@ def process_tweaks(profile_data: Dict, base_url: str, api_key: str, base_url=base_url, api_key=api_key, arr_type=arr_type, - feature_name="Dolby Vision no fallback") + feature_name="Dolby Vision no fallback", + import_as_unique=import_as_unique) codec_formats = ["AV1", "VVC"] codec_score = 0 if tweaks.get('allowBleedingEdgeCodecs', False) else -9999 @@ -271,23 +278,56 @@ def process_tweaks(profile_data: Dict, base_url: str, api_key: str, base_url=base_url, api_key=api_key, arr_type=arr_type, - feature_name="bleeding edge codecs") + feature_name="bleeding edge codecs", + import_as_unique=import_as_unique) return profile_data -def _import_and_score_formats(formats: List[str], scores: List[Dict[str, Any]], - profile_data: Dict, base_url: str, api_key: str, - arr_type: str, feature_name: str) -> None: +def _import_and_score_formats(formats: List[str], + scores: List[Dict[str, Any]], + profile_data: Dict, + base_url: str, + api_key: str, + arr_type: str, + feature_name: str, + import_as_unique: bool = False) -> None: logger.info( f"Processing {feature_name} formats for profile '{profile_data.get('name')}'" ) try: - result = import_formats_to_arr(formats, base_url, api_key, arr_type) + # Create modified format names if import_as_unique is true + format_names = [ + f"{name} [Dictionarry]" if import_as_unique else name + for name in formats + ] + + result = import_formats_to_arr( + format_names=format_names, # Use modified names for import + original_names=formats, # Original names for file lookup + base_url=base_url, + api_key=api_key, + arr_type=arr_type) + if not result.get('success', False): logger.warning( f"Failed to import {feature_name} formats for '{profile_data.get('name')}'" ) + return + + if 'custom_formats' not in profile_data: + profile_data['custom_formats'] = [] + + # Use the modified format names in the profile's format list + modified_scores = [] + for i, score in enumerate(scores): + score_copy = score.copy() + # Use the same modified name that was used for import + score_copy['name'] = format_names[i] + modified_scores.append(score_copy) + + profile_data['custom_formats'].extend(modified_scores) + except Exception as e: logger.error(f"Error importing {feature_name} formats: {str(e)}") return