mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
225 lines
7.7 KiB
Python
225 lines
7.7 KiB
Python
# app/compile/format_compiler.py
|
|
"""Format compilation module for converting custom formats"""
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
import json
|
|
import yaml
|
|
|
|
from .mappings import TargetApp, ValueResolver
|
|
|
|
|
|
@dataclass
|
|
class Specification:
|
|
"""Data class for format specifications"""
|
|
name: str
|
|
implementation: str
|
|
negate: bool = False
|
|
required: bool = False
|
|
fields: List[Dict[str, str]] = None
|
|
|
|
def __post_init__(self):
|
|
if self.fields is None:
|
|
self.fields = []
|
|
|
|
|
|
@dataclass
|
|
class CustomFormat:
|
|
"""Data class for custom format definitions"""
|
|
name: str
|
|
description: str
|
|
tags: List[str]
|
|
conditions: List[Dict]
|
|
tests: List[Dict]
|
|
|
|
|
|
@dataclass
|
|
class ConvertedFormat:
|
|
"""Data class for converted format output"""
|
|
name: str
|
|
specifications: List[Specification]
|
|
|
|
|
|
class FormatConverter:
|
|
"""Converts between different format types"""
|
|
|
|
def __init__(self, patterns: Dict[str, str]):
|
|
self.patterns = patterns
|
|
|
|
def _create_specification(
|
|
self, condition: Dict,
|
|
target_app: TargetApp) -> Optional[Specification]:
|
|
condition_type = condition['type']
|
|
|
|
if condition_type in ['release_title', 'release_group', 'edition']:
|
|
pattern_name = condition['pattern']
|
|
pattern = self.patterns.get(pattern_name)
|
|
if not pattern:
|
|
return None
|
|
implementation = ('ReleaseTitleSpecification'
|
|
if condition_type == 'release_title' else
|
|
'ReleaseGroupSpecification' if condition_type
|
|
== 'release_group' else 'EditionSpecification')
|
|
fields = [{'name': 'value', 'value': pattern}]
|
|
|
|
elif condition_type == 'source':
|
|
implementation = 'SourceSpecification'
|
|
value = ValueResolver.get_source(condition['source'], target_app)
|
|
fields = [{'name': 'value', 'value': value}]
|
|
|
|
elif condition_type == 'resolution':
|
|
implementation = 'ResolutionSpecification'
|
|
value = ValueResolver.get_resolution(condition['resolution'])
|
|
fields = [{'name': 'value', 'value': value}]
|
|
|
|
elif condition_type == 'indexer_flag':
|
|
implementation = 'IndexerFlagSpecification'
|
|
value = ValueResolver.get_indexer_flag(condition.get('flag', ''),
|
|
target_app)
|
|
fields = [{'name': 'value', 'value': value}]
|
|
|
|
elif condition_type == 'quality_modifier':
|
|
if target_app == TargetApp.SONARR:
|
|
return None
|
|
implementation = 'QualityModifierSpecification'
|
|
value = ValueResolver.get_quality_modifier(
|
|
condition['qualityModifier'])
|
|
fields = [{'name': 'value', 'value': value}]
|
|
|
|
elif condition_type == 'size':
|
|
implementation = 'SizeSpecification'
|
|
min_size = condition.get('minSize')
|
|
max_size = condition.get('maxSize')
|
|
fields = [{
|
|
'name': 'min',
|
|
'value': min_size
|
|
}, {
|
|
'name': 'max',
|
|
'value': max_size
|
|
}]
|
|
|
|
elif condition_type == 'year':
|
|
implementation = 'YearSpecification'
|
|
min_year = condition.get('minYear')
|
|
max_year = condition.get('maxYear')
|
|
fields = [{
|
|
'name': 'min',
|
|
'value': min_year
|
|
}, {
|
|
'name': 'max',
|
|
'value': max_year
|
|
}]
|
|
|
|
elif condition_type == 'release_type':
|
|
if target_app == TargetApp.RADARR:
|
|
return None
|
|
implementation = 'ReleaseTypeSpecification'
|
|
value = ValueResolver.get_release_type(condition['releaseType'])
|
|
fields = [{'name': 'value', 'value': value}]
|
|
|
|
elif condition_type == 'language':
|
|
implementation = 'LanguageSpecification'
|
|
language_name = condition['language'].lower()
|
|
try:
|
|
language_data = ValueResolver.get_language(language_name,
|
|
target_app,
|
|
for_profile=False)
|
|
fields = [{'name': 'value', 'value': language_data['id']}]
|
|
if 'exceptLanguage' in condition:
|
|
except_value = condition['exceptLanguage']
|
|
fields.append({
|
|
'name': 'exceptLanguage',
|
|
'value': except_value
|
|
})
|
|
except Exception:
|
|
return None
|
|
|
|
else:
|
|
return None
|
|
|
|
return Specification(name=condition.get('name', ''),
|
|
implementation=implementation,
|
|
negate=condition.get('negate', False),
|
|
required=condition.get('required', False),
|
|
fields=fields)
|
|
|
|
def convert_format(self, custom_format: CustomFormat,
|
|
target_app: TargetApp) -> ConvertedFormat:
|
|
specifications = []
|
|
for condition in custom_format.conditions:
|
|
try:
|
|
spec = self._create_specification(condition, target_app)
|
|
if spec:
|
|
specifications.append(spec)
|
|
except Exception:
|
|
continue
|
|
|
|
return ConvertedFormat(name=custom_format.name,
|
|
specifications=specifications)
|
|
|
|
|
|
class FormatProcessor:
|
|
"""Main class for processing format files"""
|
|
|
|
def __init__(self, input_dir: Path, output_dir: Path, patterns_dir: Path):
|
|
self.input_dir = input_dir
|
|
self.output_dir = output_dir
|
|
self.patterns = self._load_patterns(patterns_dir)
|
|
self.converter = FormatConverter(self.patterns)
|
|
|
|
@staticmethod
|
|
def _load_patterns(patterns_dir: Path) -> Dict[str, str]:
|
|
patterns = {}
|
|
for file_path in patterns_dir.glob('*.yml'):
|
|
with file_path.open('r') as f:
|
|
pattern_data = yaml.safe_load(f)
|
|
patterns[pattern_data['name']] = pattern_data['pattern']
|
|
return patterns
|
|
|
|
def _load_custom_format(self, format_name: str) -> Optional[CustomFormat]:
|
|
format_path = self.input_dir / f"{format_name}.yml"
|
|
if not format_path.exists():
|
|
return None
|
|
|
|
with format_path.open('r') as f:
|
|
raw_data = yaml.safe_load(f)
|
|
return CustomFormat(**raw_data)
|
|
|
|
def process_format(self,
|
|
format_name: str,
|
|
target_app: TargetApp,
|
|
return_data: bool = False) -> Optional[ConvertedFormat]:
|
|
custom_format = self._load_custom_format(format_name)
|
|
if not custom_format:
|
|
return None
|
|
|
|
converted_format = self.converter.convert_format(
|
|
custom_format, target_app)
|
|
|
|
output_data = [{
|
|
'name':
|
|
converted_format.name,
|
|
'specifications':
|
|
[vars(spec) for spec in converted_format.specifications]
|
|
}]
|
|
|
|
if not return_data:
|
|
output_path = self.output_dir / f"{format_name}.json"
|
|
with output_path.open('w') as f:
|
|
json.dump(output_data, f, indent=2)
|
|
|
|
return converted_format
|
|
|
|
|
|
def compile_custom_format(format_data: Dict) -> List[Dict]:
|
|
custom_format = CustomFormat(**format_data)
|
|
patterns = {}
|
|
converter = FormatConverter(patterns)
|
|
converted = converter.convert_format(custom_format, TargetApp.RADARR)
|
|
output_data = [{
|
|
'name':
|
|
converted.name,
|
|
'specifications': [vars(spec) for spec in converted.specifications]
|
|
}]
|
|
return output_data
|