Skip to content

Commit f7110f7

Browse files
authored
merge: Detect soundtrack based on the release title
Implements auto-detection of soundtrack releases based the title based on a wide range of common title patterns. Supports both common title formats in English (which often are used for releases in other languages as well), German, Swedish, and Norwegian. I've added test cases for a wide variation of soundtracks. In contrast to the existing patterns for detecting release group types from the title, the pattern does not always capture the type and as such I had to come up with a way of associating the pattern to a type. I think this can probably be useful for when detecting more release group types from titles in the future.
2 parents 187daab + 24a9fbb commit f7110f7

File tree

2 files changed

+106
-9
lines changed

2 files changed

+106
-9
lines changed

harmonizer/release_types.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,67 @@ describe('release types', () => {
4242
['should detect type before comment', 'Enter Suicidal Angels - EP (Remastered 2021)', new Set(['EP'])],
4343
['should detect EP suffix', 'Zero Distance EP', new Set(['EP'])],
4444
['should detect demo type', 'Parasite Inc. (Demo)', new Set(['Demo'])],
45+
// Soundtrack releases
46+
...([
47+
// Titles with original/official <medium> soundtrack
48+
'The Lord of the Rings: The Return of the King (Original Motion Picture Soundtrack)',
49+
'The Bodyguard - Original Soundtrack Album',
50+
'Plants Vs. Zombies (Original Video Game Soundtrack)',
51+
'Stardew Valley (Original Game Soundtrack)',
52+
'L.A. Noire Official Soundtrack',
53+
'Tarzan (Deutscher Original Film-Soundtrack)',
54+
'Die Eiskönigin Völlig Unverfroren (Deutscher Original Film Soundtrack)',
55+
// Soundtrack from the ... <medium>
56+
'KPop Demon Hunters (Soundtrack from the Netflix Film)',
57+
'The Witcher: Season 2 (Soundtrack from the Netflix Original Series)',
58+
'The White Lotus (Soundtrack from the HBO® Original Limited Series)',
59+
'Inception (Music from the Motion Picture)',
60+
// Releases referring to score instead of soundtrack
61+
'Fantastic Mr. Fox - Additional Music From The Original Score By Alexandre Desplat - The Abbey Road Mixes',
62+
'Scott Pilgrim Vs. The World (Original Score Composed by Nigel Godrich)',
63+
'F1® The Movie (Original Score By Hans Zimmer)',
64+
'EUPHORIA SEASON 2 OFFICIAL SCORE (FROM THE HBO ORIGINAL SERIES)',
65+
'The Bible (Official Score Soundtrack)',
66+
'The Good Wife (The Official TV Score)',
67+
// German release titles
68+
'Get Up (Der Original Soundtrack zum Kinofilm)',
69+
'Ein Mädchen namens Willow - Soundtrack zum Film',
70+
'Das Boot (Soundtrack zur TV Serie, zweite Staffel)',
71+
// Swedish release titles
72+
'Fucking Åmål - Musiken från filmen',
73+
'Fejkpatient (Musik från TV-serien)',
74+
'Kärlek Fårever (Soundtrack till Netflix-filmen)',
75+
// Norwegian release titles
76+
'Kvitebjørn (Musikken fra filmen)',
77+
'Døden på Oslo S (Musikken fra teaterforestillingen)',
78+
// Musical releases
79+
'The Lion King: Original Broadway Cast Recording',
80+
].map((
81+
title,
82+
): FunctionSpec<typeof guessTypesFromTitle>[number] => [
83+
`should detect soundtrack type (${title})`,
84+
title,
85+
new Set(['Soundtrack']),
86+
])),
87+
];
88+
89+
const passingCaseSensitiveCases: FunctionSpec<typeof guessTypesFromTitle> = [
90+
// Soundtrack releases
91+
...([
92+
// Releases with OST abbreviation
93+
'O.S.T. Das Boot',
94+
'Alvin & The Chipmunks / OST',
95+
].map((
96+
title,
97+
): FunctionSpec<typeof guessTypesFromTitle>[number] => [
98+
`should detect soundtrack type (${title})`,
99+
title,
100+
new Set(['Soundtrack']),
101+
])),
45102
];
46103

47104
describe('exact case match', () => {
48-
passingCases.forEach(([description, input, expected]) => {
105+
[...passingCases, ...passingCaseSensitiveCases].forEach(([description, input, expected]) => {
49106
it(description, () => {
50107
assertEquals(guessTypesFromTitle(input), expected);
51108
});

harmonizer/release_types.ts

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,62 @@ export function guessTypesForRelease(release: HarmonyRelease): Iterable<ReleaseG
1111
return types;
1212
}
1313

14-
const detectTypesPatterns = [
14+
const releaseGroupTypeMatchers: Array<{ type?: ReleaseGroupType; pattern: RegExp }> = [
1515
// Commonly used for Bandcamp releases
16-
/\s\((EP|Single|Live|Demo)\)(?:\s\(.*?\))?$/i,
16+
{ pattern: /\s\((EP|Single|Live|Demo)\)(?:\s\(.*?\))?$/i },
1717
// iTunes singles and EPs
18-
/\s- (EP|Single|Live)(?:\s\(.*?\))?$/i,
18+
{ pattern: /\s- (EP|Single|Live)(?:\s\(.*?\))?$/i },
1919
// Generic "EP" suffix
20-
/\s(EP)(?:\s\(.*?\))?$/i,
20+
{ pattern: /\s(EP)(?:\s\(.*?\))?$/i },
21+
// Common soundtrack title: "Official/Original <Medium> Soundtrack" and "Original Score"
22+
{
23+
type: 'Soundtrack',
24+
pattern: /(?:Original|Official)(?:.*?)(?:Soundtrack|Score)/i,
25+
},
26+
// Common soundtrack title: "Soundtrack from the <Medium>", should also match "Soundtrack from the <Streaming service> <Medium>"
27+
{
28+
type: 'Soundtrack',
29+
pattern:
30+
/(?:Soundtrack|Score|Music)\s(?:(?:from|to) the)\s(?:.+[\s-])?(?:(?:Video\s)?Game|Motion Picture|Film|Movie|(?:(?:TV|Television)[\s-]?)?(?:Mini[\s-]?)?Series|Musical)/i,
31+
},
32+
// Common soundtrack title. Starting or ending with O.S.T. or OST (with or without wrapping parenthesis). Note: it's case sensitive.
33+
{
34+
type: 'Soundtrack',
35+
pattern: /(?:^(?:\(O\.S\.T\.\)|O\.S\.T\.|OST|\(OST\))\s.+|.+\s(?:\(O\.S\.T\.\)|O\.S\.T\.|OST|\(OST\))$)/,
36+
},
37+
// Common musical soundtrack release titles
38+
{ type: 'Soundtrack', pattern: /Original (?:.+\s)?Cast Recording/i },
39+
// Common German soundtrack release titles
40+
{
41+
type: 'Soundtrack',
42+
pattern: /(?:Soundtrack|Musik)\s(?:zum|zur)\s(?:.+[\s-])?(?:(?:Kino)?Film|Theaterstück|(?:TV[\s-]?)?Serie)/i,
43+
},
44+
// Common Swedish soundtrack release titles
45+
{
46+
type: 'Soundtrack',
47+
pattern:
48+
/(?:Soundtrack|Musik(?:en)?)\s(?:från|till|ur)\s(?:.+[\s-])?(?:Film(?:en)?|(?:TV[\s-]?)?(?:Mini[\s-]?)?Serien?|Musikalen)/i,
49+
},
50+
// Common Norwegian soundtrack release titles
51+
{ type: 'Soundtrack', pattern: /Musikk(?:en)? (?:fra) (?:Filmen|TV[\s-]serien|(?:teater)?forestillingen)/i },
2152
];
2253

2354
/** Guesses a release type from a title. */
2455
export function guessTypesFromTitle(title: string): Set<ReleaseGroupType> {
2556
const types = new Set<ReleaseGroupType>();
26-
detectTypesPatterns.forEach((pattern) => {
27-
const match = title.match(pattern);
28-
if (match) {
29-
types.add(capitalizeReleaseType(match[1]));
57+
releaseGroupTypeMatchers.forEach((matcher) => {
58+
if (matcher.type && types.has(matcher.type)) {
59+
// Release has already been assigned this type.
60+
return;
61+
}
62+
63+
const match = title.match(matcher.pattern);
64+
if (!match) {
65+
return;
66+
}
67+
const type = match[1] || matcher.type;
68+
if (type) {
69+
types.add(capitalizeReleaseType(type));
3070
}
3171
});
3272
return types;

0 commit comments

Comments
 (0)