| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- const settings = require('../data/settings.json');
- const MovieDB = require('moviedb')(settings.tmdbApiKey);
- const _ = require('lodash');
- const GENRE_TRANSLATIONS = settings.genre.translations;
- const GENRE_WEIGHTS = settings.genre.weights;
- const GENRE_COMBINATIONS = settings.genre.combinations;
- const searchQuery = name => {
- let parts = name.match(/(.+?)(\s\((\d{4})\))$/);
- let query = (parts && parts.length > 0) ? parts[1] : name;
- let year = (parts && parts.length > 2) ? parts[3] : false;
- return { query:query, year:year };
- };
- const searchFilter = (data, year = false) => {
- if (!year) return data;
- return data.filter(d => d.release_date.toString().includes(year.toString()));
- };
- const searchMovie = name => {
- const { query, year } = searchQuery(name);
- return new Promise((resolve, reject) => {
- MovieDB.searchMovie({ query: query }, (err, res) => {
- if (err) reject(err);
- resolve(searchFilter(res.results,year));
- });
- });
- };
- const searchTv = name => {
- const { query, year } = searchQuery(name);
- return new Promise((resolve, reject) => {
- MovieDB.searchMovie({ query: query }, (err, res) => {
- if (err) reject(err);
- resolve(searchFilter(res.results,year));
- });
- });
- };
- const infoMovie = id => {
- return new Promise((resolve, reject) => {
- MovieDB.movieInfo({ id: id }, (err, res) => {
- if (err) reject(err);
- resolve(res);
- });
- });
- };
- const infoTv = id => {
- return new Promise((resolve, reject) => {
- MovieDB.tvInfo({ id: id }, (err, res) => {
- if (err) reject(err);
- resolve(res);
- });
- });
- };
- const genreTranslate = (data = {}) => {
- let genres = Object.keys(data);
- let translations = Object.keys(GENRE_TRANSLATIONS);
- for (let i=0, l=translations.length; i<l; i++) {
- let to = translations[i];
- let froms = GENRE_TRANSLATIONS[to];
- for (let j=0, c=froms.length; j<c; j++) {
- let from = froms[j];
- if (data[from]) { // if we have the from ... translate
- if (!data[to]) data[to] = 0; // init the to value
- data[to] += data[from]; // translate value and add value
- delete data[from]; // delete the orig
- }
- }
- }
- let combinations = Object.keys(GENRE_COMBINATIONS);
- for (let i=0, l=combinations.length; i<l; i++) {
- let to = combinations[i]; // new to combination
- let froms = GENRE_COMBINATIONS[to]; // get the list of things to combine
- // check if we have all the genre intersections with the froms
- if (_.intersection(genres,froms).length === froms.length) {
- if (!data[to]) data[to] = 0; // init the to combination value
- for (let j=0, c=froms.length; j<c; j++) {
- let from = froms[j];
- if (data[from]) { // if we have the from ... translate
- data[to] += data[from];
- delete data[from]; // delete the orig
- }
- }
- }
- }
- };
- const genreTally = (data = {}, info = {}) => {
- let genres = info && info.genres ? info.genres.map(g => g.name) : [];
- for (let i = 0, l = genres.length; i < l; i++) {
- let genre = genres[i];
- if (!data) data = {};
- if (!data[genre]) data[genre] = 0;
- data[genre] = data[genre] + 1;
- }
- genreTranslate(data);
- };
- const genreSorted = data => {
- if (!data) return [];
- let keys = Object.keys(data);
- let values = Object.values(data);
- // get a distinctset of values
- let distinctValues = [...new Set(values)];
- // sort the keys based on current values
- let sorted = keys.sort((a, b) => data[b] - data[a]);
- // only one key ... return
- if (keys.length === 1) return sorted;
- // we have more than 1 distinct value
- if (distinctValues.length > 1) return sorted;
- // we have one entry and all genere are equally weighted ... apply boosting
- let genreWeightKeys = Object.keys(GENRE_WEIGHTS); // get the key list
- for (let i=0, l=genreWeightKeys.length; i<l; i++) { // loop the list
- let genreWeightKey = genreWeightKeys[i]; // pointer to the key name
- if (genreWeightKey && keys && keys.includes(genreWeightKey)) // we have a match
- data[genreWeightKey] += GENRE_WEIGHTS[genreWeightKey]; // boost
- }
- // re-sort boosted values
- return keys.sort((a, b) => data[b] - data[a]);
- };
- const genrePrimary = data => {
- if (!data) return 'Unknown';
- let sorted = genreSorted(data);
- return sorted[0];
- };
- const genreMovie = async (name, list = false) => {
- let results = await searchMovie(name);
- let genres = {};
- for (let i = 0, l = results.length; i < l; i++) {
- let result = results[i];
- let info = await infoMovie(result.id);
- genreTally(genres, info);
- }
- return (list) ? genreSorted(genres) : genrePrimary(genres);
- };
- const genreTv = async (name, list = false) => {
- let results = await searchTv(name);
- let genres = {};
- for (let i = 0, l = results.length; i < l; i++) {
- let result = results[i];
- let info = await infoTv(result.id);
- genreTally(genres, info);
- }
- return (list) ? genreSorted(genres) : genrePrimary(genres);
- };
- const search = async (name, type = 'movie') => {
- if (!name) return new Error('nothing to search');
- switch (type) {
- case 'tv':
- return await searchTv(name);
- case 'movie':
- return await searchMovie(name);
- default:
- return new Error('invalid type specified');
- }
- };
- const info = async (id, type = 'movie') => {
- if (!id) return new Error('no id specified');
- switch (type) {
- case 'tv':
- return await infoTv(id);
- case 'movie':
- return await infoMovie(id);
- default:
- return new Error('invalid type specified');
- }
- };
- const genre = async (name, type = 'movie') => {
- if (!name) return new Error('nothing to search');
- switch (type) {
- case 'tv':
- return await genreTv(name);
- case 'movie':
- return await genreMovie(name);
- default:
- return new Error('invalid type specified');
- }
- };
- const genres = async (name, type = 'movie') => {
- if (!name) return new Error('nothing to search');
- switch (type) {
- case 'tv':
- return await genreTv(name, true);
- case 'movie':
- return await genreMovie(name, true);
- default:
- return new Error('invalid type specified');
- }
- };
- module.exports = {
- search,
- info,
- genre,
- genres,
- };
|