Skip to content
Merged
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
1 change: 1 addition & 0 deletions common/known-removable-phrases.js
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,7 @@ const knownRemovablePhrases = [
"Taste Film presents",
"Tatau to Tattoo:",
"TFFF:",
"- TFFF",
"Tibet Film Festival London:",
"Tim Burton's ",
"Times+:",
Expand Down
10 changes: 10 additions & 0 deletions common/normalize-title.js
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,16 @@ function normalizeTitle(title, options) {
title = hasBrackets[1];
}

// Collapse multiple years
title = title.replace(/\((\d{4}),\s+(\d{4})\)/i, "($1/$2)");

// Remove diretor or other notes from bracketted years
// E.g. Convert "(1964, Glauber Rocha)" to "(1964)"
const hasYearWithComment = title.match(/^(.*?)\s+\((\d{4}),\s[^)]+\)/i);
if (hasYearWithComment) {
title = `${hasYearWithComment[1]} (${hasYearWithComment[2]})`;
}

const hasSlavicPremier = title.match(/Кинопремиера на "([^"]+)" /i);
if (hasSlavicPremier) {
title = hasSlavicPremier[1];
Expand Down
12 changes: 7 additions & 5 deletions common/standardize-prefixing-for-theatre-performances.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const yearRangeMatcher = /(\d{2})(\d{2})[-|/](\d{2})/;
const shortYearRangeMatcher = /(\d{2})[-|/](\d{2})/;
const yearSuffixMatcher = /\(\d{4}\)$/;
const ownerMatcher = /:\s+[^\s]+['|’]s/;
const baselineYear = new Date().getFullYear() - 1; // Keep the year at 2025
const getBaselineYear = () => new Date().getFullYear();

// Les Miserables
function standardizePrefixingForLesMiserablesPerformances(title) {
Expand Down Expand Up @@ -90,7 +90,7 @@ function standardizePrefixingForMetropolitanOperaPerformances(title, options) {

updatedPrefixTitle = updatedPrefixTitle.replace(ownerMatcher, ":");

let year = baselineYear;
let year = getBaselineYear();

const fullYearRangeMatch = updatedPrefixTitle.match(fullYearRangeMatcher);
if (fullYearRangeMatch) {
Expand All @@ -117,7 +117,7 @@ function standardizePrefixingForMetropolitanOperaPerformances(title, options) {
}

const yearNumber = parseInt(year, 10);
const isFutureYear = yearNumber > baselineYear;
const isFutureYear = yearNumber > getBaselineYear();
year = isFutureYear ? `${yearNumber - 1}` : year;
updatedPrefixTitle = `${updatedPrefixTitle} (${year})`;

Expand All @@ -144,7 +144,9 @@ const rboPrefixes = [
/Royal Opera Live[:|\s]/i,
/Royal Ballet Live[:|\s]/i,
/RBO[:|\s]/i,
/Royal Ballet and Opera Live[:|\s]/i,
/Royal Ballet and Opera[:|\s]/i,
/Royal Ballet & Opera Live[:|\s]/i,
/Royal Ballet & Opera[:|\s]/i,
/Royal Opera House[:|\s]/i,
/^The Royal Ballet[:|\s]/i,
Expand Down Expand Up @@ -176,7 +178,7 @@ function standardizePrefixingForRoyalBalletOperaPerformances(title) {
":",
);

let year = baselineYear;
let year = getBaselineYear();

const fullYearRangeMatch = updatedPrefixTitle.match(fullYearRangeMatcher);
if (fullYearRangeMatch) {
Expand All @@ -200,7 +202,7 @@ function standardizePrefixingForRoyalBalletOperaPerformances(title) {
if (yearSuffixMatch) {
const yearSuffix = yearSuffixMatch[0].replaceAll(/[()]/g, "");
const yearNumber = parseInt(yearSuffix, 10);
const isFutureYear = yearNumber > baselineYear;
const isFutureYear = yearNumber > getBaselineYear();
year = isFutureYear ? `${yearNumber - 1}` : yearSuffix;
updatedPrefixTitle = updatedPrefixTitle.replace(yearMatcher, "");
}
Expand Down
2 changes: 2 additions & 0 deletions common/tests/normalize-title.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const normalizeTitle = require("../normalize-title");
const testTitles = require("./test-titles.json");

describe("Normalise Title", () => {
jest.useFakeTimers().setSystemTime(new Date("2025-06-15"));

test.each(testTitles)(
"normalizes the title '$input'",
({ input, output }) => {
Expand Down
98 changes: 93 additions & 5 deletions common/tests/test-titles.json
Original file line number Diff line number Diff line change
Expand Up @@ -27893,7 +27893,7 @@
},
{
"input": "The Gothique Film Society presents The Flying Serpent (1946) And It’s Alive (1974, 35mm)",
"output": "flying serpent (1946) its alive"
"output": "flying serpent (1946) its alive (1974)"
},
{
"input": "Honey Don't! Unlimited Screening",
Expand Down Expand Up @@ -71505,7 +71505,7 @@
},
{
"input": "New York Ninja (1984, 2021) vs Miami Connection (1987) - Pick Your Trash!",
"output": "new york ninja (1984 2021) vs miami connection (1987)"
"output": "new york ninja (1984/2021) vs miami connection (1987)"
},
{
"input": "Cheese & Port Afternoon (October)",
Expand Down Expand Up @@ -79005,11 +79005,11 @@
},
{
"input": "Hi, Mom! (1970, Brian De Palma) 4K Restoration - TFFF",
"output": "hi mom"
"output": "hi mom (1970)"
},
{
"input": "The Dancing Hawk (1978, Grzegorz Królikiewicz) UK Theatrical Premiere - TFFF",
"output": "dancing hawk"
"output": "dancing hawk (1978)"
},
{
"input": "All Power to the People: The Black Panther Party at 60",
Expand Down Expand Up @@ -79293,7 +79293,7 @@
},
{
"input": "Black God, White Devil (1964, Glauber Rocha) 4k Restoration - TFFF",
"output": "black god white devil"
"output": "black god white devil (1964)"
},
{
"input": "Faces of Death (1978) world theatrical premiere + Video intro by Daniel Goldhaber - TFFF",
Expand Down Expand Up @@ -79862,5 +79862,93 @@
{
"input": "THE EXCHANGE CINEMA CLUB PRESENTS - CHEF",
"output": "chef"
},
{
"input": "ROYAL BALLET & OPERA LIVE: ALICE’S ADVENTURES IN WONDERLAND",
"output": "royal ballet opera 2025 alices adventures in wonderland"
},
{
"input": "ROYAL BALLET & OPERA LIVE: CARMEN",
"output": "royal ballet opera 2025 carmen"
},
{
"input": "ROYAL BALLET & OPERA LIVE: COSÌ FAN TUTTE",
"output": "royal ballet opera 2025 cosi fan tutte"
},
{
"input": "ROYAL BALLET & OPERA LIVE: GÖTTERDÄMMERUNG",
"output": "royal ballet opera 2025 gotterdammerung"
},
{
"input": "ROYAL BALLET & OPERA LIVE: LA FANCIULLA DEL WEST",
"output": "royal ballet opera 2025 la fanciulla del west"
},
{
"input": "ROYAL BALLET & OPERA LIVE: MACBETH",
"output": "royal ballet opera 2025 macbeth"
},
{
"input": "ROYAL BALLET & OPERA LIVE: MANON",
"output": "royal ballet opera 2025 manon"
},
{
"input": "ROYAL BALLET & OPERA LIVE: SWAN LAKE",
"output": "royal ballet opera 2025 swan lake"
},
{
"input": "ROYAL BALLET & OPERA LIVE: THE NUTCRACKER",
"output": "royal ballet opera 2025 the nutcracker"
},
{
"input": "ROYAL BALLET & OPERA LIVE: TOSCA",
"output": "royal ballet opera 2025 tosca"
},
{
"input": "Moana (Live Action) (2026) Sing-along",
"output": "moana (2026)"
},
{
"input": "Black God, White Devil (1964, Glauber Rocha)- TFFF",
"output": "black god white devil (1964)"
},
{
"input": "Hi, Mom! (1970, Brian De Palma) - TFFF",
"output": "hi mom (1970)"
},
{
"input": "All I Ever Wanted",
"output": "all i ever wanted"
},
{
"input": "Harbinger",
"output": "harbinger"
},
{
"input": "Rise of the Footsoldier: All Day Marathon",
"output": "rise of the footsoldier all day marathon"
},
{
"input": "Rise of the Footsoldier: Retribution",
"output": "rise of the footsoldier retribution"
},
{
"input": "Shaun the Sheep Movie (2015)",
"output": "shaun the sheep movie (2015)"
},
{
"input": "Shaun The Sheep Movie: Farmageddon (2019)",
"output": "shaun the sheep movie farmageddon (2019)"
},
{
"input": "The Pirates! In an Adventure with Scientists! (2012)",
"output": "pirates in an adventure with scientists (2012)"
},
{
"input": "Wallace & Gromit: A Close Shave/A Matter of Loaf and Death",
"output": "wallace gromit a close shave/a matter of loaf death"
},
{
"input": "Cine-Real presents: One Flew Over The Cuckoo's Nest",
"output": "one flew over the cuckoos nest"
}
]