mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
refactor: remove tweaks functionality from profile handling (#145)
- removed because I couldn't integrate it nicely with the rest of the application, will keep in the todo pile for later.
This commit is contained in:
@@ -201,15 +201,10 @@ class ProfileConverter:
|
||||
language=selected_language)
|
||||
|
||||
used_qualities = set()
|
||||
tweaks = profile.get('tweaks', {})
|
||||
allow_prereleases = tweaks.get('allowPrereleases', False)
|
||||
|
||||
for quality_entry in profile.get("qualities", []):
|
||||
if quality_entry.get("id", 0) < 0:
|
||||
converted_group = self.convert_quality_group(quality_entry)
|
||||
if (quality_entry.get("name") == "Prereleases"
|
||||
and not allow_prereleases):
|
||||
converted_group["allowed"] = False
|
||||
if converted_group["items"]:
|
||||
converted_profile.items.append(converted_group)
|
||||
for q in quality_entry.get("qualities", []):
|
||||
@@ -246,7 +241,6 @@ class ProfileConverter:
|
||||
if cutoff_id < 0:
|
||||
converted_profile.cutoff = self._convert_group_id(cutoff_id)
|
||||
else:
|
||||
# And use mapped_cutoff_name here instead of cutoff_name
|
||||
converted_profile.cutoff = self.quality_mappings[
|
||||
mapped_cutoff_name]["id"]
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ PROFILE_FIELDS = [
|
||||
"custom_formats", # Array of {name, score} objects
|
||||
"qualities", # Array of strings
|
||||
"upgrade_until",
|
||||
"language",
|
||||
"tweaks"
|
||||
"language"
|
||||
]
|
||||
|
||||
# Category mappings
|
||||
|
||||
@@ -77,12 +77,6 @@ def import_profiles_to_arr(profile_names: List[str], original_names: List[str],
|
||||
f"Profile '{profile_name}' has language override: {profile_language}"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Processing tweaks and importing formats for profile '{profile_name}'"
|
||||
)
|
||||
profile_data = process_tweaks(profile_data, base_url, api_key,
|
||||
arr_type, import_as_unique)
|
||||
|
||||
logger.info("Compiling quality profile...")
|
||||
compiled_profiles = compile_quality_profile(
|
||||
profile_data=profile_data,
|
||||
@@ -221,123 +215,6 @@ 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,
|
||||
import_as_unique: bool = False) -> Dict:
|
||||
logger.debug(f"Processing tweaks for profile: {profile_data.get('name')}")
|
||||
tweaks = profile_data.get('tweaks', {})
|
||||
|
||||
if tweaks.get('preferFreeleech', False):
|
||||
freeleech_formats = ["Free25", "Free50", "Free75", "Free100"]
|
||||
freeleech_scores = [{
|
||||
"name": n,
|
||||
"score": s
|
||||
} for n, s in zip(freeleech_formats, range(1, 5))]
|
||||
_import_and_score_formats(formats=freeleech_formats,
|
||||
scores=freeleech_scores,
|
||||
profile_data=profile_data,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
arr_type=arr_type,
|
||||
feature_name="freeleech",
|
||||
import_as_unique=import_as_unique)
|
||||
|
||||
lossless_formats = [
|
||||
"FLAC", "DTS-X", "DTS-HD MA", "TrueHD", "TrueHD (Missing)"
|
||||
]
|
||||
default_score = 0 if tweaks.get('allowLosslessAudio', False) else -9999
|
||||
lossless_scores = [{
|
||||
"name": f,
|
||||
"score": default_score
|
||||
} for f in lossless_formats]
|
||||
_import_and_score_formats(formats=lossless_formats,
|
||||
scores=lossless_scores,
|
||||
profile_data=profile_data,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
arr_type=arr_type,
|
||||
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
|
||||
dv_scores = [{"name": n, "score": dv_score} for n in dv_formats]
|
||||
_import_and_score_formats(formats=dv_formats,
|
||||
scores=dv_scores,
|
||||
profile_data=profile_data,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
arr_type=arr_type,
|
||||
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
|
||||
codec_scores = [{"name": f, "score": codec_score} for f in codec_formats]
|
||||
_import_and_score_formats(formats=codec_formats,
|
||||
scores=codec_scores,
|
||||
profile_data=profile_data,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
arr_type=arr_type,
|
||||
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,
|
||||
import_as_unique: bool = False) -> None:
|
||||
logger.info(
|
||||
f"Processing {feature_name} formats for profile '{profile_data.get('name')}'"
|
||||
)
|
||||
try:
|
||||
# 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)
|
||||
|
||||
# Only append once with the modified scores
|
||||
profile_data['custom_formats'].extend(modified_scores)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error importing {feature_name} formats: {str(e)}")
|
||||
return
|
||||
|
||||
|
||||
def process_profile(profile_data: Dict, existing_names: Dict[str, int],
|
||||
base_url: str, api_key: str) -> Dict:
|
||||
profile_name = profile_data['name']
|
||||
|
||||
@@ -8,17 +8,8 @@ import ProfileGeneralTab from './ProfileGeneralTab';
|
||||
import ProfileScoringTab from './scoring/ProfileScoringTab';
|
||||
import ProfileQualitiesTab from './ProfileQualitiesTab';
|
||||
import ProfileLangaugesTab from './ProfileLangaugesTab';
|
||||
import ProfileTweaksTab from './ProfileTweaksTab';
|
||||
import QUALITIES from '../../constants/qualities';
|
||||
|
||||
const DEFAULT_TWEAKS = {
|
||||
preferFreeleech: true,
|
||||
allowLosslessAudio: true,
|
||||
allowDVNoFallback: false,
|
||||
allowBleedingEdgeCodecs: false,
|
||||
allowPrereleases: false
|
||||
};
|
||||
|
||||
function unsanitize(text) {
|
||||
if (!text) return '';
|
||||
return text.replace(/\\:/g, ':').replace(/\\n/g, '\n');
|
||||
@@ -70,15 +61,11 @@ function ProfileModal({
|
||||
// Language state
|
||||
const [language, setLanguage] = useState('must_english');
|
||||
|
||||
// Tweaks state
|
||||
const [tweaks, setTweaks] = useState(DEFAULT_TWEAKS);
|
||||
|
||||
const tabs = [
|
||||
{id: 'general', label: 'General'},
|
||||
{id: 'scoring', label: 'Scoring'},
|
||||
{id: 'qualities', label: 'Qualities'},
|
||||
{id: 'languages', label: 'Languages'},
|
||||
{id: 'tweaks', label: 'Tweaks'}
|
||||
{id: 'languages', label: 'Languages'}
|
||||
];
|
||||
|
||||
const resetState = () => {
|
||||
@@ -124,7 +111,6 @@ function ProfileModal({
|
||||
|
||||
// Reset other states
|
||||
setLanguage('must_english');
|
||||
setTweaks(DEFAULT_TWEAKS);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -185,12 +171,6 @@ function ProfileModal({
|
||||
});
|
||||
setTagScores(initialTagScores);
|
||||
|
||||
// Tweaks
|
||||
setTweaks({
|
||||
...DEFAULT_TWEAKS,
|
||||
...(content.tweaks || {})
|
||||
});
|
||||
|
||||
// Qualities setup - include all qualities, set enabled status
|
||||
const allQualitiesMap = {}; // Map of all qualities by id
|
||||
QUALITIES.forEach(quality => {
|
||||
@@ -335,7 +315,6 @@ function ProfileModal({
|
||||
|
||||
// Initialize with defaults
|
||||
setLanguage('must_english');
|
||||
setTweaks(DEFAULT_TWEAKS);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
@@ -401,8 +380,7 @@ function ProfileModal({
|
||||
})
|
||||
}
|
||||
: null,
|
||||
language,
|
||||
tweaks
|
||||
language
|
||||
};
|
||||
|
||||
if (isCloning || !initialProfile) {
|
||||
@@ -589,12 +567,6 @@ function ProfileModal({
|
||||
onLanguageChange={setLanguage}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'tweaks' && (
|
||||
<ProfileTweaksTab
|
||||
tweaks={tweaks}
|
||||
onTweaksChange={setTweaks}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -638,8 +610,7 @@ ProfileModal.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
}),
|
||||
language: PropTypes.string,
|
||||
tweaks: PropTypes.object
|
||||
language: PropTypes.string
|
||||
})
|
||||
}),
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {InfoIcon, AlertTriangle} from 'lucide-react';
|
||||
|
||||
const ProfileTweaksTab = ({tweaks, onTweaksChange}) => {
|
||||
const handleTweakChange = key => {
|
||||
onTweaksChange({
|
||||
...tweaks,
|
||||
[key]: !tweaks[key]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='h-full flex flex-col'>
|
||||
<div className='mt-4 space-y-4'>
|
||||
<div className='flex gap-2 p-3 text-xs bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg'>
|
||||
<InfoIcon className='h-4 w-4 text-blue-600 dark:text-blue-400 flex-shrink-0' />
|
||||
<p className='text-blue-700 dark:text-blue-300'>
|
||||
Tweaks are custom changes that can be toggled according
|
||||
to your preference. These settings are profile-specific
|
||||
and won't create merge conflicts when synchronizing with
|
||||
remote repositories. Use tweaks to fine-tune your
|
||||
profile's behavior without affecting the core
|
||||
configuration.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
{/* Allow Dolby Vision without Fallback */}
|
||||
<div
|
||||
onClick={() => handleTweakChange('allowDVNoFallback')}
|
||||
className={`
|
||||
p-4 rounded-lg cursor-pointer select-none
|
||||
border transition-colors duration-200
|
||||
${
|
||||
tweaks.allowDVNoFallback
|
||||
? 'border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
|
||||
}
|
||||
hover:border-blue-500 dark:hover:border-blue-400
|
||||
`}>
|
||||
<div className='space-y-1'>
|
||||
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||
Allow Dolby Vision without Fallback
|
||||
</h3>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
Allow Dolby Vision releases that don't include
|
||||
HDR10 fallback. These may display incorrectly on
|
||||
non-Dolby Vision displays.
|
||||
</p>
|
||||
<div className='flex items-center gap-1.5 mt-2'>
|
||||
<AlertTriangle className='h-3 w-3 text-amber-500' />
|
||||
<p className='text-[10px] text-amber-600 dark:text-amber-400'>
|
||||
Only enable if your display supports Dolby
|
||||
Vision
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Allow Bleeding Edge Codecs */}
|
||||
<div
|
||||
onClick={() =>
|
||||
handleTweakChange('allowBleedingEdgeCodecs')
|
||||
}
|
||||
className={`
|
||||
p-4 rounded-lg cursor-pointer select-none
|
||||
border transition-colors duration-200
|
||||
${
|
||||
tweaks.allowBleedingEdgeCodecs
|
||||
? 'border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
|
||||
}
|
||||
hover:border-blue-500 dark:hover:border-blue-400
|
||||
`}>
|
||||
<div className='space-y-1'>
|
||||
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||
Allow Bleeding Edge Codecs
|
||||
</h3>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
Allow releases using newer codecs like AV1 and
|
||||
H.266/VVC. These may offer better compression
|
||||
but have limited hardware support.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Allow Lossless Audio */}
|
||||
<div
|
||||
onClick={() => handleTweakChange('allowLosslessAudio')}
|
||||
className={`
|
||||
p-4 rounded-lg cursor-pointer select-none
|
||||
border transition-colors duration-200
|
||||
${
|
||||
tweaks.allowLosslessAudio ?? true
|
||||
? 'border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
|
||||
}
|
||||
hover:border-blue-500 dark:hover:border-blue-400
|
||||
`}>
|
||||
<div className='space-y-1'>
|
||||
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||
Allow Lossless Audio
|
||||
</h3>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
Allow high-quality lossless audio formats
|
||||
including TrueHD + Atmos, DTS-HD MA, DTS-X,
|
||||
FLAC, and PCM.
|
||||
</p>
|
||||
<div className='flex items-center gap-1.5 mt-2'>
|
||||
<AlertTriangle className='h-3 w-3 text-amber-500' />
|
||||
<p className='text-[10px] text-amber-600 dark:text-amber-400'>
|
||||
May skip better quality releases if disabled
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Allow Prereleases */}
|
||||
<div
|
||||
onClick={() => handleTweakChange('allowPrereleases')}
|
||||
className={`
|
||||
p-4 rounded-lg cursor-pointer select-none
|
||||
border transition-colors duration-200
|
||||
${
|
||||
tweaks.allowPrereleases
|
||||
? 'border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
|
||||
}
|
||||
hover:border-blue-500 dark:hover:border-blue-400
|
||||
`}>
|
||||
<div className='space-y-1'>
|
||||
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||
Allow Prereleases
|
||||
</h3>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
Allow early releases like CAMs, Telecines,
|
||||
Telesyncs, and Screeners. These are typically
|
||||
available before official releases but at lower
|
||||
quality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Prefer Freeleech */}
|
||||
<div
|
||||
onClick={() => handleTweakChange('preferFreeleech')}
|
||||
className={`
|
||||
p-4 rounded-lg cursor-pointer select-none
|
||||
border transition-colors duration-200
|
||||
${
|
||||
tweaks.preferFreeleech
|
||||
? 'border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800'
|
||||
}
|
||||
hover:border-blue-500 dark:hover:border-blue-400
|
||||
`}>
|
||||
<div className='space-y-1'>
|
||||
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||
Prefer Freeleech
|
||||
</h3>
|
||||
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
Prioritize releases tagged as freeleech when
|
||||
choosing between different indexers' releases.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ProfileTweaksTab.propTypes = {
|
||||
tweaks: PropTypes.object.isRequired,
|
||||
onTweaksChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ProfileTweaksTab;
|
||||
Reference in New Issue
Block a user