Skip to content

Commit

Permalink
Create script to generate combined data blob
Browse files Browse the repository at this point in the history
  • Loading branch information
alistairjcbrown committed Nov 2, 2024
1 parent 5f248d1 commit c8e5eff
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 53 deletions.
57 changes: 57 additions & 0 deletions common/get-movie-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const { MovieDb } = require("moviedb-promise");
const { dailyCache } = require("./cache");
require("dotenv").config();

const moviedb = new MovieDb(process.env.MOVIEDB_API_KEY);

const searchMovieAndCacheResults = ({
slug,
year: yearValue,
normalizedTitle,
}) =>
dailyCache(`moviedb-search-${yearValue || "no-year"}-${slug}`, async () => {
const getPayload = (additional = {}) => ({
query: normalizedTitle,
...additional,
});

// If there's no year provided, just search for the title
if (!yearValue) {
return moviedb.searchMovie(getPayload());
}

const year = parseInt(yearValue, 10);

// Try to find a movie first released on that year
let search = await moviedb.searchMovie(
getPayload({ primary_release_year: year }),
);

// If there's no matches, then try to find a movie with some release related
// to that year
if (search.results.length === 0) {
search = await moviedb.searchMovie(getPayload({ year }));
}

// If there's no matches, sometimes the movie listing has the year off by 1,
// so try to find a movie with some release related to the next year
if (search.results.length === 0) {
return moviedb.searchMovie(getPayload({ year: year + 1 }));
}

return search;
});

const getMovieInfoAndCacheResults = ({ id }) =>
dailyCache(`moviedb-info-${id}`, async () => {
const payload = {
id,
append_to_response: "credits,external_ids,keywords,release_dates,videos",
};
return moviedb.movieInfo(payload);
});

module.exports = {
searchMovieAndCacheResults,
getMovieInfoAndCacheResults,
};
56 changes: 4 additions & 52 deletions common/hydrate.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,10 @@
const slugify = require("slugify");
const { MovieDb } = require("moviedb-promise");
const { dailyCache } = require("./cache");
const { parseMinsToMs } = require("./utils");
const normalizeTitle = require("./normalize-title");
require("dotenv").config();

const moviedb = new MovieDb(process.env.MOVIEDB_API_KEY);

const searchMovieAndCacheResults = ({
slug,
year: yearValue,
normalizedTitle,
}) =>
dailyCache(`moviedb-search-${yearValue || "no-year"}-${slug}`, async () => {
const getPayload = (additional = {}) => ({
query: normalizedTitle,
...additional,
});

// If there's no year provided, just search for the title
if (!yearValue) {
return moviedb.searchMovie(getPayload());
}

const year = parseInt(yearValue, 10);

// Try to find a movie first released on that year
let search = await moviedb.searchMovie(
getPayload({ primary_release_year: year }),
);

// If there's no matches, then try to find a movie with some release related
// to that year
if (search.results.length === 0) {
search = await moviedb.searchMovie(getPayload({ year }));
}

// If there's no matches, sometimes the movie listing has the year off by 1,
// so try to find a movie with some release related to the next year
if (search.results.length === 0) {
return moviedb.searchMovie(getPayload({ year: year + 1 }));
}

return search;
});

const getMovieInfoAndCacheResults = ({ id }) =>
dailyCache(`moviedb-info-${id}`, async () => {
const payload = {
id,
append_to_response: "credits,external_ids,keywords,videos",
};
return moviedb.movieInfo(payload);
});
const {
searchMovieAndCacheResults,
getMovieInfoAndCacheResults,
} = require("./get-movie-data");

