Skip to content

Commit 0d282d7

Browse files
fix: use indexer reported langague in evaluator in front of release title parsed value, specify source in parsed badge
1 parent 915bc5b commit 0d282d7

File tree

6 files changed

+101
-20
lines changed

6 files changed

+101
-20
lines changed

docs/api/v1/schemas/entity-testing.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ParsedInfo:
1212
- resolution
1313
- modifier
1414
- languages
15+
- languageSource
1516
- year
1617
properties:
1718
source:
@@ -28,6 +29,12 @@ ParsedInfo:
2829
items:
2930
type: string
3031
description: Detected languages
32+
languageSource:
33+
type: string
34+
enum:
35+
- Indexer
36+
- Title
37+
description: Where the language information came from (Indexer = indexer-provided, Title = parsed from release title)
3138
releaseGroup:
3239
type: string
3340
nullable: true
@@ -59,6 +66,11 @@ ReleaseInput:
5966
description: Release title to parse and evaluate
6067
type:
6168
$ref: '#/MediaType'
69+
languages:
70+
type: array
71+
items:
72+
type: string
73+
description: Indexer-provided languages. When present and non-empty, these override languages parsed from the title.
6274

6375
ReleaseEvaluation:
6476
type: object

src/lib/api/v1.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ export interface components {
287287
modifier: string;
288288
/** @description Detected languages */
289289
languages: string[];
290+
/**
291+
* @description Where the language information came from (Indexer = indexer-provided, Title = parsed from release title)
292+
* @enum {string}
293+
*/
294+
languageSource: "Indexer" | "Title";
290295
/** @description Detected release group */
291296
releaseGroup?: string | null;
292297
/** @description Detected year */
@@ -302,6 +307,8 @@ export interface components {
302307
/** @description Release title to parse and evaluate */
303308
title: string;
304309
type: components["schemas"]["MediaType"];
310+
/** @description Indexer-provided languages. When present and non-empty, these override languages parsed from the title. */
311+
languages?: string[];
305312
};
306313
ReleaseEvaluation: {
307314
/** @description Release ID */

src/lib/server/pcd/entities/customFormats/evaluator.ts

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ const sourceNames: Record<QualitySource, string> = {
6363
[QualitySource.Bluray]: 'bluray'
6464
};
6565

66+
// Display-friendly names for parsed info shown in the UI
67+
const sourceDisplayNames: Record<QualitySource, string> = {
68+
[QualitySource.Unknown]: 'Unknown',
69+
[QualitySource.Cam]: 'CAM',
70+
[QualitySource.Telesync]: 'Telesync',
71+
[QualitySource.Telecine]: 'Telecine',
72+
[QualitySource.Workprint]: 'Workprint',
73+
[QualitySource.DVD]: 'DVD',
74+
[QualitySource.TV]: 'Television',
75+
[QualitySource.WebDL]: 'WEB-DL',
76+
[QualitySource.WebRip]: 'WEBRip',
77+
[QualitySource.Bluray]: 'Blu-ray'
78+
};
79+
6680
const resolutionNames: Record<Resolution, string> = {
6781
[Resolution.Unknown]: 'unknown',
6882
[Resolution.R360p]: '360p',
@@ -83,6 +97,15 @@ const modifierNames: Record<QualityModifier, string> = {
8397
[QualityModifier.Remux]: 'remux'
8498
};
8599

100+
const modifierDisplayNames: Record<QualityModifier, string> = {
101+
[QualityModifier.None]: 'None',
102+
[QualityModifier.Regional]: 'Regional',
103+
[QualityModifier.Screener]: 'Screener',
104+
[QualityModifier.RawHD]: 'Raw-HD',
105+
[QualityModifier.BRDisk]: 'BR-DISK',
106+
[QualityModifier.Remux]: 'Remux'
107+
};
108+
86109
const releaseTypeNames: Record<ReleaseType, string> = {
87110
[ReleaseType.Unknown]: 'unknown',
88111
[ReleaseType.SingleEpisode]: 'single_episode',
@@ -152,15 +175,36 @@ const languageNames: Record<Language, string> = {
152175
[Language.Original]: 'Original'
153176
};
154177

178+
/** Reverse lookup: display name → Language enum value */
179+
const languageNameToEnum = new Map<string, Language>(
180+
Object.entries(languageNames).map(([enumVal, name]) => [name, Number(enumVal) as Language])
181+
);
182+
183+
/** Convert indexer language display names to Language enum values. Unrecognized names are skipped. */
184+
function indexerLanguagesToEnum(names: string[]): Language[] {
185+
const result: Language[] = [];
186+
for (const name of names) {
187+
const enumVal = languageNameToEnum.get(name);
188+
if (enumVal !== undefined) {
189+
result.push(enumVal);
190+
}
191+
}
192+
return result;
193+
}
194+
155195
/**
156196
* Get serializable parsed info for frontend display
157197
*/
158-
export function getParsedInfo(parsed: ParseResult): ParsedInfo {
198+
export function getParsedInfo(parsed: ParseResult, indexerLangs?: string[]): ParsedInfo {
199+
const useIndexer = indexerLangs && indexerLangs.length > 0;
200+
const effectiveLangs = useIndexer ? indexerLanguagesToEnum(indexerLangs) : parsed.languages;
201+
159202
return {
160-
source: sourceNames[parsed.source] || 'Unknown',
203+
source: sourceDisplayNames[parsed.source] || 'Unknown',
161204
resolution: resolutionNames[parsed.resolution] || 'Unknown',
162-
modifier: modifierNames[parsed.modifier] || 'None',
163-
languages: parsed.languages.map((l) => languageNames[l] || 'Unknown'),
205+
modifier: modifierDisplayNames[parsed.modifier] || 'None',
206+
languages: effectiveLangs.map((l) => languageNames[l] || 'Unknown'),
207+
languageSource: useIndexer ? 'Indexer' : 'Title',
164208
releaseGroup: parsed.releaseGroup,
165209
year: parsed.year,
166210
edition: parsed.edition,
@@ -181,14 +225,15 @@ function evaluateCondition(
181225
condition: ConditionData,
182226
parsed: ParseResult,
183227
title: string,
184-
patternMatches?: Map<string, boolean>
228+
patternMatches?: Map<string, boolean>,
229+
indexerLangs?: Language[]
185230
): ConditionEvalResult {
186231
switch (condition.type) {
187232
case 'release_title':
188233
return evaluatePattern(condition, title, patternMatches);
189234

190235
case 'language':
191-
return evaluateLanguage(condition, parsed);
236+
return evaluateLanguage(condition, parsed, indexerLangs);
192237

193238
case 'source':
194239
return evaluateSource(condition, parsed);
@@ -262,13 +307,21 @@ function evaluatePattern(
262307
/**
263308
* Evaluate language condition
264309
*/
265-
function evaluateLanguage(condition: ConditionData, parsed: ParseResult): ConditionEvalResult {
310+
function evaluateLanguage(
311+
condition: ConditionData,
312+
parsed: ParseResult,
313+
indexerLangs?: Language[]
314+
): ConditionEvalResult {
266315
if (!condition.languages || condition.languages.length === 0) {
267316
return { matched: false, expected: 'No languages defined', actual: 'N/A' };
268317
}
269318

270-
const parsedLangNames = parsed.languages.map((l) => languageNames[l] || 'Unknown');
271-
const actual = parsedLangNames.length > 0 ? parsedLangNames.join(', ') : 'None detected';
319+
// Prefer indexer languages when available
320+
const effectiveLangs =
321+
indexerLangs && indexerLangs.length > 0 ? indexerLangs : parsed.languages;
322+
323+
const langNames = effectiveLangs.map((l) => languageNames[l] || 'Unknown');
324+
const actual = langNames.length > 0 ? langNames.join(', ') : 'None detected';
272325

273326
const expectedParts: string[] = [];
274327
for (const lang of condition.languages) {
@@ -284,7 +337,7 @@ function evaluateLanguage(condition: ConditionData, parsed: ParseResult): Condit
284337
const langEnum = Language[lang.name as keyof typeof Language];
285338
if (langEnum === undefined) continue;
286339

287-
const hasLanguage = parsed.languages.includes(langEnum);
340+
const hasLanguage = effectiveLangs.includes(langEnum);
288341

289342
if (lang.except) {
290343
if (hasLanguage) return { matched: false, expected, actual };
@@ -477,12 +530,19 @@ export function evaluateCustomFormat(
477530
conditions: ConditionData[],
478531
parsed: ParseResult,
479532
title: string,
480-
patternMatches?: Map<string, boolean>
533+
patternMatches?: Map<string, boolean>,
534+
indexerLangNames?: string[]
481535
): EvaluationResult {
536+
// Convert indexer language names to enum values once
537+
const indexerLangs =
538+
indexerLangNames && indexerLangNames.length > 0
539+
? indexerLanguagesToEnum(indexerLangNames)
540+
: undefined;
541+
482542
const results: ConditionResult[] = [];
483543

484544
for (const condition of conditions) {
485-
const evalResult = evaluateCondition(condition, parsed, title, patternMatches);
545+
const evalResult = evaluateCondition(condition, parsed, title, patternMatches, indexerLangs);
486546
const passes = condition.negate ? !evalResult.matched : evalResult.matched;
487547

488548
results.push({

src/lib/shared/pcd/display.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ export interface ParsedInfo {
193193
resolution: string;
194194
modifier: string;
195195
languages: string[];
196+
languageSource: 'Indexer' | 'Title';
196197
releaseGroup: string | null;
197198
year: number;
198199
edition: string | null;

src/routes/api/v1/entity-testing/evaluate/+server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const POST: RequestHandler = async ({ request }) => {
5858
return {
5959
releaseId: release.id,
6060
title: release.title,
61-
parsed: parsed ? getParsedInfo(parsed) : undefined,
61+
parsed: parsed ? getParsedInfo(parsed, release.languages) : undefined,
6262
cfMatches: {}
6363
};
6464
});
@@ -108,14 +108,14 @@ export const POST: RequestHandler = async ({ request }) => {
108108
continue;
109109
}
110110

111-
const result = evaluateCustomFormat(cf.conditions, parsed, release.title, patternMatches);
111+
const result = evaluateCustomFormat(cf.conditions, parsed, release.title, patternMatches, release.languages);
112112
cfMatches[cf.name] = result.matches;
113113
}
114114

115115
return {
116116
releaseId: release.id,
117117
title: release.title,
118-
parsed: getParsedInfo(parsed),
118+
parsed: getParsedInfo(parsed, release.languages),
119119
cfMatches
120120
};
121121
});

src/routes/quality-profiles/entity-testing/[databaseId]/+page.svelte

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@
100100
releases: entity.releases.map((r) => ({
101101
id: r.id,
102102
title: r.title,
103-
type: entity.type
103+
type: entity.type,
104+
languages: r.languages.length > 0 ? r.languages : undefined
104105
}))
105106
})
106107
});
@@ -359,15 +360,15 @@
359360
<ActionButton
360361
icon={Sliders}
361362
hasDropdown={true}
362-
dropdownPosition="middle"
363+
dropdownPosition="right"
363364
square={!selectedProfile}
364365
>
365366
{#if selectedProfile}
366367
<span class="ml-2 text-sm text-neutral-700 dark:text-neutral-300"
367368
>{selectedProfile.name}</span
368369
>
369370
{/if}
370-
<Dropdown slot="dropdown" position="middle">
371+
<Dropdown slot="dropdown" position="right">
371372
<DropdownItem
372373
label="No Profile"
373374
selected={selectedProfileId === null}
@@ -382,8 +383,8 @@
382383
{/each}
383384
</Dropdown>
384385
</ActionButton>
385-
<ActionButton icon={Clapperboard} hasDropdown={true} dropdownPosition="middle">
386-
<Dropdown slot="dropdown" position="middle">
386+
<ActionButton icon={Clapperboard} hasDropdown={true} dropdownPosition="right">
387+
<Dropdown slot="dropdown" position="right">
387388
<DropdownItem
388389
icon={Film}
389390
label="Movies"

0 commit comments

Comments
 (0)