Moved contents of seasoned_api up to root folder

This commit is contained in:
2022-08-19 01:03:27 +02:00
parent 0efc109992
commit 56262a45c8
134 changed files with 885 additions and 32 deletions

3
src/tmdb/.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

48
src/tmdb/cache.js Normal file
View File

@@ -0,0 +1,48 @@
const assert = require("assert");
const establishedDatabase = require("../database/database");
class Cache {
constructor(database) {
this.database = database || establishedDatabase;
this.queries = {
read:
'SELECT value, time_to_live, created_at, DATETIME("now", "localtime") as now, ' +
'DATETIME(created_at, "+" || time_to_live || " seconds") as expires ' +
"FROM cache WHERE key = ? AND now < expires",
create:
"INSERT OR REPLACE INTO cache (key, value, time_to_live) VALUES (?, ?, ?)"
};
}
/**
* Retrieve an unexpired cache entry by key.
* @param {String} key of the cache entry
* @returns {Object}
*/
get(key) {
return Promise.resolve()
.then(() => this.database.get(this.queries.read, [key]))
.then(row => {
assert(row, "Could not find cache entry with that key.");
return JSON.parse(row.value);
});
}
/**
* Insert cache entry with key and value.
* @param {String} key of the cache entry
* @param {String} value of the cache entry
* @param {Number} timeToLive the number of seconds before entry expires
* @returns {Object}
*/
set(key, value, timeToLive = 10800) {
const json = JSON.stringify(value);
return Promise.resolve()
.then(() =>
this.database.run(this.queries.create, [key, json, timeToLive])
)
.then(() => value);
}
}
module.exports = Cache;

293
src/tmdb/tmdb.js Normal file
View File

