diff --git a/seasoned_api/package.json b/seasoned_api/package.json index 01e852c..b64922f 100644 --- a/seasoned_api/package.json +++ b/seasoned_api/package.json @@ -26,7 +26,9 @@ "km-moviedb": "^0.2.12", "mongoose": "~5.0.11", "node-cache": "^4.1.1", + "node-fetch": "^2.6.0", "python-shell": "^0.5.0", + "raven": "^2.4.2", "request": "^2.85.0", "request-promise": "^4.2", "sqlite3": "^4.0.0" @@ -36,6 +38,7 @@ "@babel/node": "^7.5.5", "@babel/preset-env": "^7.5.5", "@babel/register": "^7.5.5", + "@types/node": "^12.6.8", "coveralls": "^3.0.5", "documentation": "^12.0.3", "eslint": "^4.9.0", @@ -45,8 +48,8 @@ "mocha": "^6.2.0", "mocha-lcov-reporter": "^1.3.0", "nyc": "^11.6.0", - "raven": "^2.4.2", "supertest": "^3.0.0", - "supertest-as-promised": "^4.0.1" + "supertest-as-promised": "^4.0.1", + "typescript": "^3.5.3" } } diff --git a/seasoned_api/src/pirate/pirateRepository.js b/seasoned_api/src/pirate/pirateRepository.js index f14075a..031f780 100644 --- a/seasoned_api/src/pirate/pirateRepository.js +++ b/seasoned_api/src/pirate/pirateRepository.js @@ -21,7 +21,7 @@ function getMagnetFromURL(url) { async function find(searchterm, callback) { const options = { - pythonPath: '../torrent_search/env/bin/python3.6', + pythonPath: '../torrent_search/env/bin/python3', scriptPath: '../torrent_search', args: [searchterm, '-s', 'jackett', '-f', '--print'] } @@ -35,7 +35,7 @@ async function callPythonAddMagnet(url, callback) { getMagnetFromURL(url) .then((magnet) => { const options = { - pythonPath: '../delugeClient/env/bin/python3.6', + pythonPath: '../delugeClient/env/bin/python3', scriptPath: '../delugeClient', args: ['add', magnet] }; @@ -51,13 +51,10 @@ async function callPythonAddMagnet(url, callback) { async function SearchPiratebay(query) { return await new Promise((resolve, reject) => find(query, (err, results) => { if (err) { - /* eslint-disable no-console */ console.log('THERE WAS A FUCKING ERROR!\n', err); reject(Error('There was a error when searching for torrents')); } if (results) { - /* eslint-disable no-console */ - console.log('result', results); resolve(JSON.parse(results, null, '\t')); } })); diff --git a/seasoned_api/src/plex/plex.js b/seasoned_api/src/plex/plex.js index 19fa502..30e4693 100644 --- a/seasoned_api/src/plex/plex.js +++ b/seasoned_api/src/plex/plex.js @@ -1,59 +1,89 @@ -const axios = require('axios') +const fetch = require('node-fetch') const convertPlexToMovie = require('src/plex/convertPlexToMovie') const convertPlexToShow = require('src/plex/convertPlexToShow') const convertPlexToEpisode = require('src/plex/convertPlexToEpisode') + +const { Movie, Show, Person } = require('src/tmdb/types'); + +// const { Movie, } +// TODO? import class definitions to compare types ? +// what would typescript do? + class Plex { - constructor(ip) { + constructor(ip, port=32400) { this.plexIP = ip - this.plexPort = 32400 + this.plexPort = port + } + + matchTmdbAndPlexMedia(plex, tmdb) { + if (plex === undefined || tmdb === undefined) + return false + + const sanitize = (string) => string.toLowerCase() + + const matchTitle = sanitize(plex.title) === sanitize(tmdb.title) + const matchYear = plex.year === tmdb.year + + return matchTitle && matchYear } existsInPlex(tmdbMovie) { - return Promise.resolve() - .then(() => this.search(tmdbMovie.title)) - // TODO handle this when whitelist of local ip is not set in plex - .catch((error) => { console.error('Unable to search plex')}) - .then((plexMovies) => { - const matches = plexMovies.some((plexMovie) => { - return tmdbMovie.title === plexMovie.title && tmdbMovie.type === plexMovie.type - }) + return this.search(tmdbMovie.title) + .then(plexMovies => plexMovies.some(plex => this.matchTmdbAndPlexMedia(plex, tmdbMovie))) + } - tmdbMovie.existsInPlex = matches - return tmdbMovie - }) + successfullResponse(response) { + const { status, statusText } = response + + if (status === 200) { + return response.json() + } else { + throw { message: statusText, status: status } + } } search(query) { + const url = `http://${this.plexIP}:${this.plexPort}/hubs/search?query=${query}` const options = { - baseURL: `http://${this.plexIP}:${this.plexPort}`, - url: '/hubs/search', - params: { query: query }, - responseType: 'json', - timeout: 3000 + timeout: 2000, + headers: { 'Accept': 'application/json' } } - return Promise.resolve() - .then(() => axios.request(options)) - .catch((error) => { throw new Error(`Unable to search plex library`, error) }) - .then(response => this.mapResults(response)) + return fetch(url, options) + .then(this.successfullResponse) + .then(this.mapResults) + .catch(error => { + if (error.type === 'request-timeout') { + throw { message: 'Plex did not respond', status: 408, success: false } + } + + throw error + }) } - mapResults(response) { - return response.data.MediaContainer.Hub.reduce((result, hub) => { - if (hub.type === 'movie' && hub.Metadata !== undefined) { - return [...result, ...hub.Metadata.map(convertPlexToMovie)] - } - else if (hub.type === 'show' && hub.Metadata !== undefined) { - return [...result, ...hub.Metadata.map(convertPlexToShow)] - } - else if (hub.type === 'episode' && hub.Metadata !== undefined) { - return [...result, ...hub.Metadata.map(convertPlexToEpisode)] - } + if (response === undefined || response.MediaContainer === undefined) { + console.log('response was not valid to map', response) + return [] + } - return result - }, []) + return response.MediaContainer.Hub + .filter(category => category.size > 0) + .map(category => { + if (category.type === 'movie') { + return category.Metadata.map(movie => { + const ovie = Movie.convertFromPlexResponse(movie) + return ovie.createJsonResponse() + }) + } else if (category.type === 'show') { + return category.Metadata.map(convertPlexToShow) + } else if (category.type === 'episode') { + return category.Metadata.map(convertPlexToEpisode) + } + }) + .filter(result => result !== undefined) + .flat() } } diff --git a/seasoned_api/src/plex/types/show.js b/seasoned_api/src/plex/types/show.js index 8290f1e..86c1ef4 100644 --- a/seasoned_api/src/plex/types/show.js +++ b/seasoned_api/src/plex/types/show.js @@ -6,7 +6,6 @@ class Show { this.rating = null; this.seasons = null; this.episodes = null; - this.type = 'show'; } } diff --git a/seasoned_api/src/request/request.js b/seasoned_api/src/request/request.js index b83882e..380c12a 100644 --- a/seasoned_api/src/request/request.js +++ b/seasoned_api/src/request/request.js @@ -23,6 +23,7 @@ class RequestRepository { downloaded: '(select status from requests where id is request.id and type is request.type limit 1)', // deluge: '(select status from deluge_torrent where id is request.id and type is request.type limit 1)', // fetchAllFilterStatus: 'select * from request where ' + readWithoutUserData: 'select id, title, year, type, status, date from requests where id is ? and type is ?', read: 'select id, title, year, type, status, requested_by, ip, date, user_agent from requests where id is ? and type is ?' }; } @@ -106,11 +107,17 @@ class RequestRepository { * @returns {Promise} */ getRequestByIdAndType(id, type) { - return Promise.resolve() - .then(() => this.database.get(this.queries.read, [id, type])) + return this.database.get(this.queries.readWithoutUserData, [id, type]) .then(row => { assert(row, 'Could not find request item with that id and type') - return JSON.stringify(row) + return { + id: row.id, + title: row.title, + year: row.year, + type: row.type, + status: row.status, + requested_date: new Date(row.date) + } }) } @@ -145,11 +152,15 @@ class RequestRepository { const totalRequests = sqliteResponse['totalRequests'] const totalPages = Math.ceil(totalRequests / 26) - return [ rows.map(item => { item.poster = item.poster_path; return item }), totalPages ] + return [ rows.map(item => { + item.poster = item.poster_path; delete item.poster_path; + item.backdrop = item.background_path; delete item.background_path; + return item + }), totalPages, totalRequests ] return Promise.all(this.mapToTmdbByType(rows)) }) - .then(([result, totalPages]) => Promise.resolve({ - results: result, total_results: result.length, page: page, total_pages: totalPages + .then(([result, totalPages, totalRequests]) => Promise.resolve({ + results: result, total_results: totalRequests, page: page, total_pages: totalPages })) .catch(error => { console.log(error);throw error }) } diff --git a/seasoned_api/src/tmdb/cache.js b/seasoned_api/src/tmdb/cache.js index f55179e..e85494a 100644 --- a/seasoned_api/src/tmdb/cache.js +++ b/seasoned_api/src/tmdb/cache.js @@ -18,12 +18,12 @@ class Cache { * @returns {Object} */ get(key) { - return Promise.resolve() - .then(() => this.database.get(this.queries.read, [key])) - .then((row) => { - assert(row, 'Could not find cache enrty with that key.'); - return JSON.parse(row.value); - }); + 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); + }) } /** diff --git a/seasoned_api/src/tmdb/convertTmdbToMovie.js b/seasoned_api/src/tmdb/convertTmdbToMovie.js deleted file mode 100644 index 3604f8c..0000000 --- a/seasoned_api/src/tmdb/convertTmdbToMovie.js +++ /dev/null @@ -1,71 +0,0 @@ -import { Movie } from './types' - -const tmdbSwitcher = (tmdbMovie, property) => tmdbMovie[property] - -const releaseTypeEnum = { - 1: 'Premier', - 2: 'Limited theatrical', - 3: 'Theatrical', - 4: 'Digital', - 5: 'Physical', - 6: 'TV' -} - -function convertTmdbToMovie(tmdbMovie, credits=undefined, releaseDates=undefined) { - const movie = new Movie(tmdbMovie.id, tmdbMovie.title) - movie.overview = tmdbMovie.overview; - movie.rank = tmdbMovie.vote_average; - - if (credits) { - movie.credits = { cast: credits.cast, crew: credits.crew }; - } - - if (releaseDates) { - movie.release_dates = releaseDates.results.map((releasePlace) => { - const newestRelease = releasePlace.release_dates.sort((a,b) => a.type < b.type ? 1 : -1)[0] - const type = releaseTypeEnum[newestRelease.type] - - return { - country: releasePlace.iso_3166_1, - type: type, - date: newestRelease.release_date - } - }) - } - - if (tmdbMovie.release_date !== undefined && tmdbMovie.release_date) { - movie.release_date = new Date(tmdbMovie.release_date); - movie.year = movie.release_date.getFullYear(); - } - - if (tmdbMovie.poster_path !== undefined && tmdbMovie.poster_path) { - movie.poster = tmdbMovie.poster_path; - } - if (tmdbMovie.backdrop_path !== undefined && tmdbMovie.backdrop_path) { - movie.backdrop = tmdbMovie.backdrop_path; - } - - if (tmdbMovie.status !== undefined && tmdbMovie.status) { - movie.status = tmdbMovie.status; - } - - if (tmdbMovie.genres !== undefined && tmdbMovie.genres) { - movie.genres = tmdbMovie.genres.map(genre => genre.name); - } - - if (tmdbMovie.tagline !== undefined && tmdbMovie.tagline) { - movie.tagline = tmdbMovie.tagline; - } - - if (tmdbMovie.runtime !== undefined && tmdbMovie.runtime) { - movie.runtime = tmdbMovie.runtime; - } - - if (tmdbMovie.imdb_id !== undefined && tmdbMovie.imdb_id) { - movie.imdb_id = tmdbMovie.imdb_id; - } - - return movie; -} - -module.exports = convertTmdbToMovie; diff --git a/seasoned_api/src/tmdb/convertTmdbToPerson.js b/seasoned_api/src/tmdb/convertTmdbToPerson.js deleted file mode 100644 index 3626ff4..0000000 --- a/seasoned_api/src/tmdb/convertTmdbToPerson.js +++ /dev/null @@ -1,30 +0,0 @@ -import { Person } from './types' -const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie'); - -function convertTmdbToPerson(tmdbPerson, cast=undefined) { - const person = new Person(tmdbPerson.id, tmdbPerson.name); - - if (tmdbPerson.profile_path !== undefined) { - person.poster = tmdbPerson.profile_path; - } - - if (tmdbPerson.birthday !== undefined) { - person.birthday = new Date(tmdbPerson.birthday); - } - - if (tmdbPerson.deathday !== undefined) { - person.deathday = tmdbPerson.deathday; - } - - if (tmdbPerson.known_for !== undefined) { - person.known_for = tmdbPerson.known_for.map(convertTmdbToMovie); - } - - if (cast) { - person.cast = cast.cast; - } - - return person; -} - -module.exports = convertTmdbToPerson; diff --git a/seasoned_api/src/tmdb/convertTmdbToSeasoned.js b/seasoned_api/src/tmdb/convertTmdbToSeasoned.js deleted file mode 100644 index 0ce7886..0000000 --- a/seasoned_api/src/tmdb/convertTmdbToSeasoned.js +++ /dev/null @@ -1,43 +0,0 @@ -const TMDB = require('src/media_classes/tmdb'); - -function translateYear(tmdbReleaseDate) { - return new Date(tmdbReleaseDate).getFullYear(); -} - -function translateGenre(tmdbGenres) { - return tmdbGenres.map(genre => genre.name); -} - -function convertType(tmdbType) { - if (tmdbType === 'tv') return 'show'; - return undefined; -} - -function convertTmdbToMovie(tmdb) { - const title = tmdb.title || tmdb.name; - const year = translateYear(tmdb.release_date || tmdb.first_air_date); - const type = manualType || convertType(tmdb.type) || 'movie'; - - const id = tmdb.id; - const summary = tmdb.overview; - const poster_path = tmdb.poster_path; - const background_path = tmdb.backdrop_path; - const popularity = tmdb.popularity; - const score = tmdb.vote_average; - // const genres = translateGenre(tmdb.genres); - const release_status = tmdb.status; - const tagline = tmdb.tagline; - - const seasons = tmdb.number_of_seasons; - const episodes = tmdb.episodes; - - const seasoned = new TMDB( - title, year, type, id, summary, poster_path, background_path, - popularity, score, release_status, tagline, seasons, episodes - ); - - // seasoned.print() - return seasoned; -} - -module.exports = convertTmdbToSeasoned; diff --git a/seasoned_api/src/tmdb/convertTmdbToShow.js b/seasoned_api/src/tmdb/convertTmdbToShow.js deleted file mode 100644 index 1a25fe5..0000000 --- a/seasoned_api/src/tmdb/convertTmdbToShow.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Show } from './types' - -function convertTmdbToShow(tmdbShow, credits=undefined) { - const show = new Show(tmdbShow.id, tmdbShow.name) - show.seasons = tmdbShow.number_of_seasons; - show.episodes = tmdbShow.number_of_episodes; - show.overview = tmdbShow.overview; - show.rank = tmdbShow.vote_average; - - if (credits) { - show.credits = credits - } - - if (tmdbShow.genres !== undefined) { - show.genres = tmdbShow.genres.map(genre => genre.name); - } - - if (tmdbShow.first_air_date !== undefined) { - show.first_air_date = new Date(tmdbShow.first_air_date); - show.year = show.first_air_date.getFullYear(); - } - - if (tmdbShow.poster_path !== undefined) { - show.poster = tmdbShow.poster_path; - } - if (tmdbShow.backdrop_path !== undefined) { - show.backdrop = tmdbShow.backdrop_path; - } - - if (tmdbShow.status !== undefined) { - show.status = tmdbShow.status; - } - - if (tmdbShow.episode_run_time !== undefined) { - show.runtime = tmdbShow.runtime; - } - - return show; -} - -module.exports = convertTmdbToShow; diff --git a/seasoned_api/src/tmdb/tmdb.js b/seasoned_api/src/tmdb/tmdb.js index a6eb35d..3cd68ec 100644 --- a/seasoned_api/src/tmdb/tmdb.js +++ b/seasoned_api/src/tmdb/tmdb.js @@ -1,9 +1,6 @@ const moviedb = require('km-moviedb'); -const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie'); -const convertTmdbToShow = require('src/tmdb/convertTmdbToShow'); -const convertTmdbToPerson = require('src/tmdb/convertTmdbToPerson'); -const { Credits, ReleaseDates } = require('src/tmdb/types'); +const { Movie, Show, Person, Credits, ReleaseDates } = require('src/tmdb/types'); // const { tmdbInfo } = require('src/tmdb/types') class TMDB { @@ -18,7 +15,8 @@ class TMDB { movieInfo: 'mi', movieCredits: 'mc', movieReleaseDates: 'mrd', - showInfo: 'si', + showInfo: 'si', + showCredits: 'sc', personInfo: 'pi', miscNowPlayingMovies: 'npm', miscPopularMovies: 'pm', @@ -30,123 +28,22 @@ class TMDB { }; } - /** - * Retrieve a specific movie by id from TMDB. - * @param {Number} identifier of the movie you want to retrieve - * @param {String} type filter results by type (default movie). - * @returns {Promise} succeeds if movie was found - */ - lookup(identifier, type = 'movie') { - const query = { id: identifier }; - const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb(TMDB_METHODS['info'][type], query)) - .catch(() => { throw new Error('Could not find a movie with that id.'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => { - try { - return convertTmdbToSeasoned(response, type); - } catch (parseError) { - console.error(parseError); - throw new Error('Could not parse movie.'); - } - }); - } - - /** - * Retrive search results from TMDB. - * @param {String} text query you want to search for - * @param {Number} page representing pagination of results - * @param {String} type filter results by type (default multi) - * @returns {Promise} dict with query results, current page and total_pages - */ - search(text, page = 1, type = 'multi') { - const query = { query: text, page: page }; - const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb(TMDB_METHODS['search'][type], query)) - .catch(() => { throw new Error('Could not search for movies/shows at tmdb.'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapResults(response)); - } - /** * Retrieve a specific movie by id from TMDB. * @param {Number} identifier of the movie you want to retrieve - * @param {String} type filter results by type (default movie). + * @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, credits=false, releaseDates=false) { + movieInfo(identifier) { const query = { id: identifier }; - const cacheKey = `${this.cacheTags.movieInfo}:${identifier}:${credits}`; + const cacheKey = `${this.cacheTags.movieInfo}:${identifier}`; - const requests = [this.tmdb('movieInfo', query)] - - if (credits) { - requests.push(this.tmdb('movieCredits', query)) - } else { - // This is because we expect ordered parameters below - requests.push(Promise.resolve([])) - } - - if (releaseDates) { - requests.push(this.tmdb('movieReleaseDates', query)) - } - - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => Promise.all(requests)) - .catch(error => { - if (error.status === 401) { - throw new Error('Unathorized tmdb request, please check api key.') - } else if (error.status === 404) { - throw new Error(`Could not find a movie with id: ${identifier}`) - } - - throw new Error('Unexpected error has occured:', error.message) - }) - .then(([movies, credits, releaseDates]) => this.cache.set(cacheKey, [movies, credits, releaseDates])) - .then(([movies, credits, releaseDates]) => convertTmdbToMovie(movies, credits, releaseDates)) - } - - tmdbCreditsError(error) { - if (error.status === 404) { - throw { - status: 404, - message: error.response.body.status_message - } - } else if (error.status === 401) { - throw { - status: 401, - message: error.response.body.status_message - } - } - - throw { - status: 500, - message: 'An unexpected error occured while fetching credits from tmdb' - } - } - - tmdbRelaseDatesError(error) { - if (error.status === 404) { - throw { - status: 404, - message: error.response.body.status_message - } - } else if (error.status === 401) { - throw { - status: 401, - message: error.response.body.status_message - } - } - - throw { - status: 500, - message: 'An unexpected error occured while fetching release dates from tmdb' - } + return this.cache.get(cacheKey) + .catch(() => this.tmdb('movieInfo', query)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie info')) + .then(movie => this.cache.set(cacheKey, movie, 1)) + .then(movie => Movie.convertFromTmdbResponse(movie)) } /** @@ -160,8 +57,8 @@ class TMDB { return this.cache.get(cacheKey) .catch(() => this.tmdb('movieCredits', query)) - .catch(tmdbError => this.tmdbCreditsError(tmdbError)) - .then(credits => this.cache.set(cacheKey, credits, 86400)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie credits')) + .then(credits => this.cache.set(cacheKey, credits, 1)) .then(credits => Credits.convertFromTmdbResponse(credits)) } @@ -176,8 +73,8 @@ class TMDB { return this.cache.get(cacheKey) .catch(() => this.tmdb('movieReleaseDates', query)) - .catch(tmdbError => this.tmdbRelaseDatesError(tmdbError)) - .then(releaseDates => this.cache.set(cacheKey, releaseDates, 86400)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie release dates')) + .then(releaseDates => this.cache.set(cacheKey, releaseDates, 1)) .then(releaseDates => ReleaseDates.convertFromTmdbResponse(releaseDates)) } @@ -187,30 +84,26 @@ class TMDB { * @param {String} type filter results by type (default show). * @returns {Promise} succeeds if show was found */ - showInfo(identifier, credits=false) { + showInfo(identifier) { const query = { id: identifier }; - const cacheKey = `${this.cacheTags.showInfo}:${identifier}:${credits}`; + const cacheKey = `${this.cacheTags.showInfo}:${identifier}`; - const requests = [this.tmdb('tvInfo', query)] + return this.cache.get(cacheKey) + .catch(() => this.tmdb('tvInfo', query)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'tv info')) + .then(show => this.cache.set(cacheKey, show, 1)) + .then(show => Show.convertFromTmdbResponse(show)) + } - if (credits) { - requests.push(this.tmdb('tvCredits', query)) - } + showCredits(identifier) { + const query = { id: identifier } + const cacheKey = `${this.cacheTags.showCredits}:${identifier}` - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => Promise.all(requests)) - .catch(error => { - if (error.status === 401) { - throw new Error('Unathorized tmdb request, please check api key.') - } else if (error.status === 404) { - throw new Error(`Could not find a show with id: ${identifier}`) - } - - throw new Error('Unexpected error has occured:', error.message) - }) - .then(([shows, credits]) => this.cache.set(cacheKey, [shows, credits])) - .then(([shows, credits]) => convertTmdbToShow(shows, credits)) + return this.cache.get(cacheKey) + .catch(() => this.tmdb('tvCredits', query)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'show credits')) + .then(credits => this.cache.set(cacheKey, credits, 1)) + .then(credits => Credits.convertFromTmdbResponse(credits)) } /** @@ -223,25 +116,20 @@ class TMDB { const query = { id: identifier }; const cacheKey = `${this.cacheTags.personInfo}:${identifier}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => Promise.all([this.tmdb('personInfo', query), this.tmdb('personCombinedCredits', query)])) - .catch(() => { throw new Error('Could not find a person with that id.'); }) - .then(([person, cast]) => this.cache.set(cacheKey, [person, cast])) - .then(([person, cast]) => convertTmdbToPerson(person, cast)) - .catch(err => new Error('Unable to convert result to person', err)) + return this.cache.get(cacheKey) + .catch(() => this.tmdb('personInfo', query)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'person info')) + .then(person => this.cache.set(cacheKey, person, 1)) + .then(person => Person.convertFromTmdbResponse(person)) } - - multiSearch(search_query, page=1) { const query = { query: search_query, page: page }; const cacheKey = `${this.cacheTags.multiSearch}:${page}:${search_query}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) + return this.cache.get(cacheKey) .catch(() => this.tmdb('searchMulti', query)) - .catch(() => { throw new Error('Could not complete search to tmdb'); }) - .then(response => this.cache.set(cacheKey, response)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'search results')) + .then(response => this.cache.set(cacheKey, response, 1)) .then(response => this.mapResults(response)); } @@ -255,13 +143,11 @@ class TMDB { const tmdbquery = { query: query, page: page }; const cacheKey = `${this.cacheTags.movieSearch}:${page}:${query}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb('searchMovie', tmdbquery)) - .catch(() => { throw new Error('Could not complete movie search to tmdb'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapAndCreateResponse(response, convertTmdbToMovie)) - .catch((error) => { console.log(error); throw new Error('Could not parse movie search result') }) + return this.cache.get(cacheKey) + .catch(() => this.tmdb('searchMovie', tmdbquery)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie search results')) + .then(response => this.cache.set(cacheKey, response, 1)) + .then(response => this.mapResults(response, 'movie')) } /** @@ -273,13 +159,12 @@ class TMDB { showSearch(query, page=1) { const tmdbquery = { query: query, page: page }; const cacheKey = `${this.cacheTags.showSearch}:${page}:${query}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb('searchTv', tmdbquery)) - .catch(() => { throw new Error('Could not complete show search to tmdb'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapAndCreateResponse(response, convertTmdbToShow)) - .catch((error) => { console.log(error); throw new Error('Could not parse show search result') }) + + return this.cache.get(cacheKey) + .catch(() => this.tmdb('searchTv', tmdbquery)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'tv search results')) + .then(response => this.cache.set(cacheKey, response, 1)) + .then(response => this.mapResults(response, 'show')) } /** @@ -289,78 +174,36 @@ class TMDB { * @returns {Promise} dict with query results, current page and total_pages */ personSearch(query, page=1) { - const tmdbquery = { query: query, page: page }; + + const tmdbquery = { query: query, page: page, include_adult: true }; const cacheKey = `${this.cacheTags.personSearch}:${page}:${query}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb('searchPerson', tmdbquery)) - .catch(() => { throw new Error('Could not complete person search to tmdb'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapAndCreateResponse(response, convertTmdbToPerson)) - .catch((error) => { console.log(error); throw new Error('Could not parse person search result') }) - } - mapAndCreateResponse(response, resultConvertFunction) { - // console.log(response) - return { - results: response.results.map(resultConvertFunction), - page: response.page, - total_results: response.total_results, - total_pages: response.total_pages - } + return this.cache.get(cacheKey) + .catch(() => this.tmdb('searchPerson', tmdbquery)) + .catch(tmdbError => tmdbErrorResponse(tmdbError, 'person search results')) + .then(response => this.cache.set(cacheKey, response, 1)) + .then(response => this.mapResults(response, 'person')) } - movieList(listname, page = 1) { const query = { page: page }; const cacheKey = `${this.cacheTags[listname]}:${page}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) + return this.cache.get(cacheKey) .catch(() => this.tmdb(listname, query)) - .catch(() => { throw new Error('Unable to get movie list from tmdb')}) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapAndCreateResponse(response, convertTmdbToMovie)); + .catch(tmdbError => this.tmdbErrorResponse(tmdbError, 'movie list ' + listname)) + .then(response => this.cache.set(cacheKey, response, 1)) + .then(response => this.mapResults(response, 'movie')) } showList(listname, page = 1) { const query = { page: page }; const cacheKey = `${this.cacheTags[listname]}:${page}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) + + return this.cache.get(cacheKey) .catch(() => this.tmdb(listname, query)) - .catch(() => { throw new Error('Unable to get show list from tmdb')}) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapAndCreateResponse(response, convertTmdbToShow)); - } - - /** - * Fetches a given list from tmdb. - * @param {String} listName Name of list - * @param {String} type filter results by type (default movie) - * @param {Number} page representing pagination of results - * @returns {Promise} dict with query results, current page and total_pages - */ - listSearch(listName, type = 'movie', page = '1') { - const query = { page: page }; - console.log(query); - const cacheKey = `${this.cacheTags[listName]}:${type}:${page}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb(TMDB_METHODS[listName][type], query)) - .catch(() => { throw new Error('Error fetching list from tmdb.'); }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapResults(response, type)); - } - - popular(type='movie', page=1) { - const query = { type: type, page: page }; - const cacheKey = `${this.cacheTags.popular}:${type}:${page}`; - return Promise.resolve() - .then(() => this.cache.get(cacheKey)) - .catch(() => this.tmdb('miscPopularMovies', query)) - .catch((e) => { throw new Error(`Error fetching popular list of type ${type} : ${e}`) }) - .then(response => this.cache.set(cacheKey, response)) - .then(response => this.mapResults(response, type)); + .catch(tmdbError => this.tmdbErrorResponse(tmdbError, 'show list ' + listname)) + .then(response => this.cache.set(cacheKey, response, 1)) + .then(response => this.mapResults(response, 'show')) } /** @@ -369,14 +212,20 @@ class TMDB { * @param {String} The type declared in listSearch. * @returns {Promise} dict with tmdb results, mapped as movie/show objects. */ - mapResults(response, _) { - let results = response.results.map((result) => { - if (result.media_type === 'movie') { - return convertTmdbToMovie(result); - } else if (result.media_type === 'tv') { - return convertTmdbToShow(result); - } else if (result.media_type === 'person') { - return convertTmdbToPerson(result); + mapResults(response, type=undefined) { + // console.log(response.results) + // response.results.map(te => console.table(te)) + + 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() } }) @@ -410,6 +259,28 @@ class TMDB { } }); } + +} + +function 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` + } } module.exports = TMDB; diff --git a/seasoned_api/src/tmdb/tmdb.ts b/seasoned_api/src/tmdb/tmdb.ts new file mode 100644 index 0000000..85da44f --- /dev/null +++ b/seasoned_api/src/tmdb/tmdb.ts @@ -0,0 +1,7 @@ +import { Movie } from './types' + +Movie('str', 123) + + + +module.exports = TMDB; diff --git a/seasoned_api/src/tmdb/types.ts b/seasoned_api/src/tmdb/types.ts new file mode 100644 index 0000000..89e2f8a --- /dev/null +++ b/seasoned_api/src/tmdb/types.ts @@ -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 } diff --git a/seasoned_api/src/tmdb/types/movie.js b/seasoned_api/src/tmdb/types/movie.js index d3e4a23..64556a8 100644 --- a/seasoned_api/src/tmdb/types/movie.js +++ b/seasoned_api/src/tmdb/types/movie.js @@ -1,7 +1,7 @@ class Movie { constructor(id, title, year=undefined, overview=undefined, poster=undefined, - backdrop=undefined, rank=undefined, genres=undefined, status=undefined, - tagline=undefined, runtime=undefined, imdb_id=undefined) { + backdrop=undefined, rank=undefined, genres=undefined, productionStatus=undefined, + tagline=undefined, runtime=undefined, imdb_id=undefined, popularity) { this.id = id; this.title = title; this.year = year; @@ -10,13 +10,33 @@ class Movie { this.backdrop = backdrop; this.rank = rank; this.genres = genres; - this.status = status; + 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, rank, genres, status, + tagline, runtime, imdb_id, popularity } = response; + + const year = new Date(release_date).getFullYear() + const genreNames = genres ? genres.map(g => g.name) : undefined + + return new Movie(id, title, year, overview, poster_path, backdrop_path, rank, 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, @@ -27,7 +47,7 @@ class Movie { backdrop: this.backdrop, rank: this.rank, genres: this.genres, - status: this.status, + production_status: this.productionStatus, tagline: this.tagline, runtime: this.runtime, imdb_id: this.imdb_id, diff --git a/seasoned_api/src/tmdb/types/person.js b/seasoned_api/src/tmdb/types/person.js index d051a2a..45df6c9 100644 --- a/seasoned_api/src/tmdb/types/person.js +++ b/seasoned_api/src/tmdb/types/person.js @@ -1,14 +1,25 @@ class Person { - constructor(id, name, poster=null, birthday=null, deathday=null, known_for=null) { + constructor(id, name, poster=undefined, birthday=undefined, deathday=undefined, + adult=undefined, knownForDepartment=undefined) { this.id = id; this.name = name; this.poster = poster; this.birthday = birthday; this.deathday = deathday; - this.known_for = known_for; + this.adult = adult; + this.knownForDepartment = knownForDepartment; this.type = 'person'; } + static convertFromTmdbResponse(response) { + const { id, name, poster, birthday, deathday, adult, known_for_department } = response; + + const birthDay = new Date(birthday) + const deathDay = deathday ? new Date(deathday) : null + + return new Person(id, name, poster, birthDay, deathDay, adult, known_for_department) + } + createJsonResponse() { return { id: this.id, @@ -16,7 +27,8 @@ class Person { poster: this.poster, birthday: this.birthday, deathday: this.deathday, - known_for: this.known_for, + known_for_department: this.knownForDepartment, + adult: this.adult, type: this.type } } diff --git a/seasoned_api/src/tmdb/types/releaseDates.js b/seasoned_api/src/tmdb/types/releaseDates.js index 8ce749a..340479e 100644 --- a/seasoned_api/src/tmdb/types/releaseDates.js +++ b/seasoned_api/src/tmdb/types/releaseDates.js @@ -68,7 +68,7 @@ class ReleaseDate { return { certification: this.certification, language: this.language, - releaseDate: this.releaseDate, + release_date: this.releaseDate, type: this.type, note: this.note } diff --git a/seasoned_api/src/tmdb/types/show.js b/seasoned_api/src/tmdb/types/show.js index d6b69d5..176c59a 100644 --- a/seasoned_api/src/tmdb/types/show.js +++ b/seasoned_api/src/tmdb/types/show.js @@ -1,7 +1,7 @@ class Show { - constructor(id, title, year=null, overview=null, poster=null, backdrop=null, - seasons=null, episodes=null, rank=null, genres=null, status=null, - runtime=null) { + 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; @@ -12,11 +12,22 @@ class Show { this.episodes = episodes; this.rank = rank; this.genres = genres; - this.status = status; + 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, @@ -29,9 +40,8 @@ class Show { episodes: this.episodes, rank: this.rank, genres: this.genres, - status: this.status, + production_status: this.productionStatus, runtime: this.runtime, - // imdb_id: this.imdb_id, type: this.type } } diff --git a/seasoned_api/src/webserver/app.js b/seasoned_api/src/webserver/app.js index db48442..b1b625e 100644 --- a/seasoned_api/src/webserver/app.js +++ b/seasoned_api/src/webserver/app.js @@ -28,8 +28,6 @@ router.use(tokenToUser); // TODO: Should have a separate middleware/router for handling headers. router.use((req, res, next) => { // TODO add logging of all incoming - console.log('Request: ', req.originalUrl); - const origin = req.headers.origin; if (allowedOrigins.indexOf(origin) > -1) { res.setHeader('Access-Control-Allow-Origin', origin); @@ -81,10 +79,11 @@ router.get('/v2/show/top_rated', listController.topRatedShows); router.get('/v2/movie/:id/credits', require('./controllers/movie/credits.js')); router.get('/v2/movie/:id/release_dates', require('./controllers/movie/releaseDates.js')); +router.get('/v2/show/:id/credits', require('./controllers/show/credits.js')); -router.get('/v2/movie/:id', require('./controllers/info/movieInfo.js')); -router.get('/v2/show/:id', require('./controllers/info/showInfo.js')); -router.get('/v2/person/:id', require('./controllers/info/personInfo.js')); +router.get('/v2/movie/:id', require('./controllers/movie/info.js')); +router.get('/v2/show/:id', require('./controllers/show/info.js')); +router.get('/v2/person/:id', require('./controllers/person/info.js')); /** * Plex @@ -117,13 +116,6 @@ router.put('/v1/plex/request/:requestId', mustBeAuthenticated, require('./contro router.get('/v1/pirate/search', mustBeAuthenticated, require('./controllers/pirate/searchTheBay.js')); router.post('/v1/pirate/add', mustBeAuthenticated, require('./controllers/pirate/addMagnet.js')); -/** - * TMDB - */ -router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.js')); -router.get('/v1/tmdb/list/:listname', require('./controllers/tmdb/listSearch.js')); -router.get('/v1/tmdb/:mediaId', require('./controllers/tmdb/readMedia.js')); - /** * git */ diff --git a/seasoned_api/src/webserver/controllers/info/movieInfo.js b/seasoned_api/src/webserver/controllers/info/movieInfo.js deleted file mode 100644 index 2c18b5e..0000000 --- a/seasoned_api/src/webserver/controllers/info/movieInfo.js +++ /dev/null @@ -1,38 +0,0 @@ -const configuration = require('src/config/configuration').getInstance(); -const Cache = require('src/tmdb/cache'); -const TMDB = require('src/tmdb/tmdb'); -const Plex = require('src/plex/plex'); -const cache = new Cache(); -const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); -const plex = new Plex(configuration.get('plex', 'ip')); - -/** - * Controller: Retrieve information for a movie - * @param {Request} req http request variable - * @param {Response} res - * @returns {Callback} - */ -async function movieInfoController(req, res) { - const movieId = req.params.id; - const queryCredits = req.query.credits; - const queryReleaseDates = req.query.release_dates; - let credits = undefined - let releaseDates = undefined - - if (queryCredits && queryCredits.toLowerCase() === 'true') - credits = true - if (queryReleaseDates && queryReleaseDates.toLowerCase() === 'true') - releaseDates = true - - const movie = await tmdb.movieInfo(movieId, credits, releaseDates); - - plex.existsInPlex(movie) - .catch((error) => { console.log('Error when searching plex'); }) - .then(() => { - res.send(movie); - }).catch((error) => { - res.status(404).send({ success: false, error: error.message }); - }); -} - -module.exports = movieInfoController; diff --git a/seasoned_api/src/webserver/controllers/info/showInfo.js b/seasoned_api/src/webserver/controllers/info/showInfo.js deleted file mode 100644 index 4cbc5a9..0000000 --- a/seasoned_api/src/webserver/controllers/info/showInfo.js +++ /dev/null @@ -1,31 +0,0 @@ -const configuration = require('src/config/configuration').getInstance(); -const Cache = require('src/tmdb/cache'); -const TMDB = require('src/tmdb/tmdb'); -const Plex = require('src/plex/plex'); -const cache = new Cache(); -const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); -const plex = new Plex(configuration.get('plex', 'ip')); - -/** - * Controller: Retrieve information for a show - * @param {Request} req http request variable - * @param {Response} res - * @returns {Callback} - */ - -async function showInfoController(req, res) { - const showId = req.params.id; - const { credits } = req.query; - const show = await tmdb.showInfo(showId, credits); - - plex.existsInPlex(show) - .catch((error) => { console.log('Error when searching plex'); }) - .then(() => { - console.log('show', show) - res.send(show); - }).catch((error) => { - res.status(404).send({ success: false, error: error.message }); - }); -} - -module.exports = showInfoController; diff --git a/seasoned_api/src/webserver/controllers/list/listController.js b/seasoned_api/src/webserver/controllers/list/listController.js index 037bc89..c6357dd 100644 --- a/seasoned_api/src/webserver/controllers/list/listController.js +++ b/seasoned_api/src/webserver/controllers/list/listController.js @@ -16,40 +16,48 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); // + newly created (tv/latest). // + movie/latest // +function handleError(error, res) { + const { status, message } = error; - -const respondWithErrorMessage = (res, error) => { - const status = error.status || 500 - const message = error.message || 'Unhandled error occured' - const success = error.success || false - - // console.log('Unknown error:', error) - return res.status(status).send({ success: success, error: message}) + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + console.log('caught list controller error', error) + res.status(500).send({ message: 'An unexpected error occured while requesting list'}) + } } -function fetchTmdbMovieList(req, res, listName, tmdbListFunction) { +function handleListResponse(response, res) { + return res.send(response) + .catch(error => handleError(error, res)) +} + +function fetchTmdbList(req, res, listName, type) { const { page } = req.query; - return tmdb.movieList(listName, page) - .then(nowPlayingMovieList => res.send(nowPlayingMovieList)) - .catch(error => respondWithErrorMessage(res, error)) + if (type === 'movie') { + return tmdb.movieList(listName, page) + .then(listResponse => res.send(listResponse)) + .catch(error => handleError(error, res)) + } else if (type === 'show') { + return tmdb.showList(listname, page) + .then(listResponse => res.send(listResponse)) + .catch(error => handleError(error, res)) + } + + handleError({ + status: 400, + message: `'${type}' is not a valid list type.` + }, res) } -function fetchTmdbShowList(req, res, listName, tmdbListFunction) { - const { page } = req.query; - - return tmdb.showList(listName, page) - .then(nowPlayingMovieList => res.send(nowPlayingMovieList)) - .catch(error => respondWithErrorMessage(res, error)) -} - -const nowPlayingMovies = (req, res) => fetchTmdbMovieList(req, res, 'miscNowPlayingMovies') -const popularMovies = (req, res) => fetchTmdbMovieList(req, res, 'miscPopularMovies') -const topRatedMovies = (req, res) => fetchTmdbMovieList(req, res, 'miscTopRatedMovies') -const upcomingMovies = (req, res) => fetchTmdbMovieList(req, res, 'miscUpcomingMovies') -const nowPlayingShows = (req, res) => fetchTmdbShowList(req, res, 'tvOnTheAir') -const popularShows = (req, res) => fetchTmdbShowList(req, res, 'miscPopularTvs') -const topRatedShows = (req, res) => fetchTmdbShowList(req, res, 'miscTopRatedTvs') +const nowPlayingMovies = (req, res) => fetchTmdbList(req, res, 'miscNowPlayingMovies', 'movie') +const popularMovies = (req, res) => fetchTmdbList(req, res, 'miscPopularMovies', 'movie') +const topRatedMovies = (req, res) => fetchTmdbList(req, res, 'miscTopRatedMovies', 'movie') +const upcomingMovies = (req, res) => fetchTmdbList(req, res, 'miscUpcomingMovies', 'movie') +const nowPlayingShows = (req, res) => fetchTmdbList(req, res, 'tvOnTheAir', 'show') +const popularShows = (req, res) => fetchTmdbList(req, res, 'miscPopularTvs', 'show') +const topRatedShows = (req, res) => fetchTmdbList(req, res, 'miscTopRatedTvs', 'show') module.exports = { nowPlayingMovies, diff --git a/seasoned_api/src/webserver/controllers/movie/credits.js b/seasoned_api/src/webserver/controllers/movie/credits.js index 5ca5b58..a3eceb8 100644 --- a/seasoned_api/src/webserver/controllers/movie/credits.js +++ b/seasoned_api/src/webserver/controllers/movie/credits.js @@ -17,7 +17,7 @@ const movieCreditsController = (req, res) => { res.status(error.status).send({ success: false, error: error.message }) } else { // TODO log unhandled errors - console.log('caugth credits controller error', error) + console.log('caugth movie credits controller error', error) res.status(500).send({ message: 'An unexpected error occured while requesting movie credits' }) } }) diff --git a/seasoned_api/src/webserver/controllers/movie/info.js b/seasoned_api/src/webserver/controllers/movie/info.js new file mode 100644 index 0000000..14d4d82 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/movie/info.js @@ -0,0 +1,58 @@ +const configuration = require('src/config/configuration').getInstance(); +const Cache = require('src/tmdb/cache'); +const TMDB = require('src/tmdb/tmdb'); +const Plex = require('src/plex/plex'); +const cache = new Cache(); +const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); +const plex = new Plex(configuration.get('plex', 'ip')); + +function handleError(error, res) { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + console.log('caught movieinfo controller error', error) + res.status(500).send({ message: 'An unexpected error occured while requesting movie info'}) + } +} + +/** + * Controller: Retrieve information for a movie + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +async function movieInfoController(req, res) { + const movieId = req.params.id; + let { credits, release_dates, check_existance } = req.query; + + credits && credits.toLowerCase() === 'true' ? credits = true : credits = false + release_dates && release_dates.toLowerCase() === 'true' ? release_dates = true : release_dates = false + check_existance && check_existance.toLowerCase() === 'true' ? check_existance = true : check_existance = false + + let tmdbQueue = [tmdb.movieInfo(movieId)] + if (credits) + tmdbQueue.push(tmdb.movieCredits(movieId)) + if (release_dates) + tmdbQueue.push(tmdb.movieReleaseDates(movieId)) + + try { + const [ Movie, Credits, ReleaseDates ] = await Promise.all(tmdbQueue) + + const movie = Movie.createJsonResponse() + if (Credits) + movie.credits = Credits.createJsonResponse() + if (ReleaseDates) + movie.release_dates = ReleaseDates.createJsonResponse().results + + if (check_existance) + movie.exists_in_plex = await plex.existsInPlex(movie) + + res.send(movie) + } catch(error) { + handleError(error, res) + } +} + +module.exports = movieInfoController; diff --git a/seasoned_api/src/webserver/controllers/info/personInfo.js b/seasoned_api/src/webserver/controllers/person/info.js similarity index 88% rename from seasoned_api/src/webserver/controllers/info/personInfo.js rename to seasoned_api/src/webserver/controllers/person/info.js index 1d19c9d..a0eeab6 100644 --- a/seasoned_api/src/webserver/controllers/info/personInfo.js +++ b/seasoned_api/src/webserver/controllers/person/info.js @@ -13,10 +13,11 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); function personInfoController(req, res) { const personId = req.params.id; + + tmdb.personInfo(personId) - .then((person) => { - res.send(person); - }).catch((error) => { + .then(person => res.send(person.createJsonResponse())) + .catch(error => { res.status(404).send({ success: false, error: error.message }); }); } diff --git a/seasoned_api/src/webserver/controllers/request/getRequest.js b/seasoned_api/src/webserver/controllers/request/getRequest.js index 2420211..e72b8e2 100644 --- a/seasoned_api/src/webserver/controllers/request/getRequest.js +++ b/seasoned_api/src/webserver/controllers/request/getRequest.js @@ -11,10 +11,9 @@ function fetchAllRequests(req, res) { const id = req.params.id; const { type } = req.query; - Promise.resolve() - .then(() => request.getRequestByIdAndType(id, type)) - .then((result) => res.send(result)) - .catch((error) => { + request.getRequestByIdAndType(id, type) + .then(result => res.send(result)) + .catch(error => { res.status(404).send({ success: false, error: error.message }); }); } diff --git a/seasoned_api/src/webserver/controllers/search/movieSearch.js b/seasoned_api/src/webserver/controllers/search/movieSearch.js index 906a132..4a9dfd7 100644 --- a/seasoned_api/src/webserver/controllers/search/movieSearch.js +++ b/seasoned_api/src/webserver/controllers/search/movieSearch.js @@ -1,7 +1,7 @@ -const SearchHistory = require('src/searchHistory/searchHistory'); const configuration = require('src/config/configuration').getInstance(); const Cache = require('src/tmdb/cache'); const TMDB = require('src/tmdb/tmdb'); +const SearchHistory = require('src/searchHistory/searchHistory'); const cache = new Cache(); const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); const searchHistory = new SearchHistory(); @@ -16,20 +16,25 @@ function movieSearchController(req, res) { const user = req.loggedInUser; const { query, page } = req.query; - Promise.resolve() - .then(() => { - if (user) { - return searchHistory.create(user, query); - } - return null - }) - .then(() => tmdb.movieSearch(query, page)) - .then((movies) => { - res.send(movies); - }) - .catch((error) => { - res.status(500).send({ success: false, error: error.message }); - }); + if (user) { + return searchHistory.create(user, query); + } + + tmdb.movieSearch(query, page) + .then(movieSearchResults => res.send(movieSearchResults)) + .catch(error => { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + // TODO log unhandled errors + console.log('caugth movie search controller error', error) + res.status(500).send({ + message: `An unexpected error occured while searching movies with query: ${query}` + }) + } + }) } module.exports = movieSearchController; diff --git a/seasoned_api/src/webserver/controllers/search/multiSearch.js b/seasoned_api/src/webserver/controllers/search/multiSearch.js index bf90753..37b44cf 100644 --- a/seasoned_api/src/webserver/controllers/search/multiSearch.js +++ b/seasoned_api/src/webserver/controllers/search/multiSearch.js @@ -1,11 +1,18 @@ -const SearchHistory = require('src/searchHistory/searchHistory'); const configuration = require('src/config/configuration').getInstance(); const Cache = require('src/tmdb/cache'); const TMDB = require('src/tmdb/tmdb'); +const SearchHistory = require('src/searchHistory/searchHistory'); const cache = new Cache(); const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); const searchHistory = new SearchHistory(); +function checkAndCreateJsonResponse(result) { + if (typeof result['createJsonResponse'] === 'function') { + return result.createJsonResponse() + } + return result +} + /** * Controller: Search for multi (movies, shows and people by query and pagey * @param {Request} req http request variable @@ -16,20 +23,23 @@ function multiSearchController(req, res) { const user = req.loggedInUser; const { query, page } = req.query; - Promise.resolve() - .then(() => { - if (user) { - return searchHistory.create(user, query); + if (user) { + searchHistory.create(user, query) + } + + return tmdb.multiSearch(query, page) + .then(multiSearchResults => res.send(multiSearchResults)) + .catch(error => { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + // TODO log unhandled errors + console.log('caugth multi search controller error', error) + res.status(500).send({ message: `An unexpected error occured while searching with query: ${query}` }) } - return null }) - .then(() => tmdb.multiSearch(query, page)) - .then((result) => { - res.send(result); - }) - .catch((error) => { - res.status(500).send({ success: false, error: error.message }); - }); } module.exports = multiSearchController; diff --git a/seasoned_api/src/webserver/controllers/search/personSearch.js b/seasoned_api/src/webserver/controllers/search/personSearch.js index 34bab20..7c35272 100644 --- a/seasoned_api/src/webserver/controllers/search/personSearch.js +++ b/seasoned_api/src/webserver/controllers/search/personSearch.js @@ -1,7 +1,7 @@ -const SearchHistory = require('src/searchHistory/searchHistory'); const configuration = require('src/config/configuration').getInstance(); const Cache = require('src/tmdb/cache'); const TMDB = require('src/tmdb/tmdb'); +const SearchHistory = require('src/searchHistory/searchHistory'); const cache = new Cache(); const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); const searchHistory = new SearchHistory(); @@ -16,20 +16,27 @@ function personSearchController(req, res) { const user = req.loggedInUser; const { query, page } = req.query; - Promise.resolve() - .then(() => { - if (user) { - return searchHistory.create(user, query); - } - return null - }) - .then(() => tmdb.personSearch(query, page)) - .then((person) => { - res.send(person); - }) - .catch((error) => { - res.status(500).send({ success: false, error: error.message }); - }); + if (user) { + return searchHistory.create(user, query); + } + + tmdb.personSearch(query, page) + .then((person) => { + res.send(person); + }) + .catch(error => { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + // TODO log unhandled errors + console.log('caugth person search controller error', error) + res.status(500).send({ + message: `An unexpected error occured while searching people with query: ${query}` + }) + } + }) } module.exports = personSearchController; diff --git a/seasoned_api/src/webserver/controllers/show/credits.js b/seasoned_api/src/webserver/controllers/show/credits.js new file mode 100644 index 0000000..fcfccbd --- /dev/null +++ b/seasoned_api/src/webserver/controllers/show/credits.js @@ -0,0 +1,26 @@ +const configuration = require('src/config/configuration').getInstance(); +const Cache = require('src/tmdb/cache'); +const TMDB = require('src/tmdb/tmdb'); + +const cache = new Cache(); +const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); + +const showCreditsController = (req, res) => { + const showId = req.params.id; + + tmdb.showCredits(showId) + .then(credits => res.send(credits.createJsonResponse())) + .catch(error => { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + // TODO log unhandled errors + console.log('caugth show credits controller error', error) + res.status(500).send({ message: 'An unexpected error occured while requesting show credits' }) + } + }) +} + +module.exports = showCreditsController; \ No newline at end of file diff --git a/seasoned_api/src/webserver/controllers/show/info.js b/seasoned_api/src/webserver/controllers/show/info.js new file mode 100644 index 0000000..5cd56e8 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/show/info.js @@ -0,0 +1,54 @@ +const configuration = require('src/config/configuration').getInstance(); +const Cache = require('src/tmdb/cache'); +const TMDB = require('src/tmdb/tmdb'); +const Plex = require('src/plex/plex'); +const cache = new Cache(); +const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); +const plex = new Plex(configuration.get('plex', 'ip')); + +function handleError(error, res) { + const { status, message } = error; + + if (status && message) { + res.status(error.status).send({ success: false, error: error.message }) + } else { + console.log('caught showinfo controller error', error) + res.status(500).send({ message: 'An unexpected error occured while requesting show info'}) + } +} + +/** + * Controller: Retrieve information for a show + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ + +async function showInfoController(req, res) { + const showId = req.params.id; + let { credits, check_existance } = req.query; + + credits && credits.toLowerCase() === 'true' ? credits = true : credits = false + check_existance && check_existance.toLowerCase() === 'true' ? check_existance = true : check_existance = false + + let tmdbQueue = [tmdb.showInfo(showId)] + if (credits) + tmdbQueue.push(tmdb.showCredits(showId)) + + try { + const [Show, Credits] = await Promise.all(tmdbQueue) + + const show = Show.createJsonResponse() + if (credits) + show.credits = Credits.createJsonResponse() + + if (check_existance) + show.exists_in_plex = await plex.existsInPlex(show) + + res.send(show) + } catch(error) { + handleError(error, res) + } +} + +module.exports = showInfoController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/listSearch.js b/seasoned_api/src/webserver/controllers/tmdb/listSearch.js deleted file mode 100644 index 80e7add..0000000 --- a/seasoned_api/src/webserver/controllers/tmdb/listSearch.js +++ /dev/null @@ -1,25 +0,0 @@ -const configuration = require('src/config/configuration').getInstance(); -const Cache = require('src/tmdb/cache'); -const TMDB = require('src/tmdb/tmdb'); - -const cache = new Cache(); -const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); - -/** - * Controller: Retrieve nowplaying movies / now airing shows - * @param {Request} req http request variable - * @param {Response} res - * @returns {Callback} - */ -function listSearchController(req, res) { - const listname = req.params.listname; - const { type, page } = req.query; - tmdb.listSearch(listname, type, page) - .then((results) => { - res.send(results); - }).catch((error) => { - res.status(404).send({ success: false, error: error.message }); - }); -} - -module.exports = listSearchController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/readMedia.js b/seasoned_api/src/webserver/controllers/tmdb/readMedia.js deleted file mode 100644 index d2c7294..0000000 --- a/seasoned_api/src/webserver/controllers/tmdb/readMedia.js +++ /dev/null @@ -1,25 +0,0 @@ -const configuration = require('src/config/configuration').getInstance(); -const Cache = require('src/tmdb/cache'); -const TMDB = require('src/tmdb/tmdb'); - -const cache = new Cache(); -const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); - -/** - * Controller: Retrieve information for a movie - * @param {Request} req http request variable - * @param {Response} res - * @returns {Callback} - */ -function readMediaController(req, res) { - const mediaId = req.params.mediaId; - const { type } = req.query; - tmdb.lookup(mediaId, type) - .then((movies) => { - res.send(movies); - }).catch((error) => { - res.status(404).send({ success: false, error: error.message }); - }); -} - -module.exports = readMediaController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js b/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js deleted file mode 100644 index 5d5910e..0000000 --- a/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js +++ /dev/null @@ -1,31 +0,0 @@ -const configuration = require('src/config/configuration').getInstance(); -const Cache = require('src/tmdb/cache'); -const TMDB = require('src/tmdb/tmdb'); - -const cache = new Cache(); -const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey')); - -/** - * Controller: Search for movies by query, page and optional type - * @param {Request} req http request variable - * @param {Response} res - * @returns {Callback} - */ -function searchMediaController(req, res) { - const { query, page, type } = req.query; - - Promise.resolve() - .then(() => tmdb.search(query, page, type)) - .then((movies) => { - if (movies !== undefined || movies.length > 0) { - res.send(movies); - } else { - res.status(404).send({ success: false, error: 'Search query did not return any results.' }); - } - }) - .catch((error) => { - res.status(500).send({ success: false, error: error.message }); - }); -} - -module.exports = searchMediaController;