Merge pull request #111 from KevinMidboe/api/v2
Api/v2
This commit was merged in pull request #111.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
development.json
|
||||||
env
|
env
|
||||||
shows.db
|
shows.db
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js: '8.7.0'
|
node_js: '11.9.0'
|
||||||
git:
|
git:
|
||||||
submodules: true
|
submodules: true
|
||||||
script:
|
script:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
<img src="https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=master"
|
<img src="https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=master"
|
||||||
alt="Travis CI">
|
alt="Travis CI">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage">
|
<a href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=api/v2">
|
||||||
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=coverage" alt="">
|
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=api/v2" alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json">
|
<a href="https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json">
|
||||||
<img src="https://snyk.io/test/github/KevinMidboe/seasonedShows/badge.svg?targetFile=seasoned_api/package.json" alt="">
|
<img src="https://snyk.io/test/github/KevinMidboe/seasonedShows/badge.svg?targetFile=seasoned_api/package.json" alt="">
|
||||||
|
|||||||
+2
-2
@@ -30,8 +30,8 @@
|
|||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"urijs": "^1.18.12",
|
"urijs": "^1.18.12",
|
||||||
"webfontloader": "^1.6.28",
|
"webfontloader": "^1.6.28",
|
||||||
"webpack": "^3.5.5",
|
"webpack": "^4.0.0",
|
||||||
"webpack-dev-server": "^2.4.5",
|
"webpack-dev-server": "^3.1.11",
|
||||||
"webpack-merge": "^4.1.0"
|
"webpack-merge": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -8,17 +8,13 @@
|
|||||||
"tmdb": {
|
"tmdb": {
|
||||||
"apiKey": ""
|
"apiKey": ""
|
||||||
},
|
},
|
||||||
|
"plex": {
|
||||||
|
"ip": ""
|
||||||
|
},
|
||||||
"raven": {
|
"raven": {
|
||||||
"DSN": ""
|
"DSN": ""
|
||||||
},
|
},
|
||||||
"mail": {
|
|
||||||
"host": "",
|
|
||||||
"user": "",
|
|
||||||
"password": "",
|
|
||||||
"user_pi": "",
|
|
||||||
"password_pi": ""
|
|
||||||
},
|
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"secret": "secret"
|
"secret": "secret"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
"tmdb": {
|
"tmdb": {
|
||||||
"apiKey": "bogus-api-key"
|
"apiKey": "bogus-api-key"
|
||||||
},
|
},
|
||||||
|
"plex": {
|
||||||
|
"ip": "0.0.0.0"
|
||||||
|
},
|
||||||
"raven": {
|
"raven": {
|
||||||
"DSN": ""
|
"DSN": ""
|
||||||
},
|
},
|
||||||
|
|||||||
+21
-10
@@ -7,37 +7,48 @@
|
|||||||
},
|
},
|
||||||
"main": "webserver/server.js",
|
"main": "webserver/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env SEASONED_CONFIG=conf/development.json PROD=true NODE_PATH=. node src/webserver/server.js",
|
"start": "cross-env SEASONED_CONFIG=conf/development.json PROD=true NODE_PATH=. babel-node src/webserver/server.js",
|
||||||
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --recursive test",
|
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --require @babel/register --recursive test/unit test/system",
|
||||||
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --recursive test && nyc report --reporter=text-lcov | coveralls",
|
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --require @babel/register --recursive test && nyc report --reporter=text-lcov | coveralls",
|
||||||
"lint": "./node_modules/.bin/eslint src/",
|
"lint": "./node_modules/.bin/eslint src/",
|
||||||
"update": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/plex/updateRequestsInPlex.js"
|
"update": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/plex/updateRequestsInPlex.js",
|
||||||
|
"docs": "yarn apiDocs; yarn classDocs",
|
||||||
|
"apiDocs": "",
|
||||||
|
"classDocs": "./script/generate-class-docs.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt-nodejs": "^0.0.3",
|
"axios": "^0.18.0",
|
||||||
|
"bcrypt": "^3.0.6",
|
||||||
"body-parser": "~1.18.2",
|
"body-parser": "~1.18.2",
|
||||||
"cross-env": "~5.1.4",
|
"cross-env": "~5.1.4",
|
||||||
"express": "~4.16.0",
|
"express": "~4.16.0",
|
||||||
"jsonwebtoken": "^8.2.0",
|
"jsonwebtoken": "^8.2.0",
|
||||||
"mongoose": "~5.2.12",
|
|
||||||
"km-moviedb": "^0.2.12",
|
"km-moviedb": "^0.2.12",
|
||||||
"node-cache": "^4.1.1",
|
"node-cache": "^4.1.1",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
"python-shell": "^0.5.0",
|
"python-shell": "^0.5.0",
|
||||||
|
"raven": "^2.4.2",
|
||||||
"request": "^2.87.0",
|
"request": "^2.87.0",
|
||||||
"request-promise": "^4.2",
|
"request-promise": "^4.2",
|
||||||
"sqlite3": "^4.0.0"
|
"sqlite3": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"coveralls": "^3.0.0",
|
"@babel/core": "^7.5.5",
|
||||||
|
"@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",
|
"eslint": "^4.9.0",
|
||||||
"eslint-config-airbnb-base": "^12.1.0",
|
"eslint-config-airbnb-base": "^12.1.0",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"mocha": "^5.0.4",
|
"mocha": "^6.2.0",
|
||||||
"mocha-lcov-reporter": "^1.3.0",
|
"mocha-lcov-reporter": "^1.3.0",
|
||||||
"nyc": "^11.6.0",
|
"nyc": "^11.6.0",
|
||||||
"raven": "^2.4.2",
|
|
||||||
"supertest": "^3.0.0",
|
"supertest": "^3.0.0",
|
||||||
"supertest-as-promised": "^4.0.1"
|
"supertest-as-promised": "^4.0.1",
|
||||||
|
"typescript": "^3.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,14 @@ CREATE TABLE IF NOT EXISTS requests(
|
|||||||
type CHAR(50) DEFAULT 'movie'
|
type CHAR(50) DEFAULT 'movie'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS request(
|
||||||
|
id int not null,
|
||||||
|
title text not null,
|
||||||
|
year int not null,
|
||||||
|
type char(10) not null,
|
||||||
|
date timestamp default (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS stray_eps(
|
CREATE TABLE IF NOT EXISTS stray_eps(
|
||||||
id TEXT UNIQUE,
|
id TEXT UNIQUE,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
DROP TABLE IF EXISTS user;
|
DROP TABLE IF EXISTS user;
|
||||||
DROP TABLE IF EXISTS search_history;
|
DROP TABLE IF EXISTS search_history;
|
||||||
DROP TABLE IF EXISTS requests;
|
DROP TABLE IF EXISTS requests;
|
||||||
|
DROP TABLE IF EXISTS request;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class SqliteDatabase {
|
|||||||
* @param {Array} parameters in the SQL query
|
* @param {Array} parameters in the SQL query
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async run(sql, parameters) {
|
run(sql, parameters) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.connection.run(sql, parameters, (error, result) => {
|
this.connection.run(sql, parameters, (error, result) => {
|
||||||
if (error)
|
if (error)
|
||||||
@@ -41,7 +41,7 @@ class SqliteDatabase {
|
|||||||
* @param {Array} parameters in the SQL query
|
* @param {Array} parameters in the SQL query
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async all(sql, parameters) {
|
all(sql, parameters) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.connection.all(sql, parameters, (err, rows) => {
|
this.connection.all(sql, parameters, (err, rows) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -58,7 +58,7 @@ class SqliteDatabase {
|
|||||||
* @param {Array} parameters in the SQL query
|
* @param {Array} parameters in the SQL query
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async get(sql, parameters) {
|
get(sql, parameters) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.connection.get(sql, parameters, (err, rows) => {
|
this.connection.get(sql, parameters, (err, rows) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -75,7 +75,7 @@ class SqliteDatabase {
|
|||||||
* @param {Array} parameters in the SQL query
|
* @param {Array} parameters in the SQL query
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async execute(sql) {
|
execute(sql) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.connection.exec(sql, (err, database) => {
|
this.connection.exec(sql, (err, database) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function getMagnetFromURL(url) {
|
|||||||
resolve(url)
|
resolve(url)
|
||||||
|
|
||||||
http.get(options, (res) => {
|
http.get(options, (res) => {
|
||||||
if (res.statusCode == 301) {
|
if (res.statusCode == 301 || res.statusCode == 302) {
|
||||||
resolve(res.headers.location)
|
resolve(res.headers.location)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -21,12 +21,12 @@ function getMagnetFromURL(url) {
|
|||||||
|
|
||||||
async function find(searchterm, callback) {
|
async function find(searchterm, callback) {
|
||||||
const options = {
|
const options = {
|
||||||
pythonPath: '/usr/bin/python3',
|
pythonPath: '../torrent_search/env/bin/python3',
|
||||||
// pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
|
scriptPath: '../torrent_search',
|
||||||
args: [searchterm, '-s', 'jackett', '-f', '--print'],
|
args: [searchterm, '-s', 'jackett', '-f', '--print']
|
||||||
};
|
}
|
||||||
|
|
||||||
PythonShell.run('../torrent_search/torrentSearch/search.py', options, callback);
|
PythonShell.run('torrentSearch/search.py', options, callback);
|
||||||
// PythonShell does not support return
|
// PythonShell does not support return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,12 +35,12 @@ async function callPythonAddMagnet(url, callback) {
|
|||||||
getMagnetFromURL(url)
|
getMagnetFromURL(url)
|
||||||
.then((magnet) => {
|
.then((magnet) => {
|
||||||
const options = {
|
const options = {
|
||||||
pythonPath: '/usr/bin/python',
|
pythonPath: '../delugeClient/env/bin/python3',
|
||||||
// pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
|
scriptPath: '../delugeClient',
|
||||||
args: [magnet],
|
args: ['add', magnet]
|
||||||
};
|
};
|
||||||
|
|
||||||
PythonShell.run('../app/magnet.py', options, callback);
|
PythonShell.run('deluge_cli.py', options, callback);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -51,19 +51,16 @@ async function callPythonAddMagnet(url, callback) {
|
|||||||
async function SearchPiratebay(query) {
|
async function SearchPiratebay(query) {
|
||||||
return await new Promise((resolve, reject) => find(query, (err, results) => {
|
return await new Promise((resolve, reject) => find(query, (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
/* eslint-disable no-console */
|
|
||||||
console.log('THERE WAS A FUCKING ERROR!\n', err);
|
console.log('THERE WAS A FUCKING ERROR!\n', err);
|
||||||
reject(Error('There was a error when searching for torrents'));
|
reject(Error('There was a error when searching for torrents'));
|
||||||
}
|
}
|
||||||
if (results) {
|
if (results) {
|
||||||
/* eslint-disable no-console */
|
|
||||||
console.log('result', results);
|
|
||||||
resolve(JSON.parse(results, null, '\t'));
|
resolve(JSON.parse(results, null, '\t'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function AddMagnet(magnet) {
|
async function AddMagnet(magnet, name, tmdb_id) {
|
||||||
return await new Promise((resolve, reject) => callPythonAddMagnet(magnet, (err, results) => {
|
return await new Promise((resolve, reject) => callPythonAddMagnet(magnet, (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
const Episode = require('src/plex/types/episode');
|
||||||
|
|
||||||
|
function convertPlexToEpisode(plexEpisode) {
|
||||||
|
const episode = new Episode(plexEpisode.title, plexEpisode.grandparentTitle, plexEpisode.year);
|
||||||
|
episode.season = plexEpisode.parentIndex;
|
||||||
|
episode.episode = plexEpisode.index;
|
||||||
|
episode.summary = plexEpisode.summary;
|
||||||
|
episode.rating = plexEpisode.rating;
|
||||||
|
|
||||||
|
if (plexEpisode.viewCount !== undefined) {
|
||||||
|
episode.views = plexEpisode.viewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plexEpisode.originallyAvailableAt !== undefined) {
|
||||||
|
episode.airdate = new Date(plexEpisode.originallyAvailableAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return episode;
|
||||||
|
}
|
||||||
|
module.exports = convertPlexToEpisode;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const Movie = require('src/plex/types/movie');
|
||||||
|
|
||||||
|
function convertPlexToMovie(plexMovie) {
|
||||||
|
const movie = new Movie(plexMovie.title, plexMovie.year);
|
||||||
|
movie.rating = plexMovie.rating;
|
||||||
|
movie.tagline = plexMovie.tagline;
|
||||||
|
|
||||||
|
if (plexMovie.summary !== undefined) {
|
||||||
|
movie.summary = plexMovie.summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = convertPlexToMovie;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const Show = require('src/plex/types/show');
|
||||||
|
|
||||||
|
function convertPlexToShow(plexShow) {
|
||||||
|
const show = new Show(plexShow.title, plexShow.year);
|
||||||
|
show.summary = plexShow.summary;
|
||||||
|
show.rating = plexShow.rating;
|
||||||
|
show.seasons = plexShow.childCount;
|
||||||
|
show.episodes = plexShow.leafCount;
|
||||||
|
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = convertPlexToShow;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
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, port=32400) {
|
||||||
|
this.plexIP = ip
|
||||||
|
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 this.search(tmdbMovie.title)
|
||||||
|
.then(plexMovies => plexMovies.some(plex => this.matchTmdbAndPlexMedia(plex, 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 = {
|
||||||
|
timeout: 2000,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (response === undefined || response.MediaContainer === undefined) {
|
||||||
|
console.log('response was not valid to map', response)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Plex;
|
||||||
@@ -3,6 +3,10 @@ const convertPlexToStream = require('src/plex/convertPlexToStream');
|
|||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
|
|
||||||
class PlexRepository {
|
class PlexRepository {
|
||||||
|
constructor(plexIP) {
|
||||||
|
this.plexIP = plexIP;
|
||||||
|
}
|
||||||
|
|
||||||
inPlex(tmdbResult) {
|
inPlex(tmdbResult) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => this.search(tmdbResult.title))
|
.then(() => this.search(tmdbResult.title))
|
||||||
@@ -15,9 +19,10 @@ class PlexRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
search(query) {
|
search(query) {
|
||||||
console.log('searching:', query)
|
const queryUri = encodeURIComponent(query)
|
||||||
|
const uri = encodeURI(`http://${this.plexIP}:32400/search?query=${queryUri}`)
|
||||||
const options = {
|
const options = {
|
||||||
uri: `http://10.0.0.44:32400/search?query=${query}`,
|
uri: uri,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
},
|
},
|
||||||
@@ -26,6 +31,7 @@ class PlexRepository {
|
|||||||
|
|
||||||
return rp(options)
|
return rp(options)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
throw new Error('Unable to search plex.')
|
throw new Error('Unable to search plex.')
|
||||||
})
|
})
|
||||||
.then(result => this.mapResults(result))
|
.then(result => this.mapResults(result))
|
||||||
@@ -39,6 +45,7 @@ class PlexRepository {
|
|||||||
tmdb.matchedInPlex = false
|
tmdb.matchedInPlex = false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// console.log('plex and tmdb:', plexResult, '\n', tmdb)
|
||||||
plexResult.results.map((plexItem) => {
|
plexResult.results.map((plexItem) => {
|
||||||
if (tmdb.title === plexItem.title && tmdb.year === plexItem.year)
|
if (tmdb.title === plexItem.title && tmdb.year === plexItem.year)
|
||||||
tmdb.matchedInPlex = true;
|
tmdb.matchedInPlex = true;
|
||||||
@@ -52,7 +59,6 @@ class PlexRepository {
|
|||||||
mapResults(response) {
|
mapResults(response) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('plexResponse:', response)
|
|
||||||
if (!response.MediaContainer.hasOwnProperty('Metadata')) return [[], 0];
|
if (!response.MediaContainer.hasOwnProperty('Metadata')) return [[], 0];
|
||||||
|
|
||||||
const mappedResults = response.MediaContainer.Metadata.filter((element) => {
|
const mappedResults = response.MediaContainer.Metadata.filter((element) => {
|
||||||
@@ -65,7 +71,7 @@ class PlexRepository {
|
|||||||
|
|
||||||
nowPlaying() {
|
nowPlaying() {
|
||||||
const options = {
|
const options = {
|
||||||
uri: 'http://10.0.0.44:32400/status/sessions',
|
uri: `http://${this.plexIP}:32400/status/sessions`,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const configuration = require('src/config/configuration').getInstance();
|
|||||||
const TMDB = require('src/tmdb/tmdb');
|
const TMDB = require('src/tmdb/tmdb');
|
||||||
const establishedDatabase = require('src/database/database');
|
const establishedDatabase = require('src/database/database');
|
||||||
|
|
||||||
const plexRepository = new PlexRepository();
|
const plexRepository = new PlexRepository(configuration.get('plex', 'ip'));
|
||||||
const cache = new Cache();
|
const cache = new Cache();
|
||||||
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
||||||
|
|
||||||
@@ -86,7 +86,11 @@ class RequestRepository {
|
|||||||
}
|
}
|
||||||
throw new Error('Unable to fetch your requests');
|
throw new Error('Unable to fetch your requests');
|
||||||
})
|
})
|
||||||
.then((result) => { return result; });
|
.then((result) => {
|
||||||
|
// TODO do a correct mapping before sending, not just a dump of the database
|
||||||
|
result.map(item => item.poster = item.poster_path)
|
||||||
|
return result
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRequestedById(id, type, status) {
|
updateRequestedById(id, type, status) {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class Episode {
|
||||||
|
constructor(title, show, year) {
|
||||||
|
this.title = title;
|
||||||
|
this.show = show;
|
||||||
|
this.year = year;
|
||||||
|
this.season = null;
|
||||||
|
this.episode = null;
|
||||||
|
this.summary = null;
|
||||||
|
this.rating = null;
|
||||||
|
this.views = null;
|
||||||
|
this.aired = null;
|
||||||
|
this.type = 'episode';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Episode;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class Movie {
|
||||||
|
constructor(title, year) {
|
||||||
|
this.title = title;
|
||||||
|
this.year = year;
|
||||||
|
this.summary = null;
|
||||||
|
this.rating = null;
|
||||||
|
this.tagline = null;
|
||||||
|
this.type = 'movie';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Movie;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
class Show {
|
||||||
|
constructor(title, year) {
|
||||||
|
this.title = title;
|
||||||
|
this.year = year;
|
||||||
|
this.summary = null;
|
||||||
|
this.rating = null;
|
||||||
|
this.seasons = null;
|
||||||
|
this.episodes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Show;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
const Plex = require('src/plex/plex')
|
||||||
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
const plex = new Plex(configuration.get('plex', 'ip'))
|
||||||
|
const establishedDatabase = require('src/database/database');
|
||||||
|
|
||||||
|
class UpdateRequestsInPlex {
|
||||||
|
constructor() {
|
||||||
|
this.database = establishedDatabase;
|
||||||
|
this.queries = {
|
||||||
|
getMovies: `SELECT * FROM requests WHERE status = 'requested' OR status = 'downloading'`,
|
||||||
|
// getMovies: "select * from requests where status is 'reset'",
|
||||||
|
saveNewStatus: `UPDATE requests SET status = ? WHERE id IS ? and type IS ?`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getByStatus() {
|
||||||
|
return this.database.all(this.queries.getMovies);
|
||||||
|
}
|
||||||
|
scrub() {
|
||||||
|
return this.getByStatus()
|
||||||
|
.then((requests) => Promise.all(requests.map(movie => plex.existsInPlex(movie))))
|
||||||
|
}
|
||||||
|
|
||||||
|
commitNewStatus(status, id, type, title) {
|
||||||
|
console.log(type, title, 'updated to:', status)
|
||||||
|
this.database.run(this.queries.saveNewStatus, [status, id, type])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateStatus(status) {
|
||||||
|
this.getByStatus()
|
||||||
|
.then(requests => Promise.all(requests.map(request => plex.existsInPlex(request))))
|
||||||
|
.then(matchedRequests => matchedRequests.filter(request => request.existsInPlex))
|
||||||
|
.then(newMatches => newMatches.map(match => this.commitNewStatus(status, match.id, match.type, match.title)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestsUpdater = new UpdateRequestsInPlex();
|
||||||
|
requestsUpdater.updateStatus('downloaded')
|
||||||
|
|
||||||
|
module.exports = UpdateRequestsInPlex
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
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 establishedDatabase = require('src/database/database');
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
class RequestRepository {
|
||||||
|
constructor(database) {
|
||||||
|
this.database = database || establishedDatabase;
|
||||||
|
this.queries = {
|
||||||
|
add: 'insert into requests (id,title,year,poster_path,background_path,requested_by,ip,user_agent,type) values(?,?,?,?,?,?,?,?,?)',
|
||||||
|
fetchAll: 'select * from requests where status != "downloaded" order by date desc LIMIT 25 OFFSET ?*25-25',
|
||||||
|
fetchAllFilteredStatus: 'select * from requests where status = ? order by date desc LIMIT 25 offset ?*25-25',
|
||||||
|
totalRequests: 'select count(*) as totalRequests from requests where status != "downloaded"',
|
||||||
|
totalRequestsFilteredStatus: 'select count(*) as totalRequests from requests where status = ?',
|
||||||
|
fetchAllSort: `select id, type from request order by ? ?`,
|
||||||
|
fetchAllFilter: `select id, type from request where ? is "?"`,
|
||||||
|
fetchAllQuery: `select id, type from request where title like "%?%" or year like "%?%"`,
|
||||||
|
fetchAllFilterAndSort: `select id, type from request where ? is "?" order by ? ?`,
|
||||||
|
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 ?'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sortAndFilterToDbQuery(by, direction, filter, query) {
|
||||||
|
let dbQuery = undefined;
|
||||||
|
|
||||||
|
if (query !== undefined) {
|
||||||
|
const dbParams = [query, query];
|
||||||
|
const dbquery = this.queries.fetchAllQuery
|
||||||
|
|
||||||
|
dbQuery = dbquery.split('').map((char) => char === '?' ? dbParams.shift() : char).join('')
|
||||||
|
}
|
||||||
|
else if (by !== undefined && filter !== undefined) {
|
||||||
|
const paramToColumnAndValue = {
|
||||||
|
movie: ['type', 'movie'],
|
||||||
|
show: ['type', 'show']
|
||||||
|
}
|
||||||
|
const dbParams = paramToColumnAndValue[filter].concat([by, direction]);
|
||||||
|
const query = this.queries.fetchAllFilterAndSort;
|
||||||
|
|
||||||
|
dbQuery = query.split('').map((char) => char === '?' ? dbParams.shift() : char).join('')
|
||||||
|
}
|
||||||
|
else if (by !== undefined) {
|
||||||
|
const dbParams = [by, direction];
|
||||||
|
const query = this.queries.fetchAllSort;
|
||||||
|
|
||||||
|
dbQuery = query.split('').map((char) => char === '?' ? dbParams.shift() : char).join('')
|
||||||
|
}
|
||||||
|
else if (filter !== undefined) {
|
||||||
|
const paramToColumnAndValue = {
|
||||||
|
movie: ['type', 'movie'],
|
||||||
|
show: ['type', 'show'],
|
||||||
|
downloaded: [this.queries.downloaded, 'downloaded']
|
||||||
|
// downloading: [this.database.delugeStatus, 'downloading']
|
||||||
|
}
|
||||||
|
const dbParams = paramToColumnAndValue[filter]
|
||||||
|
const query = this.queries.fetchAllFilter;
|
||||||
|
|
||||||
|
dbQuery = query.split('').map((char) => char === '?' ? dbParams.shift() : char).join('')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dbQuery = this.queries.fetchAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
mapToTmdbByType(rows) {
|
||||||
|
return rows.map((row) => {
|
||||||
|
if (row.type === 'movie')
|
||||||
|
return tmdb.movieInfo(row.id)
|
||||||
|
else if (row.type === 'show')
|
||||||
|
return tmdb.showInfo(row.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add tmdb movie|show to requests
|
||||||
|
* @param {tmdb} tmdb class of movie|show to add
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
requestFromTmdb(tmdb, ip, user_agent, user) {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => this.database.get(this.queries.read, [tmdb.id, tmdb.type]))
|
||||||
|
.then(row => assert.equal(row, undefined, 'Id has already been requested'))
|
||||||
|
.then(() => this.database.run(this.queries.add, [tmdb.id, tmdb.title, tmdb.year, tmdb.poster, tmdb.backdrop, user, ip, user_agent, tmdb.type]))
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.name === 'AssertionError' || error.message.endsWith('been requested')) {
|
||||||
|
throw new Error('This id is already requested', error.message);
|
||||||
|
}
|
||||||
|
console.log('Error @ request.addTmdb:', error);
|
||||||
|
throw new Error('Could not add request');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get request item by id
|
||||||
|
* @param {String} id
|
||||||
|
* @param {String} type
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getRequestByIdAndType(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 {
|
||||||
|
id: row.id,
|
||||||
|
title: row.title,
|
||||||
|
year: row.year,
|
||||||
|
type: row.type,
|
||||||
|
status: row.status,
|
||||||
|
requested_date: new Date(row.date)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all requests with optional sort and filter params
|
||||||
|
* @param {String} what we are sorting by
|
||||||
|
* @param {String} direction that can be either 'asc' or 'desc', default 'asc'.
|
||||||
|
* @param {String} params to filter by
|
||||||
|
* @param {String} query param to filter result on. Filters on title and year
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
fetchAll(page=1, sort_by=undefined, sort_direction='asc', filter=undefined, query=undefined) {
|
||||||
|
// TODO implemented sort and filter
|
||||||
|
page = parseInt(page)
|
||||||
|
let fetchQuery = this.queries.fetchAll
|
||||||
|
let fetchTotalResults = this.queries.totalRequests
|
||||||
|
let fetchParams = [page]
|
||||||
|
|
||||||
|
if (filter && (filter === 'downloading' || filter === 'downloaded' || filter === 'requested')) {
|
||||||
|
console.log('tes')
|
||||||
|
fetchQuery = this.queries.fetchAllFilteredStatus
|
||||||
|
fetchTotalResults = this.queries.totalRequestsFilteredStatus
|
||||||
|
fetchParams = [filter, page]
|
||||||
|
} else {
|
||||||
|
filter = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then((dbQuery) => this.database.all(fetchQuery, fetchParams))
|
||||||
|
.then(async (rows) => {
|
||||||
|
const sqliteResponse = await this.database.get(fetchTotalResults, filter ? filter : undefined)
|
||||||
|
const totalRequests = sqliteResponse['totalRequests']
|
||||||
|
const totalPages = Math.ceil(totalRequests / 26)
|
||||||
|
|
||||||
|
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, totalRequests]) => Promise.resolve({
|
||||||
|
results: result, total_results: totalRequests, page: page, total_pages: totalPages
|
||||||
|
}))
|
||||||
|
.catch(error => { console.log(error);throw error })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = RequestRepository;
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// TODO : test title and date are valid matches to columns in the database
|
||||||
|
const validSortParams = ['title', 'date']
|
||||||
|
const validSortDirs = ['asc', 'desc']
|
||||||
|
const validFilterParams = ['movie', 'show', 'seeding', 'downloading', 'paused', 'finished', 'downloaded']
|
||||||
|
|
||||||
|
function validSort(by, direction) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (by === undefined) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validSortParams.includes(by) && validSortDirs.includes(direction)) {
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject(new Error(`invalid sort parameter, must be of: ${validSortParams} with optional sort directions: ${validSortDirs} appended with ':'`))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validFilter(filter_param) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (filter_param === undefined) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter_param && validFilterParams.includes(filter_param)) {
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject(new Error(`filter parameteres must be of type: ${validFilterParams}`))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { validSort, validFilter }
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"presets": ["@babel/preset-env"]
|
||||||
|
}
|
||||||
@@ -18,12 +18,12 @@ class Cache {
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
get(key) {
|
get(key) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => this.database.get(this.queries.read, [key]))
|
.then(() => this.database.get(this.queries.read, [key]))
|
||||||
.then((row) => {
|
.then(row => {
|
||||||
assert(row, 'Could not find cache enrty with that key.');
|
assert(row, 'Could not find cache entry with that key.');
|
||||||
return JSON.parse(row.value);
|
return JSON.parse(row.value);
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,44 +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 convertTmdbToSeasoned(tmdb, manualType = undefined) {
|
|
||||||
const title = tmdb.title || tmdb.name;
|
|
||||||
const year = translateYear(tmdb.release_date || tmdb.first_air_date);
|
|
||||||
const type = manualType || convertType(tmdb.media_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;
|
|
||||||
+237
-88
@@ -1,114 +1,241 @@
|
|||||||
const moviedb = require('km-moviedb');
|
const moviedb = require('km-moviedb');
|
||||||
const convertTmdbToSeasoned = require('src/tmdb/convertTmdbToSeasoned');
|
|
||||||
|
|
||||||
const TMDB_METHODS = {
|
const { Movie, Show, Person, Credits, ReleaseDates } = require('src/tmdb/types');
|
||||||
upcoming: { movie: 'miscUpcomingMovies' },
|
// const { tmdbInfo } = require('src/tmdb/types')
|
||||||
discover: { movie: 'discoverMovie', show: 'discoverTv' },
|
|
||||||
popular: { movie: 'miscPopularMovies', show: 'miscPopularTvs' },
|
|
||||||
nowplaying: { movie: 'miscNowPlayingMovies', show: 'tvOnTheAir' },
|
|
||||||
similar: { movie: 'movieSimilar', show: 'tvSimilar' },
|
|
||||||
search: { movie: 'searchMovie', show: 'searchTv', multi: 'searchMulti' },
|
|
||||||
info: { movie: 'movieInfo', show: 'tvInfo' }
|
|
||||||
};
|
|
||||||
|
|
||||||
class TMDB {
|
class TMDB {
|
||||||
constructor(cache, apiKey, tmdbLibrary) {
|
constructor(cache, apiKey, tmdbLibrary) {
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
|
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
|
||||||
this.cacheTags = {
|
this.cacheTags = {
|
||||||
search: 'se',
|
multiSearch: 'mus',
|
||||||
info: 'i',
|
movieSearch: 'mos',
|
||||||
upcoming: 'u',
|
showSearch: 'ss',
|
||||||
discover: 'd',
|
personSearch: 'ps',
|
||||||
popular: 'p',
|
movieInfo: 'mi',
|
||||||
nowplaying: 'n',
|
movieCredits: 'mc',
|
||||||
similar: 'si',
|
movieReleaseDates: 'mrd',
|
||||||
};
|
showInfo: 'si',
|
||||||
}
|
showCredits: 'sc',
|
||||||
|
personInfo: 'pi',
|
||||||
|
miscNowPlayingMovies: 'npm',
|
||||||
|
miscPopularMovies: 'pm',
|
||||||
|
miscTopRatedMovies: 'tpm',
|
||||||
|
miscUpcomingMovies: 'um',
|
||||||
|
tvOnTheAir: 'toa',
|
||||||
|
miscPopularTvs: 'pt',
|
||||||
|
miscTopRatedTvs: 'trt',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a specific movie by id from TMDB.
|
* Retrieve a specific movie by id from TMDB.
|
||||||
* @param {Number} identifier of the movie you want to retrieve
|
* @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
|
* @returns {Promise} succeeds if movie was found
|
||||||
*/
|
*/
|
||||||
lookup(identifier, type = 'movie') {
|
movieInfo(identifier) {
|
||||||
const query = { id: identifier };
|
const query = { id: identifier };
|
||||||
const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`;
|
const cacheKey = `${this.cacheTags.movieInfo}:${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.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return this.cache.get(cacheKey)
|
||||||
* Retrive search results from TMDB.
|
.catch(() => this.tmdb('movieInfo', query))
|
||||||
|
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie info'))
|
||||||
|
.then(movie => this.cache.set(cacheKey, movie, 1))
|
||||||
|
.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 = `${this.cacheTags.movieCredits}:${identifier}`
|
||||||
|
|
||||||
|
return this.cache.get(cacheKey)
|
||||||
|
.catch(() => this.tmdb('movieCredits', query))
|
||||||
|
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie credits'))
|
||||||
|
.then(credits => this.cache.set(cacheKey, credits, 1))
|
||||||
|
.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 = `${this.cacheTags.movieReleaseDates}:${identifier}`
|
||||||
|
|
||||||
|
return this.cache.get(cacheKey)
|
||||||
|
.catch(() => this.tmdb('movieReleaseDates', query))
|
||||||
|
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie release dates'))
|
||||||
|
.then(releaseDates => this.cache.set(cacheKey, releaseDates, 1))
|
||||||
|
.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 = `${this.cacheTags.showInfo}:${identifier}`;
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
showCredits(identifier) {
|
||||||
|
const query = { id: identifier }
|
||||||
|
const cacheKey = `${this.cacheTags.showCredits}:${identifier}`
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = `${this.cacheTags.personInfo}:${identifier}`;
|
||||||
|
|
||||||
|
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 this.cache.get(cacheKey)
|
||||||
|
.catch(() => this.tmdb('searchMulti', query))
|
||||||
|
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'search results'))
|
||||||
|
.then(response => this.cache.set(cacheKey, response, 1))
|
||||||
|
.then(response => this.mapResults(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrive movie search results from TMDB.
|
||||||
* @param {String} text query you want to search for
|
* @param {String} text query you want to search for
|
||||||
* @param {Number} page representing pagination of results
|
* @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
|
* @returns {Promise} dict with query results, current page and total_pages
|
||||||
*/
|
*/
|
||||||
search(text, page = 1, type = 'multi') {
|
movieSearch(query, page=1) {
|
||||||
const query = { query: text, page: page };
|
const tmdbquery = { query: query, page: page };
|
||||||
const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`;
|
const cacheKey = `${this.cacheTags.movieSearch}:${page}:${query}`;
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return this.cache.get(cacheKey)
|
||||||
* Fetches a given list from tmdb.
|
.catch(() => this.tmdb('searchMovie', tmdbquery))
|
||||||
* @param {String} listName Name of list
|
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie search results'))
|
||||||
* @param {String} type filter results by type (default movie)
|
.then(response => this.cache.set(cacheKey, response, 1))
|
||||||
|
.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
|
* @param {Number} page representing pagination of results
|
||||||
* @returns {Promise} dict with query results, current page and total_pages
|
* @returns {Promise} dict with query results, current page and total_pages
|
||||||
*/
|
*/
|
||||||
listSearch(listName, type = 'movie', page = '1') {
|
showSearch(query, page=1) {
|
||||||
const query = { page: page };
|
const tmdbquery = { query: query, page: page };
|
||||||
console.log(query);
|
const cacheKey = `${this.cacheTags.showSearch}:${page}:${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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
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'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(query, page=1) {
|
||||||
|
|
||||||
|
const tmdbquery = { query: query, page: page, include_adult: true };
|
||||||
|
const cacheKey = `${this.cacheTags.personSearch}:${page}:${query}`;
|
||||||
|
|
||||||
|
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 this.cache.get(cacheKey)
|
||||||
|
.catch(() => this.tmdb(listname, query))
|
||||||
|
.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 this.cache.get(cacheKey)
|
||||||
|
.catch(() => this.tmdb(listname, query))
|
||||||
|
.catch(tmdbError => this.tmdbErrorResponse(tmdbError, 'show list ' + listname))
|
||||||
|
.then(response => this.cache.set(cacheKey, response, 1))
|
||||||
|
.then(response => this.mapResults(response, 'show'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Maps our response from tmdb api to a movie/show object.
|
* Maps our response from tmdb api to a movie/show object.
|
||||||
* @param {String} response from tmdb.
|
* @param {String} response from tmdb.
|
||||||
* @param {String} The type declared in listSearch.
|
* @param {String} The type declared in listSearch.
|
||||||
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
|
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
|
||||||
*/
|
*/
|
||||||
mapResults(response, type) {
|
mapResults(response, type=undefined) {
|
||||||
console.log(response.page);
|
// console.log(response.results)
|
||||||
return Promise.resolve()
|
// response.results.map(te => console.table(te))
|
||||||
.then(() => {
|
|
||||||
const mappedResults = response.results.filter((element) => {
|
let results = response.results.map(result => {
|
||||||
return (element.media_type === 'movie' || element.media_type === 'tv' || element.media_type === undefined);
|
if (type === 'movie' || result.media_type === 'movie') {
|
||||||
}).map((element) => convertTmdbToSeasoned(element, type));
|
const movie = Movie.convertFromTmdbResponse(result)
|
||||||
return {
|
return movie.createJsonResponse()
|
||||||
results: mappedResults,
|
} else if (type === 'show' || result.media_type === 'tv') {
|
||||||
page: response.page,
|
const show = Show.convertFromTmdbResponse(result)
|
||||||
total_pages: response.total_pages,
|
return show.createJsonResponse()
|
||||||
total_results: response.total_results
|
} else if (type === 'person' || result.media_type === 'person') {
|
||||||
}
|
const person = Person.convertFromTmdbResponse(result)
|
||||||
})
|
return person.createJsonResponse()
|
||||||
.catch((error) => { throw new Error(error); });
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: results,
|
||||||
|
page: response.page,
|
||||||
|
total_results: response.total_results,
|
||||||
|
total_pages: response.total_pages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps moviedb library to support Promises.
|
* Wraps moviedb library to support Promises.
|
||||||
@@ -122,7 +249,7 @@ class TMDB {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return reject(error);
|
return reject(error);
|
||||||
}
|
}
|
||||||
return resolve(reponse);
|
resolve(reponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!argument) {
|
if (!argument) {
|
||||||
@@ -132,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;
|
module.exports = TMDB;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Movie } from './types'
|
||||||
|
|
||||||
|
Movie('str', 123)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = TMDB;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Movie from './types/movie.js'
|
||||||
|
import Show from './types/show.js'
|
||||||
|
import Person from './types/person.js'
|
||||||
|
import Credits from './types/credits.js'
|
||||||
|
import ReleaseDates from './types/releaseDates.js'
|
||||||
|
|
||||||
|
module.exports = { Movie, Show, Person, Credits, ReleaseDates }
|
||||||
@@ -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 }
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
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 =>
|
||||||
|
new CastMember(cast.character, cast.gender, cast.id, cast.name, cast.profile_path))
|
||||||
|
const allCrew = crew.map(crew =>
|
||||||
|
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 = 'cast member';
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 'crew member';
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Credits;
|
||||||
@@ -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;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
class Person {
|
||||||
|
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.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,
|
||||||
|
name: this.name,
|
||||||
|
poster: this.poster,
|
||||||
|
birthday: this.birthday,
|
||||||
|
deathday: this.deathday,
|
||||||
|
known_for_department: this.knownForDepartment,
|
||||||
|
adult: this.adult,
|
||||||
|
type: this.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Person;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -2,36 +2,44 @@ const User = require('src/user/user');
|
|||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
class Token {
|
class Token {
|
||||||
constructor(user) {
|
constructor(user, admin=false) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
this.admin = admin;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new token.
|
* Generate a new token.
|
||||||
* @param {String} secret a cipher of the token
|
* @param {String} secret a cipher of the token
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
toString(secret) {
|
toString(secret) {
|
||||||
return jwt.sign({ username: this.user.username }, secret);
|
const username = this.user.username;
|
||||||
}
|
const admin = this.admin;
|
||||||
|
let data = { username }
|
||||||
|
|
||||||
/**
|
if (admin)
|
||||||
|
data = { ...data, admin }
|
||||||
|
|
||||||
|
return jwt.sign(data, secret, { expiresIn: '90d' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Decode a token.
|
* Decode a token.
|
||||||
* @param {Token} jwtToken an encrypted token
|
* @param {Token} jwtToken an encrypted token
|
||||||
* @param {String} secret a cipher of the token
|
* @param {String} secret a cipher of the token
|
||||||
* @returns {Token}
|
* @returns {Token}
|
||||||
*/
|
*/
|
||||||
static fromString(jwtToken, secret) {
|
static fromString(jwtToken, secret) {
|
||||||
let username = null;
|
let username = null;
|
||||||
|
|
||||||
try {
|
const token = jwt.verify(jwtToken, secret, { clockTolerance: 10000 })
|
||||||
username = jwt.verify(jwtToken, secret).username;
|
if (token.username === undefined || token.username === null)
|
||||||
} catch (error) {
|
throw new Error('Malformed token')
|
||||||
throw new Error('The token is invalid.');
|
|
||||||
}
|
username = token.username
|
||||||
const user = new User(username);
|
const user = new User(username)
|
||||||
return new Token(user);
|
return new Token(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Token;
|
module.exports = Token;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class UserRepository {
|
|||||||
if (error.name === 'AssertionError' || error.message.endsWith('user_name')) {
|
if (error.name === 'AssertionError' || error.message.endsWith('user_name')) {
|
||||||
throw new Error('That username is already registered');
|
throw new Error('That username is already registered');
|
||||||
}
|
}
|
||||||
|
throw Error(error)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +1,72 @@
|
|||||||
const bcrypt = require('bcrypt-nodejs');
|
const bcrypt = require('bcrypt');
|
||||||
const UserRepository = require('src/user/userRepository');
|
const UserRepository = require('src/user/userRepository');
|
||||||
|
|
||||||
class UserSecurity {
|
class UserSecurity {
|
||||||
constructor(database) {
|
constructor(database) {
|
||||||
this.userRepository = new UserRepository(database);
|
this.userRepository = new UserRepository(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new user in PlanFlix.
|
* Create a new user in PlanFlix.
|
||||||
* @param {User} user the new user you want to create
|
* @param {User} user the new user you want to create
|
||||||
* @param {String} clearPassword a password of the user
|
* @param {String} clearPassword a password of the user
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
createNewUser(user, clearPassword) {
|
createNewUser(user, clearPassword) {
|
||||||
if (user.username.trim() === '') {
|
if (user.username.trim() === '') {
|
||||||
throw new Error('The username is empty.');
|
throw new Error('The username is empty.');
|
||||||
} else if (clearPassword.trim() === '') {
|
} else if (clearPassword.trim() === '') {
|
||||||
throw new Error('The password is empty.');
|
throw new Error('The password is empty.');
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => this.userRepository.create(user))
|
.then(() => this.userRepository.create(user))
|
||||||
.then(() => UserSecurity.hashPassword(clearPassword))
|
.then(() => UserSecurity.hashPassword(clearPassword))
|
||||||
.then(hash => this.userRepository.changePassword(user, hash));
|
.then(hash => this.userRepository.changePassword(user, hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login into PlanFlix.
|
* Login into PlanFlix.
|
||||||
* @param {User} user the user you want to login
|
* @param {User} user the user you want to login
|
||||||
* @param {String} clearPassword the user's password
|
* @param {String} clearPassword the user's password
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
login(user, clearPassword) {
|
login(user, clearPassword) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => this.userRepository.retrieveHash(user))
|
.then(() => this.userRepository.retrieveHash(user))
|
||||||
.then(hash => UserSecurity.compareHashes(hash, clearPassword))
|
.then(hash => UserSecurity.compareHashes(hash, clearPassword))
|
||||||
.catch(() => { throw new Error('Wrong username or password.'); });
|
.catch(() => { throw new Error('Incorrect username or password.'); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare between a password and a hash password from database.
|
* Compare between a password and a hash password from database.
|
||||||
* @param {String} hash the hash password from database
|
* @param {String} hash the hash password from database
|
||||||
* @param {String} clearPassword the user's password
|
* @param {String} clearPassword the user's password
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
static compareHashes(hash, clearPassword) {
|
static compareHashes(hash, clearPassword) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
bcrypt.compare(clearPassword, hash, (error, matches) => {
|
bcrypt.compare(clearPassword, hash, (error, match) => {
|
||||||
if (matches === true) {
|
if (match)
|
||||||
resolve();
|
resolve()
|
||||||
} else {
|
reject()
|
||||||
reject();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hashes a password.
|
* Hashes a password.
|
||||||
* @param {String} clearPassword the user's password
|
* @param {String} clearPassword the user's password
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
static hashPassword(clearPassword) {
|
static hashPassword(clearPassword) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
bcrypt.hash(clearPassword, null, null, (error, hash) => {
|
const saltRounds = 10;
|
||||||
resolve(hash);
|
bcrypt.hash(clearPassword, saltRounds, (error, hash) => {
|
||||||
});
|
resolve(hash);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UserSecurity;
|
module.exports = UserSecurity;
|
||||||
|
|||||||
@@ -6,16 +6,14 @@ const mustBeAuthenticated = require('./middleware/mustBeAuthenticated');
|
|||||||
const mustBeAdmin = require('./middleware/mustBeAdmin');
|
const mustBeAdmin = require('./middleware/mustBeAdmin');
|
||||||
const configuration = require('src/config/configuration').getInstance();
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
|
||||||
|
const listController = require('./controllers/list/listController');
|
||||||
|
|
||||||
// TODO: Have our raven router check if there is a value, if not don't enable raven.
|
// TODO: Have our raven router check if there is a value, if not don't enable raven.
|
||||||
Raven.config(configuration.get('raven', 'DSN')).install();
|
Raven.config(configuration.get('raven', 'DSN')).install();
|
||||||
|
|
||||||
const app = express(); // define our app using express
|
const app = express(); // define our app using express
|
||||||
app.use(Raven.requestHandler());
|
app.use(Raven.requestHandler());
|
||||||
// this will let us get the data from a POST
|
|
||||||
// configure app to use bodyParser()
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
// router.use(bodyParser.urlencoded({ extended: true }));
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080'];
|
const allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080'];
|
||||||
@@ -30,11 +28,9 @@ router.use(tokenToUser);
|
|||||||
// TODO: Should have a separate middleware/router for handling headers.
|
// TODO: Should have a separate middleware/router for handling headers.
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
// TODO add logging of all incoming
|
// TODO add logging of all incoming
|
||||||
console.log('Request: ', req.originalUrl);
|
const origin = req.headers.origin;
|
||||||
const origin = req.headers.origin;
|
|
||||||
if (allowedOrigins.indexOf(origin) > -1) {
|
if (allowedOrigins.indexOf(origin) > -1) {
|
||||||
console.log('allowed');
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
||||||
}
|
}
|
||||||
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, loggedinuser');
|
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, loggedinuser');
|
||||||
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT');
|
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT');
|
||||||
@@ -67,19 +63,50 @@ router.get('/v1/seasoned/all', require('./controllers/seasoned/readStrays.js'));
|
|||||||
router.get('/v1/seasoned/:strayId', require('./controllers/seasoned/strayById.js'));
|
router.get('/v1/seasoned/:strayId', require('./controllers/seasoned/strayById.js'));
|
||||||
router.post('/v1/seasoned/verify/:strayId', require('./controllers/seasoned/verifyStray.js'));
|
router.post('/v1/seasoned/verify/:strayId', require('./controllers/seasoned/verifyStray.js'));
|
||||||
|
|
||||||
|
router.get('/v2/search/', require('./controllers/search/multiSearch.js'));
|
||||||
|
router.get('/v2/search/movie', require('./controllers/search/movieSearch.js'));
|
||||||
|
router.get('/v2/search/show', require('./controllers/search/showSearch.js'));
|
||||||
|
router.get('/v2/search/person', require('./controllers/search/personSearch.js'));
|
||||||
|
|
||||||
|
router.get('/v2/movie/now_playing', listController.nowPlayingMovies);
|
||||||
|
router.get('/v2/movie/popular', listController.popularMovies);
|
||||||
|
router.get('/v2/movie/top_rated', listController.topRatedMovies);
|
||||||
|
router.get('/v2/movie/upcoming', listController.upcomingMovies);
|
||||||
|
|
||||||
|
router.get('/v2/show/now_playing', listController.nowPlayingShows);
|
||||||
|
router.get('/v2/show/popular', listController.popularShows);
|
||||||
|
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/movie/info.js'));
|
||||||
|
router.get('/v2/show/:id', require('./controllers/show/info.js'));
|
||||||
|
router.get('/v2/person/:id', require('./controllers/person/info.js'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plex
|
* Plex
|
||||||
*/
|
*/
|
||||||
|
router.get('/v2/plex/search', require('./controllers/plex/search'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List
|
||||||
|
*/
|
||||||
router.get('/v1/plex/search', require('./controllers/plex/searchMedia.js'));
|
router.get('/v1/plex/search', require('./controllers/plex/searchMedia.js'));
|
||||||
router.get('/v1/plex/playing', require('./controllers/plex/plexPlaying.js'));
|
router.get('/v1/plex/playing', require('./controllers/plex/plexPlaying.js'));
|
||||||
router.get('/v1/plex/request', require('./controllers/plex/searchRequest.js'));
|
router.get('/v1/plex/request', require('./controllers/plex/searchRequest.js'));
|
||||||
router.get('/v1/plex/request/:mediaId', require('./controllers/plex/readRequest.js'));
|
router.get('/v1/plex/request/:mediaId', require('./controllers/plex/readRequest.js'));
|
||||||
router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitRequest.js'));
|
router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitRequest.js'));
|
||||||
router.get('/v1/plex/hook', require('./controllers/plex/hookDump.js'));
|
router.post('/v1/plex/hook', require('./controllers/plex/hookDump.js'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests
|
* Requests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
router.get('/v2/request', require('./controllers/request/fetchAllRequests.js'));
|
||||||
|
router.get('/v2/request/:id', require('./controllers/request/getRequest.js'));
|
||||||
|
router.post('/v2/request', require('./controllers/request/requestTmdbId.js'));
|
||||||
router.get('/v1/plex/requests/all', require('./controllers/plex/fetchRequested.js'));
|
router.get('/v1/plex/requests/all', require('./controllers/plex/fetchRequested.js'));
|
||||||
router.put('/v1/plex/request/:requestId', mustBeAuthenticated, require('./controllers/plex/updateRequested.js'));
|
router.put('/v1/plex/request/:requestId', mustBeAuthenticated, require('./controllers/plex/updateRequested.js'));
|
||||||
|
|
||||||
@@ -89,13 +116,6 @@ router.put('/v1/plex/request/:requestId', mustBeAuthenticated, require('./contro
|
|||||||
router.get('/v1/pirate/search', mustBeAuthenticated, require('./controllers/pirate/searchTheBay.js'));
|
router.get('/v1/pirate/search', mustBeAuthenticated, require('./controllers/pirate/searchTheBay.js'));
|
||||||
router.post('/v1/pirate/add', mustBeAuthenticated, require('./controllers/pirate/addMagnet.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
|
* git
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
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'));
|
||||||
|
|
||||||
|
// there should be a translate function from query params to
|
||||||
|
// tmdb list that is valid. Should it be a helper function or does it
|
||||||
|
// belong in tmdb.
|
||||||
|
// + could also have default value that are sent to the client.
|
||||||
|
// * have the same class create a getListNames() and a fetchList()
|
||||||
|
// * dicover list might be overkill_https://tinyurl.com/y7f8ragw
|
||||||
|
// + trending! https://tinyurl.com/ydywrqox
|
||||||
|
// by all, mediatype, or person. Can also define time periode to
|
||||||
|
// get more trending view of what people are checking out.
|
||||||
|
// + newly created (tv/latest).
|
||||||
|
// + movie/latest
|
||||||
|
//
|
||||||
|
function handleError(error, res) {
|
||||||
|
const { status, message } = error;
|
||||||
|
|
||||||
|
if (status && message) {
|
||||||
|
res.status(status).send({ success: false, message })
|
||||||
|
} else {
|
||||||
|
console.log('caught list controller error', error)
|
||||||
|
res.status(500).send({ message: 'An unexpected error occured while requesting list'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleListResponse(response, res) {
|
||||||
|
return res.send(response)
|
||||||
|
.catch(error => handleError(error, res))
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchTmdbList(req, res, listname, type) {
|
||||||
|
const { page } = req.query;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
popularMovies,
|
||||||
|
topRatedMovies,
|
||||||
|
upcomingMovies,
|
||||||
|
nowPlayingShows,
|
||||||
|
popularShows,
|
||||||
|
topRatedShows
|
||||||
|
}
|
||||||
@@ -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 movieCreditsController = (req, res) => {
|
||||||
|
const movieId = req.params.id;
|
||||||
|
|
||||||
|
tmdb.movieCredits(movieId)
|
||||||
|
.then(credits => res.send(credits.createJsonResponse()))
|
||||||
|
.catch(error => {
|
||||||
|
const { status, message } = error;
|
||||||
|
|
||||||
|
if (status && message) {
|
||||||
|
res.status(status).send({ success: false, message })
|
||||||
|
} else {
|
||||||
|
// TODO log unhandled errors
|
||||||
|
console.log('caugth movie credits controller error', error)
|
||||||
|
res.status(500).send({ message: 'An unexpected error occured while requesting movie credits' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = movieCreditsController;
|
||||||
@@ -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(status).send({ success: false, 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;
|
||||||
@@ -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 movieReleaseDatesController = (req, res) => {
|
||||||
|
const movieId = req.params.id;
|
||||||
|
|
||||||
|
tmdb.movieReleaseDates(movieId)
|
||||||
|
.then(releaseDates => res.send(releaseDates.createJsonResponse()))
|
||||||
|
.catch(error => {
|
||||||
|
const { status, message } = error;
|
||||||
|
|
||||||
|
if (status && message) {
|
||||||
|
res.status(status).send({ success: false, message })
|
||||||
|
} else {
|
||||||
|
// TODO log unhandled errors : here our at tmdbReleaseError ?
|
||||||
|
console.log('caugth release dates controller error', error)
|
||||||
|
res.status(500).send({ message: 'An unexpected error occured while requesting movie credits' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = movieReleaseDatesController;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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 person
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function personInfoController(req, res) {
|
||||||
|
const personId = req.params.id;
|
||||||
|
|
||||||
|
|
||||||
|
tmdb.personInfo(personId)
|
||||||
|
.then(person => res.send(person.createJsonResponse()))
|
||||||
|
.catch(error => {
|
||||||
|
res.status(404).send({ success: false, message: error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = personInfoController;
|
||||||
@@ -17,8 +17,8 @@ function addMagnet(req, res) {
|
|||||||
.then((result) => {
|
.then((result) => {
|
||||||
res.send(result);
|
res.send(result);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ function updateRequested(req, res) {
|
|||||||
.then((result) => {
|
.then((result) => {
|
||||||
res.send({ success: true, results: result });
|
res.send({ success: true, results: result });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(401).send({ success: false, error: error.message });
|
res.status(401).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function fetchRequestedController(req, res) {
|
|||||||
res.send({ success: true, results: requestedItems, total_results: requestedItems.length });
|
res.send({ success: true, results: requestedItems, total_results: requestedItems.length });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(401).send({ success: false, error: error.message });
|
res.status(401).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
const PlexRepository = require('src/plex/plexRepository');
|
const PlexRepository = require('src/plex/plexRepository');
|
||||||
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
|
||||||
const plexRepository = new PlexRepository();
|
const plexRepository = new PlexRepository(configuration.get('plex', 'ip'));
|
||||||
|
|
||||||
function playingController(req, res) {
|
function playingController(req, res) {
|
||||||
plexRepository.nowPlaying()
|
plexRepository.nowPlaying()
|
||||||
.then((movies) => {
|
.then(movies => {
|
||||||
res.send(movies);
|
res.send(movies);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ function readRequestController(req, res) {
|
|||||||
const mediaId = req.params.mediaId;
|
const mediaId = req.params.mediaId;
|
||||||
const { type } = req.query;
|
const { type } = req.query;
|
||||||
requestRepository.lookup(mediaId, type)
|
requestRepository.lookup(mediaId, type)
|
||||||
.then((movies) => {
|
.then(movies => {
|
||||||
res.send(movies);
|
res.send(movies);
|
||||||
}).catch((error) => {
|
}).catch(error => {
|
||||||
res.status(404).send({ success: false, error: error.message });
|
res.status(404).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
const Plex = require('src/plex/plex');
|
||||||
|
const plex = new Plex(configuration.get('plex', 'ip'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Search plex for movies, shows and episodes by query
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function searchPlexController(req, res) {
|
||||||
|
const { query, type } = req.query;
|
||||||
|
plex.search(query, type)
|
||||||
|
.then(movies => {
|
||||||
|
if (movies.length > 0) {
|
||||||
|
res.send(movies);
|
||||||
|
} else {
|
||||||
|
res.status(404).send({ success: false, message: 'Search query did not give any results from plex.'})
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
res.status(500).send({ success: false, message: error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = searchPlexController;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const PlexRepository = require('src/plex/plexRepository');
|
const PlexRepository = require('src/plex/plexRepository');
|
||||||
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
|
||||||
const plexRepository = new PlexRepository();
|
const plexRepository = new PlexRepository(configuration.get('plex', 'ip'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller: Search for media and check existence
|
* Controller: Search for media and check existence
|
||||||
@@ -13,15 +14,15 @@ function searchMediaController(req, res) {
|
|||||||
const { query } = req.query;
|
const { query } = req.query;
|
||||||
|
|
||||||
plexRepository.search(query)
|
plexRepository.search(query)
|
||||||
.then((media) => {
|
.then(media => {
|
||||||
if (media !== undefined || media.length > 0) {
|
if (media !== undefined || media.length > 0) {
|
||||||
res.send(media);
|
res.send(media);
|
||||||
} else {
|
} else {
|
||||||
res.status(404).send({ success: false, error: 'Search query did not return any results.' });
|
res.status(404).send({ success: false, message: 'Search query did not return any results.' });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ function searchRequestController(req, res) {
|
|||||||
.then((searchResult) => {
|
.then((searchResult) => {
|
||||||
res.send(searchResult);
|
res.send(searchResult);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(500).send({ success: false, error: error });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
const RequestRepository = require('src/plex/requestRepository.js');
|
const configuration = require('src/config/configuration').getInstance()
|
||||||
|
const RequestRepository = require('src/request/request');
|
||||||
|
const Cache = require('src/tmdb/cache')
|
||||||
|
const TMDB = require('src/tmdb/tmdb')
|
||||||
|
|
||||||
const requestRepository = new RequestRepository();
|
const cache = new Cache()
|
||||||
|
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'))
|
||||||
|
const request = new RequestRepository()
|
||||||
|
|
||||||
|
const tmdbMovieInfo = (id) => {
|
||||||
|
return tmdb.movieInfo(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmdbShowInfo = (id) => {
|
||||||
|
return tmdb.showInfo(id)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller: POST a media id to be donwloaded
|
* Controller: POST a media id to be donwloaded
|
||||||
@@ -8,22 +21,31 @@ const requestRepository = new RequestRepository();
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @returns {Callback}
|
* @returns {Callback}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function submitRequestController(req, res) {
|
function submitRequestController(req, res) {
|
||||||
// This is the id that is the param of the url
|
// This is the id that is the param of the url
|
||||||
const id = req.params.mediaId;
|
const id = req.params.mediaId;
|
||||||
const type = req.query.type;
|
const type = req.query.type ? req.query.type.toLowerCase() : undefined
|
||||||
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||||
const user_agent = req.headers['user-agent'];
|
const user_agent = req.headers['user-agent'];
|
||||||
const user = req.loggedInUser;
|
const user = req.loggedInUser;
|
||||||
|
let mediaFunction = undefined
|
||||||
|
|
||||||
requestRepository.sendRequest(id, type, ip, user_agent, user)
|
if (type === 'movie') {
|
||||||
.then(() => {
|
console.log('movie')
|
||||||
res.send({ success: true, message: 'Media item sucessfully requested!' });
|
mediaFunction = tmdbMovieInfo
|
||||||
})
|
} else if (type === 'show') {
|
||||||
.catch((error) => {
|
console.log('show')
|
||||||
res.status(500).send({ success: false, error: error.message });
|
mediaFunction = tmdbShowInfo
|
||||||
});
|
} else {
|
||||||
|
res.status(422).send({ success: false, message: 'Incorrect type. Allowed types: "movie" or "show"'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaFunction === undefined) { res.status(200); return }
|
||||||
|
|
||||||
|
mediaFunction(id)
|
||||||
|
.then(tmdbMedia => request.requestFromTmdb(tmdbMedia, ip, user_agent, user))
|
||||||
|
.then(() => res.send({ success: true, message: 'Media item successfully requested' }))
|
||||||
|
.catch(err => res.status(500).send({ success: false, message: err.message }))
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = submitRequestController;
|
module.exports = submitRequestController;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function updateRequested(req, res) {
|
|||||||
res.send({ success: true });
|
res.send({ success: true });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(401).send({ success: false, error: error.message });
|
res.status(401).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
const RequestRepository = require('src/request/request');
|
||||||
|
const request = new RequestRepository();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Fetch all requested items
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function fetchAllRequests(req, res) {
|
||||||
|
let { page, filter, sort, query } = req.query;
|
||||||
|
let sort_by = sort;
|
||||||
|
let sort_direction = undefined;
|
||||||
|
|
||||||
|
if (sort !== undefined && sort.includes(':')) {
|
||||||
|
[sort_by, sort_direction] = sort.split(':')
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => request.fetchAll(page, sort_by, sort_direction, filter, query))
|
||||||
|
.then(result => res.send(result))
|
||||||
|
.catch(error => {
|
||||||
|
res.status(404).send({ success: false, message: error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchAllRequests;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const RequestRepository = require('src/request/request');
|
||||||
|
const request = new RequestRepository();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Get requested item by tmdb id and type
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function fetchAllRequests(req, res) {
|
||||||
|
const id = req.params.id;
|
||||||
|
const { type } = req.query;
|
||||||
|
|
||||||
|
request.getRequestByIdAndType(id, type)
|
||||||
|
.then(result => res.send(result))
|
||||||
|
.catch(error => {
|
||||||
|
res.status(404).send({ success: false, message: error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchAllRequests;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
const configuration = require('src/config/configuration').getInstance();
|
||||||
|
const Cache = require('src/tmdb/cache');
|
||||||
|
const TMDB = require('src/tmdb/tmdb');
|
||||||
|
const RequestRepository = require('src/request/request');
|
||||||
|
const cache = new Cache();
|
||||||
|
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
||||||
|
const request = new RequestRepository();
|
||||||
|
|
||||||
|
const tmdbMovieInfo = (id) => {
|
||||||
|
return tmdb.movieInfo(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmdbShowInfo = (id) => {
|
||||||
|
return tmdb.showInfo(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Request by id with type param
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function requestTmdbIdController(req, res) {
|
||||||
|
const { id, type } = req.body
|
||||||
|
|
||||||
|
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||||
|
const user_agent = req.headers['user-agent'];
|
||||||
|
const user = req.loggedInUser;
|
||||||
|
|
||||||
|
let mediaFunction = undefined
|
||||||
|
|
||||||
|
if (id === undefined || type === undefined) {
|
||||||
|
res.status(422).send({ success: false, message: "'Missing parameteres: 'id' and/or 'type'"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'movie') {
|
||||||
|
mediaFunction = tmdbMovieInfo
|
||||||
|
} else if (type === 'show') {
|
||||||
|
mediaFunction = tmdbShowInfo
|
||||||
|
} else {
|
||||||
|
res.status(422).send({ success: false, message: 'Incorrect type. Allowed types: "movie" or "show"'})
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaFunction(id)
|
||||||
|
// .catch((error) => { console.error(error); res.status(404).send({ success: false, error: 'Id not found' }) })
|
||||||
|
.then(tmdbMedia => request.requestFromTmdb(tmdbMedia, ip, user_agent, user))
|
||||||
|
.then(() => res.send({success: true, message: 'Request has been submitted.'}))
|
||||||
|
.catch(error => {
|
||||||
|
res.send({ success: false, message: error.message });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = requestTmdbIdController;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Search for movies by query and pagey
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function movieSearchController(req, res) {
|
||||||
|
const user = req.loggedInUser;
|
||||||
|
const { query, page } = req.query;
|
||||||
|
|
||||||
|
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(status).send({ success: false, 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;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function multiSearchController(req, res) {
|
||||||
|
const user = req.loggedInUser;
|
||||||
|
const { query, page } = req.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(status).send({ success: false, 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}` })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = multiSearchController;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Search for person by query and pagey
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function personSearchController(req, res) {
|
||||||
|
const user = req.loggedInUser;
|
||||||
|
const { query, page } = req.query;
|
||||||
|
|
||||||
|
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(status).send({ success: false, 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;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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 cache = new Cache();
|
||||||
|
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
||||||
|
const searchHistory = new SearchHistory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller: Search for shows by query and pagey
|
||||||
|
* @param {Request} req http request variable
|
||||||
|
* @param {Response} res
|
||||||
|
* @returns {Callback}
|
||||||
|
*/
|
||||||
|
function showSearchController(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.showSearch(query, page))
|
||||||
|
.then((shows) => {
|
||||||
|
res.send(shows);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send({ success: false, message: error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = showSearchController;
|
||||||
@@ -10,7 +10,7 @@ function readStraysController(req, res) {
|
|||||||
res.send(strays);
|
res.send(strays);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ function strayByIdController(req, res) {
|
|||||||
res.send(stray);
|
res.send(stray);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ function verifyStrayController(req, res) {
|
|||||||
res.send({ success: true, message: 'Episode verified' });
|
res.send({ success: true, message: 'Episode verified' });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
res.status(500).send({ success: false, error: error.message });
|
res.status(500).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(status).send({ success: false, 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;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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(status).send({ success: false, 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;
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -13,11 +13,11 @@ function historyController(req, res) {
|
|||||||
const username = user === undefined ? undefined : user.username;
|
const username = user === undefined ? undefined : user.username;
|
||||||
|
|
||||||
searchHistory.read(username)
|
searchHistory.read(username)
|
||||||
.then((searchQueries) => {
|
.then(searchQueries => {
|
||||||
res.send({ success: true, searchQueries });
|
res.send({ success: true, searchQueries });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(404).send({ success: false, error: error });
|
res.status(404).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ function loginController(req, res) {
|
|||||||
|
|
||||||
userSecurity.login(user, password)
|
userSecurity.login(user, password)
|
||||||
.then(() => userRepository.checkAdmin(user))
|
.then(() => userRepository.checkAdmin(user))
|
||||||
.then((checkAdmin) => {
|
.then(checkAdmin => {
|
||||||
const token = new Token(user).toString(secret);
|
const isAdmin = checkAdmin === 1 ? true : false;
|
||||||
const admin_state = checkAdmin === 1 ? true : false;
|
const token = new Token(user, isAdmin).toString(secret);
|
||||||
res.send({ success: true, token, admin: admin_state });
|
res.send({ success: true, token });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(401).send({ success: false, error: error.message });
|
res.status(401).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ function registerController(req, res) {
|
|||||||
|
|
||||||
userSecurity.createNewUser(user, password)
|
userSecurity.createNewUser(user, password)
|
||||||
.then(() => userRepository.checkAdmin(user))
|
.then(() => userRepository.checkAdmin(user))
|
||||||
.then((checkAdmin) => {
|
.then(checkAdmin => {
|
||||||
const token = new Token(user).toString(secret);
|
const isAdmin = checkAdmin === 1 ? true : false;
|
||||||
const admin_state = checkAdmin === 1 ? true : false;
|
const token = new Token(user, isAdmin).toString(secret);
|
||||||
res.send({
|
res.send({
|
||||||
success: true, message: 'Welcome to Seasoned!', token, admin: admin_state,
|
success: true, message: 'Welcome to Seasoned!', token
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
res.status(401).send({ success: false, error: error.message });
|
res.status(401).send({ success: false, message: error.message });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ function requestsController(req, res) {
|
|||||||
const user = req.loggedInUser;
|
const user = req.loggedInUser;
|
||||||
|
|
||||||
requestRepository.userRequests(user)
|
requestRepository.userRequests(user)
|
||||||
.then((requests) => {
|
.then(requests => {
|
||||||
res.send({ success: true, results: requests, total_results: requests.length });
|
res.send({ success: true, results: requests, total_results: requests.length });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.log(error)
|
res.status(500).send({ success: false, message: error.message });
|
||||||
res.status(500).send({ success: false, error: error });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ const Token = require('src/user/token');
|
|||||||
// curl -i -H "Authorization:[token]" localhost:31459/api/v1/user/history
|
// curl -i -H "Authorization:[token]" localhost:31459/api/v1/user/history
|
||||||
|
|
||||||
const tokenToUser = (req, res, next) => {
|
const tokenToUser = (req, res, next) => {
|
||||||
const rawToken = req.headers.authorization;
|
const rawToken = req.headers.authorization;
|
||||||
if (rawToken) {
|
if (rawToken) {
|
||||||
try {
|
try {
|
||||||
const token = Token.fromString(rawToken, secret);
|
const token = Token.fromString(rawToken, secret);
|
||||||
req.loggedInUser = token.user;
|
req.loggedInUser = token.user;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
req.loggedInUser = undefined;
|
req.loggedInUser = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = tokenToUser;
|
module.exports = tokenToUser;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
[{"adult":false,"backdrop_path":"/mVr0UiqyltcfqxbAUcLl9zWL8ah.jpg","belongs_to_collection":{"id":422837,"name":"Blade Runner Collection","poster_path":"/cWESb1o9lW2i2Z3Xllv9u40aNIk.jpg","backdrop_path":"/bSHZIvLoPBWyGLeiAudN1mXdvQX.jpg"},"budget":150000000,"genres":[{"id":9648,"name":"Mystery"},{"id":878,"name":"Science Fiction"},{"id":53,"name":"Thriller"}],"homepage":"http://bladerunnermovie.com/","id":335984,"imdb_id":"tt1856101","original_language":"en","original_title":"Blade Runner 2049","overview":"Thirty years after the events of the first film, a new blade runner, LAPD Officer K, unearths a long-buried secret that has the potential to plunge what's left of society into chaos. K's discovery leads him on a quest to find Rick Deckard, a former LAPD blade runner who has been missing for 30 years.","popularity":30.03,"poster_path":"/gajva2L0rPYkEWjzgFlBXCAVBE5.jpg","production_companies":[{"id":79529,"logo_path":"/gVN3k8emmKy4iV4KREWcCtxusZK.png","name":"Torridon Films","origin_country":"US"},{"id":101829,"logo_path":"/8IOjCvgjq0zTrtP91cWD3kL2jMK.png","name":"16:14 Entertainment","origin_country":"US"},{"id":1645,"logo_path":"/6Ry6uNBaa0IbbSs1XYIgX5DkA9r.png","name":"Scott Free Productions","origin_country":""},{"id":5,"logo_path":"/71BqEFAF4V3qjjMPCpLuyJFB9A.png","name":"Columbia Pictures","origin_country":"US"},{"id":1088,"logo_path":"/9WOE5AQUXbOtLU6GTwfjS8OMF0v.png","name":"Alcon Entertainment","origin_country":"US"},{"id":78028,"logo_path":"/sTFcDFfJaSVT3sv3DoaZDE4SlGB.png","name":"Thunderbird Entertainment","origin_country":"CA"},{"id":174,"logo_path":"/ky0xOc5OrhzkZ1N6KyUxacfQsCk.png","name":"Warner Bros. Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"CA","name":"Canada"},{"iso_3166_1":"US","name":"United States of America"},{"iso_3166_1":"HU","name":"Hungary"},{"iso_3166_1":"GB","name":"United Kingdom"}],"release_date":"2017-10-04","revenue":259239658,"runtime":163,"spoken_languages":[{"iso_639_1":"en","name":"English"},{"iso_639_1":"fi","name":"suomi"}],"status":"Released","tagline":"There's still a page left.","title":"Blade Runner 2049","video":false,"vote_average":7.3,"vote_count":5478}]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"page":1,
|
||||||
|
"results":[],
|
||||||
|
"total_results":0,
|
||||||
|
"total_pages":1
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
+1
-227
File diff suppressed because one or more lines are too long
+1
-227
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
|||||||
|
const tmdbMock = () => ({
|
||||||
|
error: null,
|
||||||
|
response: null,
|
||||||
|
searchMovie(query, callback) {
|
||||||
|
callback(this.error, this.response);
|
||||||
|
},
|
||||||
|
movieInfo(query, callback) {
|
||||||
|
callback(this.error, this.response);
|
||||||
|
},
|
||||||
|
miscPopularMovies(callback) {
|
||||||
|
console.log('miscPopMovies callback', callback)
|
||||||
|
callback(this.error, this.response);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = tmdbMock;
|
||||||
@@ -5,8 +5,8 @@ xdescribe('As a developer I want the server to start', () => {
|
|||||||
beforeEach(() =>
|
beforeEach(() =>
|
||||||
this.server = require('src/webserver/server'));
|
this.server = require('src/webserver/server'));
|
||||||
|
|
||||||
it('should listen on port 31459', (done) => {
|
it('should listen on port 31400', (done) => {
|
||||||
net.createConnection(31459, done);
|
net.createConnection(31400, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() =>
|
afterEach(() =>
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ describe('As a user I want error when registering existing username', () => {
|
|||||||
.post('/api/v1/user')
|
.post('/api/v1/user')
|
||||||
.send({ username: 'test_user', password: 'password' })
|
.send({ username: 'test_user', password: 'password' })
|
||||||
.expect(401)
|
.expect(401)
|
||||||
.then(response => assert.equal(response.text, '{"success":false,"error":"That username is already registered"}'))
|
.then(response => assert.equal(response.text, '{"success":false,"message":"That username is already registered"}'))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ const popularMoviesSuccess = require('test/fixtures/popular-movies-success-respo
|
|||||||
|
|
||||||
describe('As a user I want to get popular movies', () => {
|
describe('As a user I want to get popular movies', () => {
|
||||||
before(() => resetDatabase());
|
before(() => resetDatabase());
|
||||||
before(() => createCacheEntry('p:movie:1', popularMoviesSuccess));
|
before(() => createCacheEntry('pm:1', popularMoviesSuccess));
|
||||||
|
|
||||||
it('should return 200 with the information', () =>
|
it('should return 200 with the information', () =>
|
||||||
request(app)
|
request(app)
|
||||||
.get('/api/v1/tmdb/list/popular')
|
.get('/api/v2/movie/popular')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then(response => assert.equal(response.body.results.length, 20))
|
.then(response => assert.equal(response.body.results.length, 20))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ const popularShowsSuccess = require('test/fixtures/popular-show-success-response
|
|||||||
|
|
||||||
describe('As a user I want to get popular shows', () => {
|
describe('As a user I want to get popular shows', () => {
|
||||||
before(() => resetDatabase());
|
before(() => resetDatabase());
|
||||||
before(() => createCacheEntry('p:show:1', popularShowsSuccess));
|
before(() => createCacheEntry('pt:1', popularShowsSuccess));
|
||||||
|
|
||||||
it('should return 200 with the information', () =>
|
it('should return 200 with the information', () =>
|
||||||
request(app)
|
request(app)
|
||||||
.get('/api/v1/tmdb/list/popular?type=show')
|
.get('/api/v2/show/popular')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then(response => assert.equal(response.body.results.length, 20))
|
.then(response => assert.equal(response.body.results.length, 20))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ const app = require('src/webserver/app');
|
|||||||
const request = require('supertest-as-promised');
|
const request = require('supertest-as-promised');
|
||||||
const createUser = require('test/helpers/createUser');
|
const createUser = require('test/helpers/createUser');
|
||||||
const createToken = require('test/helpers/createToken');
|
const createToken = require('test/helpers/createToken');
|
||||||
const infoMovieSuccess = require('test/fixtures/arrival-info-success-response.json');
|
const infoMovieSuccess = require('test/fixtures/blade_runner_2049-info-success-response.json');
|
||||||
|
|
||||||
describe('As a user I want to request a movie', () => {
|
describe('As a user I want to request a movie', () => {
|
||||||
before(() => {
|
before(async () => {
|
||||||
return resetDatabase()
|
await resetDatabase()
|
||||||
.then(() => createUser('test_user', 'test@gmail.com', 'password'));
|
await createUser('test_user', 'test@gmail.com', 'password')
|
||||||
})
|
})
|
||||||
before(() => createCacheEntry('i:movie:329865', infoMovieSuccess));
|
before(() => createCacheEntry('mi:335984:false', infoMovieSuccess));
|
||||||
|
|
||||||
it('should return 200 when item is requested', () =>
|
it('should return 200 when item is requested', () =>
|
||||||
request(app)
|
request(app)
|
||||||
.post('/api/v1/plex/request/329865')
|
.post('/api/v2/request')
|
||||||
.set('Authorization', createToken('test_user', 'secret'))
|
.set('authorization', createToken('test_user', 'secret'))
|
||||||
|
.send({ id: 335984, type: 'movie' })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ const createCacheEntry = require('test/helpers/createCacheEntry');
|
|||||||
const resetDatabase = require('test/helpers/resetDatabase');
|
const resetDatabase = require('test/helpers/resetDatabase');
|
||||||
const request = require('supertest-as-promised');
|
const request = require('supertest-as-promised');
|
||||||
const app = require('src/webserver/app');
|
const app = require('src/webserver/app');
|
||||||
const interstellarQuerySuccess = require('test/fixtures/interstellar-query-success-response.json');
|
const interstellarQuerySuccess = require('test/fixtures/interstellar-query-movie-success-response.json');
|
||||||
|
|
||||||
describe('As an anonymous user I want to search for a movie', () => {
|
describe('As an anonymous user I want to search for a movie', () => {
|
||||||
before(() => resetDatabase());
|
before(() => resetDatabase());
|
||||||
before(() => createCacheEntry('se:1:multi:interstellar', interstellarQuerySuccess));
|
before(() => createCacheEntry('mos:1:interstellar', interstellarQuerySuccess));
|
||||||
|
|
||||||
it('should return 200 with the search results even if user is not logged in', () =>
|
it('should return 200 with the search results even if user is not logged in', () =>
|
||||||
request(app)
|
request(app)
|
||||||
.get('/api/v1/tmdb/search?query=interstellar&page=1')
|
.get('/api/v2/search/movie?query=interstellar&page=1')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
// const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie');
|
||||||
|
const { Movie } = require('src/tmdb/types');
|
||||||
|
const bladeRunnerQuerySuccess = require('test/fixtures/blade_runner_2049-info-success-response.json')
|
||||||
|
|
||||||
|
describe('Convert tmdb movieInfo to movie', () => {
|
||||||
|
beforeEach(() => [this.bladeRunnerTmdbMovie] = bladeRunnerQuerySuccess);
|
||||||
|
|
||||||
|
it('should translate the tmdb release date to movie year', () => {
|
||||||
|
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
|
||||||
|
assert.strictEqual(bladeRunner.year, 2017);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate the tmdb release date to instance of Date', () => {
|
||||||
|
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
|
||||||
|
assert(bladeRunner.releaseDate instanceof Date);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate the tmdb title to title', () => {
|
||||||
|
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
|
||||||
|
assert.equal(bladeRunner.title, 'Blade Runner 2049');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate the tmdb vote_average to rating', () => {
|
||||||
|
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
|
||||||
|
assert.equal(bladeRunner.rating, 7.3);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
// const Movie = require('src/movie/movie');
|
||||||
|
const TMDB = require('src/tmdb/tmdb');
|
||||||
|
const Cache = require('src/tmdb/cache');
|
||||||
|
const SqliteDatabase = require('src/database/sqliteDatabase');
|
||||||
|
const tmdbMock = require('test/helpers/tmdbMock');
|
||||||
|
|
||||||
|
const emptyQuerySuccess = require('test/fixtures/empty-query-success-response.json');
|
||||||
|
const interstellarQuerySuccess = require('test/fixtures/arrival-info-success-response.json');
|
||||||
|
const popularMovieSuccessResponse = require('test/fixtures/popular-movies-success-response.json');
|
||||||
|
|
||||||
|
describe('TMDB', function test() {
|
||||||
|
beforeEach(() => {
|
||||||
|
this.mockMoviedb = tmdbMock();
|
||||||
|
this.database = new SqliteDatabase(':memory:');
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => this.database.setUp());
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('popular', () => {
|
||||||
|
it('should return the "Blade Runner 2049" year in the collection of popular movies', () => {
|
||||||
|
this.mockMoviedb.response = popularMovieSuccessResponse;
|
||||||
|
const cache = new Cache(this.database);
|
||||||
|
const tmdb = new TMDB(cache, 'bogus-pi-key', this.mockMoviedb);
|
||||||
|
return tmdb.popular()
|
||||||
|
.then(movies =>
|
||||||
|
assert.equal(movies[0].title, "Blade Runner 2049")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
+4259
-791
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user