mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-28 05:20:57 +01:00
feat: simplify language support in quality profiles
- moved language field in quality profile general page - simplify transformation for sonarr by making languages optional
This commit is contained in:
@@ -9,6 +9,7 @@ export interface CreateQualityProfileInput {
|
||||
name: string;
|
||||
description: string | null;
|
||||
tags: string[];
|
||||
language: string | null;
|
||||
}
|
||||
|
||||
export interface CreateQualityProfileOptions {
|
||||
@@ -89,6 +90,16 @@ export async function create(options: CreateQualityProfileOptions) {
|
||||
queries.push(insertQuality);
|
||||
}
|
||||
|
||||
// 4. Insert language if one is selected
|
||||
if (input.language !== null) {
|
||||
const insertLanguage = {
|
||||
sql: `INSERT INTO quality_profile_languages (quality_profile_name, language_name, type) VALUES ('${esc(input.name)}', '${esc(input.language)}', 'simple')`,
|
||||
parameters: [],
|
||||
query: {} as never
|
||||
};
|
||||
queries.push(insertLanguage);
|
||||
}
|
||||
|
||||
// Write the operation
|
||||
const result = await writeOperation({
|
||||
databaseId,
|
||||
|
||||
@@ -32,6 +32,13 @@ export async function general(
|
||||
.orderBy('t.name')
|
||||
.execute();
|
||||
|
||||
// Get language for this profile (first one if exists)
|
||||
const languageRow = await db
|
||||
.selectFrom('quality_profile_languages as qpl')
|
||||
.select(['qpl.language_name'])
|
||||
.where('qpl.quality_profile_name', '=', profile.name)
|
||||
.executeTakeFirst();
|
||||
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
@@ -39,6 +46,7 @@ export async function general(
|
||||
tags: tags.map((tag) => ({
|
||||
name: tag.tag_name,
|
||||
created_at: tag.tag_created_at
|
||||
}))
|
||||
})),
|
||||
language: languageRow?.language_name ?? null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface QualityProfileGeneral {
|
||||
name: string;
|
||||
description: string; // Raw markdown
|
||||
tags: Tag[];
|
||||
language: string | null; // Language name, null means "Any"
|
||||
}
|
||||
|
||||
/** Language configuration for a quality profile */
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface UpdateGeneralInput {
|
||||
name: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
language: string | null; // Language name, null means no language set
|
||||
}
|
||||
|
||||
export interface UpdateGeneralOptions {
|
||||
@@ -92,6 +93,27 @@ export async function updateGeneral(options: UpdateGeneralOptions) {
|
||||
queries.push(linkTag);
|
||||
}
|
||||
|
||||
// 3. Handle language changes
|
||||
const profileNameForLanguage = input.name !== current.name ? input.name : current.name;
|
||||
|
||||
// Delete existing language for this profile
|
||||
const deleteLanguage = {
|
||||
sql: `DELETE FROM quality_profile_languages WHERE quality_profile_name = '${esc(profileNameForLanguage)}'`,
|
||||
parameters: [],
|
||||
query: {} as never
|
||||
};
|
||||
queries.push(deleteLanguage);
|
||||
|
||||
// Insert new language if one is selected
|
||||
if (input.language !== null) {
|
||||
const insertLanguage = {
|
||||
sql: `INSERT INTO quality_profile_languages (quality_profile_name, language_name, type) VALUES ('${esc(profileNameForLanguage)}', '${esc(input.language)}', 'simple')`,
|
||||
parameters: [],
|
||||
query: {} as never
|
||||
};
|
||||
queries.push(insertLanguage);
|
||||
}
|
||||
|
||||
// Log what's being changed
|
||||
const changes: Record<string, { from: unknown; to: unknown }> = {};
|
||||
|
||||
@@ -104,6 +126,9 @@ export async function updateGeneral(options: UpdateGeneralOptions) {
|
||||
if (tagsToAdd.length > 0 || tagsToRemove.length > 0) {
|
||||
changes.tags = { from: currentTagNames, to: input.tags };
|
||||
}
|
||||
if (current.language !== input.language) {
|
||||
changes.language = { from: current.language, to: input.language };
|
||||
}
|
||||
|
||||
await logger.info(`Save quality profile "${input.name}"`, {
|
||||
source: 'QualityProfile',
|
||||
|
||||
@@ -246,7 +246,13 @@ export const LANGUAGES: Record<SyncArrType, Record<string, LanguageDefinition>>
|
||||
malayalam: { id: 48, name: 'Malayalam' },
|
||||
kannada: { id: 49, name: 'Kannada' },
|
||||
albanian: { id: 50, name: 'Albanian' },
|
||||
afrikaans: { id: 51, name: 'Afrikaans' }
|
||||
afrikaans: { id: 51, name: 'Afrikaans' },
|
||||
marathi: { id: 52, name: 'Marathi' },
|
||||
tagalog: { id: 53, name: 'Tagalog' },
|
||||
urdu: { id: 54, name: 'Urdu' },
|
||||
romansh: { id: 55, name: 'Romansh' },
|
||||
mongolian: { id: 56, name: 'Mongolian' },
|
||||
georgian: { id: 57, name: 'Georgian' }
|
||||
},
|
||||
sonarr: {
|
||||
unknown: { id: 0, name: 'Unknown' },
|
||||
@@ -479,3 +485,60 @@ export function getLanguageForProfile(name: string, arrType: SyncArrType): Langu
|
||||
|
||||
return getLanguage(name, arrType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Radarr languages as an array (for UI dropdowns)
|
||||
* Returns languages sorted by name, with Any and Original at the top
|
||||
*/
|
||||
export function getRadarrLanguages(): LanguageDefinition[] {
|
||||
const languages = Object.values(LANGUAGES.radarr);
|
||||
// Sort: Any first, Original second, then alphabetically
|
||||
return languages.sort((a, b) => {
|
||||
if (a.id === -1) return -1;
|
||||
if (b.id === -1) return 1;
|
||||
if (a.id === -2) return -1;
|
||||
if (b.id === -2) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Language with arr type support information (for conditions UI)
|
||||
*/
|
||||
export interface LanguageWithSupport {
|
||||
name: string;
|
||||
radarr: boolean;
|
||||
sonarr: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all languages with their arr type support (for conditions page)
|
||||
* Returns sorted array with Original first, then alphabetically
|
||||
*/
|
||||
export function getLanguagesWithSupport(): LanguageWithSupport[] {
|
||||
const radarrLangs = new Set(Object.values(LANGUAGES.radarr).map((l) => l.name));
|
||||
const sonarrLangs = new Set(Object.values(LANGUAGES.sonarr).map((l) => l.name));
|
||||
|
||||
// Combine all language names
|
||||
const allNames = new Set([...radarrLangs, ...sonarrLangs]);
|
||||
|
||||
// Build result with support flags
|
||||
const result: LanguageWithSupport[] = [];
|
||||
for (const name of allNames) {
|
||||
// Skip "Any" - it's only for quality profiles, not conditions
|
||||
if (name === 'Any') continue;
|
||||
|
||||
result.push({
|
||||
name,
|
||||
radarr: radarrLangs.has(name),
|
||||
sonarr: sonarrLangs.has(name)
|
||||
});
|
||||
}
|
||||
|
||||
// Sort: Original first, then alphabetically
|
||||
return result.sort((a, b) => {
|
||||
if (a.name === 'Original') return -1;
|
||||
if (b.name === 'Original') return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ export class QualityProfileSyncer extends BaseSyncer {
|
||||
syncedProfiles.push({
|
||||
name: arrProfile.name,
|
||||
action: isUpdate ? 'updated' : 'created',
|
||||
language: arrProfile.language.name,
|
||||
language: arrProfile.language?.name ?? 'N/A',
|
||||
cutoffFormatScore: arrProfile.cutoffFormatScore,
|
||||
minFormatScore: arrProfile.minFormatScore,
|
||||
formats: scoredFormats
|
||||
|
||||
@@ -34,7 +34,7 @@ export interface ArrQualityProfile {
|
||||
id?: number;
|
||||
name: string;
|
||||
items: ArrQualityItem[];
|
||||
language: { id: number; name: string };
|
||||
language?: { id: number; name: string }; // Radarr only, Sonarr ignores this
|
||||
upgradeAllowed: boolean;
|
||||
cutoff: number;
|
||||
minFormatScore: number;
|
||||
@@ -201,9 +201,11 @@ export function transformQualityProfile(
|
||||
// Reverse items to match arr expected order
|
||||
items.reverse();
|
||||
|
||||
// Build language config
|
||||
const languageName = profile.language?.name ?? 'any';
|
||||
const language = getLanguageForProfile(languageName, arrType);
|
||||
// Build language config (Radarr only - Sonarr uses custom formats for language filtering)
|
||||
const language =
|
||||
arrType === 'radarr'
|
||||
? getLanguageForProfile(profile.language?.name ?? 'any', arrType)
|
||||
: undefined;
|
||||
|
||||
// Build format items
|
||||
const formatItems: ArrFormatItem[] = [];
|
||||
@@ -236,7 +238,7 @@ export function transformQualityProfile(
|
||||
return {
|
||||
name: profile.name,
|
||||
items,
|
||||
language,
|
||||
...(language && { language }), // Only include for Radarr
|
||||
upgradeAllowed: profile.upgradesAllowed,
|
||||
cutoff: cutoffId ?? items[items.length - 1]?.quality?.id ?? 0,
|
||||
minFormatScore: profile.minimumCustomFormatScore,
|
||||
|
||||
@@ -607,7 +607,7 @@ export interface ArrQualityProfilePayload {
|
||||
id?: number;
|
||||
name: string;
|
||||
items: ArrQualityProfileItem[];
|
||||
language: ArrLanguage;
|
||||
language?: ArrLanguage; // Radarr only - Sonarr uses custom formats for language filtering
|
||||
upgradeAllowed: boolean;
|
||||
cutoff: number;
|
||||
minFormatScore: number;
|
||||
|
||||
Reference in New Issue
Block a user