Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion harmonizer/release_types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,67 @@ describe('release types', () => {
['should detect type before comment', 'Enter Suicidal Angels - EP (Remastered 2021)', new Set(['EP'])],
['should detect EP suffix', 'Zero Distance EP', new Set(['EP'])],
['should detect demo type', 'Parasite Inc. (Demo)', new Set(['Demo'])],
// Soundtrack releases
...([
// Titles with original/official <medium> soundtrack
'The Lord of the Rings: The Return of the King (Original Motion Picture Soundtrack)',
'The Bodyguard - Original Soundtrack Album',
'Plants Vs. Zombies (Original Video Game Soundtrack)',
'Stardew Valley (Original Game Soundtrack)',
'L.A. Noire Official Soundtrack',
'Tarzan (Deutscher Original Film-Soundtrack)',
'Die Eiskönigin Völlig Unverfroren (Deutscher Original Film Soundtrack)',
// Soundtrack from the ... <medium>
'KPop Demon Hunters (Soundtrack from the Netflix Film)',
'The Witcher: Season 2 (Soundtrack from the Netflix Original Series)',
'The White Lotus (Soundtrack from the HBO® Original Limited Series)',
'Inception (Music from the Motion Picture)',
// Releases referring to score instead of soundtrack
'Fantastic Mr. Fox - Additional Music From The Original Score By Alexandre Desplat - The Abbey Road Mixes',
'Scott Pilgrim Vs. The World (Original Score Composed by Nigel Godrich)',
'F1® The Movie (Original Score By Hans Zimmer)',
'EUPHORIA SEASON 2 OFFICIAL SCORE (FROM THE HBO ORIGINAL SERIES)',
'The Bible (Official Score Soundtrack)',
'The Good Wife (The Official TV Score)',
// German release titles
'Get Up (Der Original Soundtrack zum Kinofilm)',
'Ein Mädchen namens Willow - Soundtrack zum Film',
'Das Boot (Soundtrack zur TV Serie, zweite Staffel)',
// Swedish release titles
'Fucking Åmål - Musiken från filmen',
'Fejkpatient (Musik från TV-serien)',
'Kärlek Fårever (Soundtrack till Netflix-filmen)',
// Norwegian release titles
'Kvitebjørn (Musikken fra filmen)',
'Døden på Oslo S (Musikken fra teaterforestillingen)',
// Musical releases
'The Lion King: Original Broadway Cast Recording',
].map((
title,
): FunctionSpec<typeof guessTypesFromTitle>[number] => [
`should detect soundtrack type (${title})`,
title,
new Set(['Soundtrack']),
])),
];

const passingCaseSensitiveCases: FunctionSpec<typeof guessTypesFromTitle> = [
// Soundtrack releases
...([
// Releases with OST abbreviation
'O.S.T. Das Boot',
'Alvin & The Chipmunks / OST',
].map((
title,
): FunctionSpec<typeof guessTypesFromTitle>[number] => [
`should detect soundtrack type (${title})`,
title,
new Set(['Soundtrack']),
])),
];

describe('exact case match', () => {
passingCases.forEach(([description, input, expected]) => {
[...passingCases, ...passingCaseSensitiveCases].forEach(([description, input, expected]) => {
it(description, () => {
assertEquals(guessTypesFromTitle(input), expected);
});
Expand Down
53 changes: 45 additions & 8 deletions harmonizer/release_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,60 @@ export function guessTypesForRelease(release: HarmonyRelease): Iterable<ReleaseG
return types;
}

const detectTypesPatterns = [
const releaseGroupTypeMatchers: Array<{ type?: ReleaseGroupType; pattern: RegExp }> = [
// Commonly used for Bandcamp releases
/\s\((EP|Single|Live|Demo)\)(?:\s\(.*?\))?$/i,
{ pattern: /\s\((EP|Single|Live|Demo)\)(?:\s\(.*?\))?$/i },
// iTunes singles and EPs
/\s- (EP|Single|Live)(?:\s\(.*?\))?$/i,
{ pattern: /\s- (EP|Single|Live)(?:\s\(.*?\))?$/i },
// Generic "EP" suffix
/\s(EP)(?:\s\(.*?\))?$/i,
{ pattern: /\s(EP)(?:\s\(.*?\))?$/i },
// Common soundtrack title: "Official/Original <Medium> Soundtrack" and "Original Score"
{
type: 'Soundtrack',
pattern:
/(?:Original\s|Official\s)(?:(?:(?:Video\s)?Game|Motion Picture|Film|Movie|Television|TV|(?:(?:TV|Television)[\s-]?)?(?:Mini[\s-]?)?Series?|Musical)[\s-])?(?:Soundtrack|Score)/i,
},
// Common soundtrack title: "Soundtrack from the <Medium>", should also match "Soundtrack from the <Streaming service> <Medium>"
{
type: 'Soundtrack',
pattern:
/(?:Soundtrack|Score|Music)\s(?:(?:from|to) the)\s(?:.+[\s-])?(?:(?:Video\s)?Game|Motion Picture|Film|Movie|(?:(?:TV|Television)[\s-]?)?(?:Mini[\s-]?)?Series|Musical)/i,
},
// Common soundtrack title. Starting or ending with O.S.T. or OST (with or without wrapping parenthesis). Note: it's case sensitive.
{
type: 'Soundtrack',
pattern: /(?:^(?:\(O\.S\.T\.\)|O\.S\.T\.|OST|\(OST\))\s.+|.+\s(?:\(O\.S\.T\.\)|O\.S\.T\.|OST|\(OST\))$)/,
},
// Common musical soundtrack release titles
{ type: 'Soundtrack', pattern: /Original (?:.+\s)?Cast Recording/i },
// Common German soundtrack release titles
{
type: 'Soundtrack',
pattern: /(?:Soundtrack|Musik)\s(?:zum|zur)\s(?:.+[\s-])?(?:(?:Kino)?Film|Theaterstück|(?:TV[\s-]?)?Serie)/i,
},
// Common Swedish soundtrack release titles
{
type: 'Soundtrack',
pattern:
/(?:Soundtrack|Musik(?:en)?)\s(?:från|till|ur)\s(?:.+[\s-])?(?:Film(?:en)?|(?:TV[\s-]?)?(?:Mini[\s-]?)?Serien?|Musikalen)/i,
},
// Common Norwegian soundtrack release titles
{ type: 'Soundtrack', pattern: /Musikk(?:en)? (?:fra) (?:Filmen|TV[\s-]serien|(?:teater)?forestillingen)/i },
];

/** Guesses a release type from a title. */
export function guessTypesFromTitle(title: string): Set<ReleaseGroupType> {
const types = new Set<ReleaseGroupType>();
detectTypesPatterns.forEach((pattern) => {
const match = title.match(pattern);
if (match) {
types.add(capitalizeReleaseType(match[1]));
releaseGroupTypeMatchers.forEach((matcher) => {
const match = title.match(matcher.pattern);
if (!match) {
return;
}
const type = match[1] || matcher.type;
if (!type) {
return;
}
types.add(capitalizeReleaseType(type));
});
return types;
}
Expand Down