@@ -0,0 +1,293 @@
const moviedb = require("km-moviedb");
const redisCache = require("../cache/redis");
const { Movie, Show, Person, Credits, ReleaseDates } = require("./types");
const tmdbErrorResponse = (error, typeString = undefined) => {
if (error.status === 404) {
let message = error.response.body.status_message;
throw {
status: 404,
message: message.slice(0, -1) + " in tmdb."
};
} else if (error.status === 401) {
throw {
status: 401,
message: error.response.body.status_message
};
}
throw {
status: 500,
message: `An unexpected error occured while fetching ${typeString} from tmdb`
};
};
class TMDB {
constructor(apiKey, cache, tmdbLibrary) {
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
this.cache = cache || redisCache;
this.cacheTags = {
multiSearch: "mus",
movieSearch: "mos",
showSearch: "ss",
personSearch: "ps",
movieInfo: "mi",
movieCredits: "mc",
movieReleaseDates: "mrd",
movieImages: "mimg",
showInfo: "si",
showCredits: "sc",
personInfo: "pi",
personCredits: "pc",
miscNowPlayingMovies: "npm",
miscPopularMovies: "pm",
miscTopRatedMovies: "tpm",
miscUpcomingMovies: "um",
tvOnTheAir: "toa",
miscPopularTvs: "pt",
miscTopRatedTvs: "trt"
};
this.defaultTTL = 86400;
}
getFromCacheOrFetchFromTmdb(cacheKey, tmdbMethod, query) {
return new Promise((resolve, reject) =>
this.cache
.get(cacheKey)
.then(resolve)
.catch(() => this.tmdb(tmdbMethod, query))
.then(resolve)
.catch(error => reject(tmdbErrorResponse(error, tmdbMethod)))
);
}
/**
* Retrieve a specific movie by id from TMDB.
* @param {Number} identifier of the movie you want to retrieve
* @param {Boolean} add credits (cast & crew) for movie
* @param {Boolean} add release dates for every country
* @returns {Promise} succeeds if movie was found
*/
movieInfo(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.movieInfo}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "movieInfo", query)
.then(movie => this.cache.set(cacheKey, movie, this.defaultTTL))
.then(movie => Movie.convertFromTmdbResponse(movie));
}
/**
* Retrieve credits for a movie
* @param {Number} identifier of the movie to get credits for
* @returns {Promise} movie cast object
*/
movieCredits(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.movieCredits}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "movieCredits", query)
.then(credits => this.cache.set(cacheKey, credits, this.defaultTTL))
.then(credits => Credits.convertFromTmdbResponse(credits));
}
/**
* Retrieve release dates for a movie
* @param {Number} identifier of the movie to get release dates for
* @returns {Promise} movie release dates object
*/
movieReleaseDates(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.movieReleaseDates}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(
cacheKey,
"movieReleaseDates",
query
)
.then(releaseDates =>
this.cache.set(cacheKey, releaseDates, this.defaultTTL)
)
.then(releaseDates => ReleaseDates.convertFromTmdbResponse(releaseDates));
}
/**
* Retrieve a specific show by id from TMDB.
* @param {Number} identifier of the show you want to retrieve
* @param {String} type filter results by type (default show).
* @returns {Promise} succeeds if show was found
*/
showInfo(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.showInfo}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvInfo", query)
.then(show => this.cache.set(cacheKey, show, this.defaultTTL))
.then(show => Show.convertFromTmdbResponse(show));
}
showCredits(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.showCredits}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvCredits", query)
.then(credits => this.cache.set(cacheKey, credits, this.defaultTTL))
.then(credits => Credits.convertFromTmdbResponse(credits));
}
/**
* Retrieve a specific person id from TMDB.
* @param {Number} identifier of the person you want to retrieve
* @param {String} type filter results by type (default person).
* @returns {Promise} succeeds if person was found
*/
personInfo(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.personInfo}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "personInfo", query)
.then(person => this.cache.set(cacheKey, person, this.defaultTTL))
.then(person => Person.convertFromTmdbResponse(person));
}
personCredits(identifier) {
const query = { id: identifier };
const cacheKey = `tmdb/${this.cacheTags.personCredits}:${identifier}`;
return this.getFromCacheOrFetchFromTmdb(
cacheKey,
"personCombinedCredits",
query
)
.then(credits => this.cache.set(cacheKey, credits, this.defaultTTL))
.then(credits => Credits.convertFromTmdbResponse(credits));
}
multiSearch(search_query, page = 1, include_adult = true) {
const query = { query: search_query, page, include_adult };
const cacheKey = `tmdb/${this.cacheTags.multiSearch}:${page}:${search_query}:${include_adult}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchMulti", query)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response));
}
/**
* Retrive movie search results from TMDB.
* @param {String} text query you want to search for
* @param {Number} page representing pagination of results
* @returns {Promise} dict with query results, current page and total_pages
*/
movieSearch(search_query, page = 1, include_adult = true) {
const tmdbquery = { query: search_query, page, include_adult };
const cacheKey = `tmdb/${this.cacheTags.movieSearch}:${page}:${search_query}:${include_adult}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchMovie", tmdbquery)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response, "movie"));
}
/**
* Retrive show search results from TMDB.
* @param {String} text query you want to search for
* @param {Number} page representing pagination of results
* @returns {Promise} dict with query results, current page and total_pages
*/
showSearch(search_query, page = 1, include_adult = true) {
const tmdbquery = { query: search_query, page, include_adult };
const cacheKey = `tmdb/${this.cacheTags.showSearch}:${page}:${search_query}:${include_adult}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchTv", tmdbquery)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response, "show"));
}
/**
* Retrive person search results from TMDB.
* @param {String} text query you want to search for
* @param {Number} page representing pagination of results
* @returns {Promise} dict with query results, current page and total_pages
*/
personSearch(search_query, page = 1, include_adult = true) {
const tmdbquery = { query: search_query, page, include_adult };
const cacheKey = `tmdb/${this.cacheTags.personSearch}:${page}:${search_query}:${include_adult}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchPerson", tmdbquery)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response, "person"));
}
movieList(listname, page = 1) {
const query = { page: page };
const cacheKey = `tmdb/${this.cacheTags[listname]}:${page}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, listname, query)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response, "movie"));
}
showList(listname, page = 1) {
const query = { page: page };
const cacheKey = `tmdb/${this.cacheTags[listname]}:${page}`;
return this.getFromCacheOrFetchFromTmdb(cacheKey, listName, query)
.then(response => this.cache.set(cacheKey, response, this.defaultTTL))
.then(response => this.mapResults(response, "show"));
}
/**
* Maps our response from tmdb api to a movie/show object.
* @param {String} response from tmdb.
* @param {String} The type declared in listSearch.
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
*/
mapResults(response, type = undefined) {
let results = response.results.map(result => {
if (type === "movie" || result.media_type === "movie") {
const movie = Movie.convertFromTmdbResponse(result);
return movie.createJsonResponse();
} else if (type === "show" || result.media_type === "tv") {
const show = Show.convertFromTmdbResponse(result);
return show.createJsonResponse();
} else if (type === "person" || result.media_type === "person") {
const person = Person.convertFromTmdbResponse(result);
return person.createJsonResponse();
}
});
return {
results: results,
page: response.page,
total_results: response.total_results,
total_pages: response.total_pages
};
}
/**
* Wraps moviedb library to support Promises.
* @param {String} method function name in the library
* @param {Object} argument argument to function being called
* @returns {Promise} succeeds if callback succeeds
*/
tmdb(method, argument) {
return new Promise((resolve, reject) => {
const callback = (error, reponse) => {
if (error) {
return reject(error);
}
resolve(reponse);
};
if (!argument) {
this.tmdbLibrary[method](callback);
} else {
this.tmdbLibrary[method](argument, callback);
}
});
}
}
module.exports = TMDB;

7
src/tmdb/tmdb.ts Normal file
View File

@@ -0,0 +1,7 @@
import { Movie } from './types'
Movie('str', 123)
module.exports = TMDB;

7
src/tmdb/types.js Normal file
View File

@@ -0,0 +1,7 @@
const Movie = require('./types/movie.js')
const Show = require('./types/show.js')
const Person = require('./types/person.js')
const Credits = require('./types/credits.js')
const ReleaseDates = require('./types/releaseDates.js')
module.exports = { Movie, Show, Person, Credits, ReleaseDates }

64
src/tmdb/types.ts Normal file
View File

@@ -0,0 +1,64 @@
interface Movie {
adult: boolean;
backdrop: string;
genres: Genre[];
id: number;
imdb_id: number;
overview: string;
popularity: number;
poster: string;
release_date: Date;
rank: number;
runtime: number;
status: string;
tagline: string;
title: string;
vote_count: number;
}
interface Show {
adult: boolean;
backdrop: string;
episodes: number;
genres: Genre[];
id: number;
imdb_id: number;
overview: string;
popularity: number;
poster: string;
rank: number;
runtime: number;
seasons: number;
status: string;
tagline: string;
title: string;
vote_count: number;
}
interface Person {
birthday: Date;
deathday: Date;
id: number;
known_for: string;
name: string;
poster: string;
}
interface SearchResult {
adult: boolean;
backdrop_path: string;
id: number;
original_title: string;
release_date: Date;
poster_path: string;
popularity: number;
vote_average: number;
vote_counte: number;
}
interface Genre {
id: number;
name: string;
}
export { Movie, Show, Person, Genre }

113
src/tmdb/types/credits.js Normal file
View File

@@ -0,0 +1,113 @@
import Movie from "./movie";
import Show from "./show";
class Credits {
constructor(id, cast = [], crew = []) {
this.id = id;
this.cast = cast;
this.crew = crew;
this.type = "credits";
}
static convertFromTmdbResponse(response) {
const { id, cast, crew } = response;
const allCast = cast.map(cast => {
if (cast["media_type"]) {
if (cast.media_type === "movie") {
return CreditedMovie.convertFromTmdbResponse(cast);
} else if (cast.media_type === "tv") {
return CreditedShow.convertFromTmdbResponse(cast);
}
}
return new CastMember(
cast.character,
cast.gender,
cast.id,
cast.name,
cast.profile_path
);
});
const allCrew = crew.map(crew => {
if (cast["media_type"]) {
if (cast.media_type === "movie") {
return CreditedMovie.convertFromTmdbResponse(cast);
} else if (cast.media_type === "tv") {
return CreditedShow.convertFromTmdbResponse(cast);
}
}
return new CrewMember(
crew.department,
crew.gender,
crew.id,
crew.job,
crew.name,
crew.profile_path
);
});
return new Credits(id, allCast, allCrew);
}
createJsonResponse() {
return {
id: this.id,
cast: this.cast.map(cast => cast.createJsonResponse()),
crew: this.crew.map(crew => crew.createJsonResponse())
};
}
}
class CastMember {
constructor(character, gender, id, name, profile_path) {
this.character = character;
this.gender = gender;
this.id = id;
this.name = name;
this.profile_path = profile_path;
this.type = "person";
}
createJsonResponse() {
return {
character: this.character,
gender: this.gender,
id: this.id,
name: this.name,
profile_path: this.profile_path,
type: this.type
};
}
}
class CrewMember {
constructor(department, gender, id, job, name, profile_path) {
this.department = department;
this.gender = gender;
this.id = id;
this.job = job;
this.name = name;
this.profile_path = profile_path;
this.type = "person";
}
createJsonResponse() {
return {
department: this.department,
gender: this.gender,
id: this.id,
job: this.job,
name: this.name,
profile_path: this.profile_path,
type: this.type
};
}
}
class CreditedMovie extends Movie {}
class CreditedShow extends Show {}
module.exports = Credits;

62
src/tmdb/types/movie.js Normal file
View File

@@ -0,0 +1,62 @@
class Movie {
constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined,
releaseDate=undefined, rating=undefined, genres=undefined, productionStatus=undefined,
tagline=undefined, runtime=undefined, imdb_id=undefined, popularity=undefined) {
this.id = id;
this.title = title;
this.year = year;
this.overview = overview;
this.poster = poster;
this.backdrop = backdrop;
this.releaseDate = releaseDate;
this.rating = rating;
this.genres = genres;
this.productionStatus = productionStatus;
this.tagline = tagline;
this.runtime = runtime;
this.imdb_id = imdb_id;
this.popularity = popularity;
this.type = 'movie';
}
static convertFromTmdbResponse(response) {
const { id, title, release_date, overview, poster_path, backdrop_path, vote_average, genres, status,
tagline, runtime, imdb_id, popularity } = response;
const releaseDate = new Date(release_date);
const year = releaseDate.getFullYear();
const genreNames = genres ? genres.map(g => g.name) : undefined
return new Movie(id, title, year, overview, poster_path, backdrop_path, releaseDate, vote_average, genreNames, status,
tagline, runtime, imdb_id, popularity)
}
static convertFromPlexResponse(response) {
// console.log('response', response)
const { title, year, rating, tagline, summary } = response;
const _ = undefined
return new Movie(null, title, year, summary, _, _, _, rating, _, _, tagline)
}
createJsonResponse() {
return {
id: this.id,
title: this.title,
year: this.year,
overview: this.overview,
poster: this.poster,
backdrop: this.backdrop,
release_date: this.releaseDate,
rating: this.rating,
genres: this.genres,
production_status: this.productionStatus,
tagline: this.tagline,
runtime: this.runtime,
imdb_id: this.imdb_id,
type: this.type
}
}
}
module.exports = Movie;

70
src/tmdb/types/person.js Normal file
View File

@@ -0,0 +1,70 @@
class Person {
constructor(
id,
name,
poster = undefined,
birthday = undefined,
deathday = undefined,
adult = undefined,
placeOfBirth = undefined,
biography = undefined,
knownForDepartment = undefined
) {
this.id = id;
this.name = name;
this.poster = poster;
this.birthday = birthday;
this.deathday = deathday;
this.adult = adult;
this.placeOfBirth = placeOfBirth;
this.biography = biography;
this.knownForDepartment = knownForDepartment;
this.type = "person";
}
static convertFromTmdbResponse(response) {
const {
id,
name,
profile_path,
birthday,
deathday,
adult,
place_of_birth,
biography,
known_for_department
} = response;
const birthDay = new Date(birthday);
const deathDay = deathday ? new Date(deathday) : null;
return new Person(
id,
name,
profile_path,
birthDay,
deathDay,
adult,
place_of_birth,
biography,
known_for_department
);
}
createJsonResponse() {
return {
id: this.id,
name: this.name,
poster: this.poster,
birthday: this.birthday,
deathday: this.deathday,
place_of_birth: this.placeOfBirth,
biography: this.biography,
known_for_department: this.knownForDepartment,
adult: this.adult,
type: this.type
};
}
}
module.exports = Person;

View File

@@ -0,0 +1,78 @@
class ReleaseDates {
constructor(id, releases) {
this.id = id;
this.releases = releases;
}
static convertFromTmdbResponse(response) {
const { id, results } = response;
const releases = results.map(countryRelease =>
new Release(
countryRelease.iso_3166_1,
countryRelease.release_dates.map(rd => new ReleaseDate(rd.certification, rd.iso_639_1, rd.release_date, rd.type, rd.note))
))
return new ReleaseDates(id, releases)
}
createJsonResponse() {
return {
id: this.id,
results: this.releases.map(release => release.createJsonResponse())
}
}
}
class Release {
constructor(country, releaseDates) {
this.country = country;
this.releaseDates = releaseDates;
}
createJsonResponse() {
return {
country: this.country,
release_dates: this.releaseDates.map(releaseDate => releaseDate.createJsonResponse())
}
}
}
class ReleaseDate {
constructor(certification, language, releaseDate, type, note) {
this.certification = certification;
this.language = language;
this.releaseDate = releaseDate;
this.type = this.releaseTypeLookup(type);
this.note = note;
}
releaseTypeLookup(releaseTypeKey) {
const releaseTypeEnum = {
1: 'Premier',
2: 'Limited theatrical',
3: 'Theatrical',
4: 'Digital',
5: 'Physical',
6: 'TV'
}
if (releaseTypeKey <= Object.keys(releaseTypeEnum).length) {
return releaseTypeEnum[releaseTypeKey]
} else {
// TODO log | Release type not defined, does this need updating?
return null
}
}
createJsonResponse() {
return {
certification: this.certification,
language: this.language,
release_date: this.releaseDate,
type: this.type,
note: this.note
}
}
}
module.exports = ReleaseDates;

50
src/tmdb/types/show.js Normal file
View File

@@ -0,0 +1,50 @@
class Show {
constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined,
seasons=undefined, episodes=undefined, rank=undefined, genres=undefined, status=undefined,
runtime=undefined) {
this.id = id;
this.title = title;
this.year = year;
this.overview = overview;
this.poster = poster;
this.backdrop = backdrop;
this.seasons = seasons;
this.episodes = episodes;
this.rank = rank;
this.genres = genres;
this.productionStatus = status;
this.runtime = runtime;
this.type = 'show';
}
static convertFromTmdbResponse(response) {
const { id, name, first_air_date, overview, poster_path, backdrop_path, number_of_seasons, number_of_episodes,
rank, genres, status, episode_run_time, popularity } = response;
const year = new Date(first_air_date).getFullYear()
const genreNames = genres ? genres.map(g => g.name) : undefined
return new Show(id, name, year, overview, poster_path, backdrop_path, number_of_seasons, number_of_episodes,
rank, genreNames, status, episode_run_time, popularity)
}
createJsonResponse() {
return {
id: this.id,
title: this.title,
year: this.year,
overview: this.overview,
poster: this.poster,
backdrop: this.backdrop,
seasons: this.seasons,
episodes: this.episodes,
rank: this.rank,
genres: this.genres,
production_status: this.productionStatus,
runtime: this.runtime,
type: this.type
}
}
}
module.exports = Show;