const getMovieTitleAndYearFrom = (title) => {
const hasYear = title.trim().match(/^(.*?)\s*\((\d{4})\)$/);
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@
"output:highlight-hydration-misses-for-review": "node ./scripts/highlight-hydration-misses-for-review.js",
"clear:cache": "rm -rf ./cache && git checkout ./cache",
"clear:output": "rm -rf ./output && git checkout ./output",
"populate:output": "./scripts/get-latest-release-assets.sh"
"populate:output": "./scripts/get-latest-release-assets.sh",
"generate:combined-data": "node ./scripts/generate-combined-data.js"
},
"author": "Alistair Brown <github@alistairjcbrown.com>",
"license": "MIT",
"dependencies": {
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"cheerio": "^1.0.0-rc.12",
"compress-json": "^3.1.0",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"ics": "^3.7.6",
"moviedb-promise": "^4.0.7",
"nanoid": "^3.3.7",
"playwright": "^1.48.1",
"replace-special-characters": "^1.2.7",
"slugify": "^1.6.6"
Expand Down
179 changes: 179 additions & 0 deletions scripts/generate-combined-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
const { writeFileSync } = require("node:fs");
const path = require("node:path");
const { nanoid } = require("nanoid");
const { compress, trimUndefinedRecursively } = require("compress-json");
const getSites = require("../common/get-sites");
const normalizeTitle = require("../common/normalize-title");
const { getMovieInfoAndCacheResults } = require("../common/get-movie-data");
const { parseMinsToMs } = require("../common/utils");

const getId = () => nanoid(8);

const getCertification = ({ release_dates: { results } }) => {
const result = results.find(({ iso_3166_1: locale }) => locale === "GB");
if (!result) return undefined;

const { release_dates: releaseDates } = result;
const releaseDateWithCertification = releaseDates.find(
({ certification }) => !!certification,
);

if (!releaseDateWithCertification) return undefined;
return releaseDateWithCertification.certification;
};

const getDirectors = ({ credits: { crew } }) =>
crew
.filter(({ job }) => job.toLowerCase() === "director")
.map(({ id, name }) => ({ id, name }));

const getActors = ({ credits: { cast } }) =>
cast
.slice(0, 10)
.filter(({ popularity }) => popularity >= 5)
.map(({ id, name }) => ({ id, name }));

const getGenres = ({ genres }) => genres;

const getYoutubeTrailer = ({ videos: { results } }) => {
const trailer = results.find(
({ type, site }) =>
type.toLowerCase() === "trailer" && site.toLowerCase() === "youtube",
);
return trailer ? trailer.key : undefined;
};

const getImddId = ({ external_ids: externalIds = {} }) => externalIds.imdb_id;

const data = getSites().reduce((mapping, site) => {
let attributes;
let shows;
try {
attributes = require(
path.join(__dirname, "..", "cinemas", `${site}`, "attributes.js"),
);
shows = require(path.join(__dirname, "..", "output", `${site}-shows.json`));
} catch (e) {
return mapping;
}
return { ...mapping, [site]: { attributes, shows } };
}, {});

const siteData = {
venues: {},
people: {},
genres: {},
movies: {},
};

(async function () {
for (cinema in data) {
console.log(`[🎞️ Cinema: ${cinema}]`);
const venueId = getId();
const {
attributes: { name, url, address, geo },
shows,
} = data[cinema];

siteData.venues[venueId] = {
id: venueId,
name,
url,
address,
geo,
};

for (show of shows) {
const { title, url, overview, performances, moviedb } = show;

let movieInfo;
if (moviedb) {
const outputTitle = title.slice(0, 35);
process.stdout.write(
` - Retriving data for ${outputTitle} ... ${"".padEnd(35 - outputTitle.length, " ")}`,
);
try {
movieInfo = await getMovieInfoAndCacheResults(moviedb);
console.log(`\t✅ Retrieved`);
} catch (e) {
console.log(`\t❌ Error retriving`);
throw e;
}
}

const movieId = movieInfo ? movieInfo.id : getId();
if (!siteData.movies[movieId]) {
if (movieInfo) {
const directors = getDirectors(movieInfo);
const actors = getActors(movieInfo);
const genres = getGenres(movieInfo);

directors.forEach((crew) => (siteData.people[crew.id] = crew));
actors.forEach((cast) => (siteData.people[cast.id] = cast));
genres.forEach((genre) => (siteData.genres[genre.id] = genre));

siteData.movies[movieId] = {
id: movieId,
title: movieInfo.title,
normalizedTitla: normalizeTitle(movieInfo.title),
certification: getCertification(movieInfo),
overview: movieInfo.overview,
year: movieInfo.release_date.split("-")[0],
duration: parseMinsToMs(movieInfo.runtime),
directors: directors.map(({ id }) => id),
actors: actors.map(({ id }) => id),
genres: genres.map(({ id }) => id),
imdbId: getImddId(movieInfo),
youtubeTrailer: getYoutubeTrailer(movieInfo),
posterPath: movieInfo.poster_path,
showings: {},
performances: [],
};
} else {
siteData.movies[movieId] = {
id: movieId,
title: title,
isUnmatched: true,
showings: {},
performances: [],
};
}
}

const showingId = getId();
const movie = siteData.movies[movieId];

movie.showings[showingId] = {
id: showingId,
venueId,
title: title !== movie.title ? title : undefined,
url,
overview,
};

movie.performances = movie.performances.concat(
performances.map(({ time, notes, bookingUrl }) => ({
showingId,
time,
notes: notes !== "" ? notes : undefined,
bookingUrl,
})),
);
}
console.log(" ");
}

process.stdout.write(`Compressing data ... `);
try {
const dataFile = `./output/combined-data.json`;
trimUndefinedRecursively(siteData);
const compressed = compress(siteData).toString();
console.log(`✅ Compressed`);

writeFileSync(dataFile, compressed);
console.log(`🗂️ Combined file created`);
} catch (e) {
console.log(`\t❌ Error compressing`);
throw e;
}
})();
File renamed without changes.

0 comments on commit c8e5eff

Please sign in to comment.