1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
|
||||
development.json
|
||||
env
|
||||
shows.db
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
language: node_js
|
||||
node_js: '8.7.0'
|
||||
node_js: '11.9.0'
|
||||
git:
|
||||
submodules: true
|
||||
script:
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<img src="https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=master"
|
||||
alt="Travis CI">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage">
|
||||
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=coverage" alt="">
|
||||
<a href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=api/v2">
|
||||
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=api/v2" alt="">
|
||||
</a>
|
||||
<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="">
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
"redux-thunk": "^2.2.0",
|
||||
"urijs": "^1.18.12",
|
||||
"webfontloader": "^1.6.28",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-dev-server": "^2.4.5",
|
||||
"webpack": "^4.0.0",
|
||||
"webpack-dev-server": "^3.1.11",
|
||||
"webpack-merge": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -8,17 +8,13 @@
|
||||
"tmdb": {
|
||||
"apiKey": ""
|
||||
},
|
||||
"plex": {
|
||||
"ip": ""
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"mail": {
|
||||
"host": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"user_pi": "",
|
||||
"password_pi": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
"tmdb": {
|
||||
"apiKey": "bogus-api-key"
|
||||
},
|
||||
"plex": {
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
|
||||
@@ -7,37 +7,48 @@
|
||||
},
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "cross-env SEASONED_CONFIG=conf/development.json PROD=true NODE_PATH=. node src/webserver/server.js",
|
||||
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --recursive test",
|
||||
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --recursive test && nyc report --reporter=text-lcov | coveralls",
|
||||
"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 --require @babel/register --recursive test/unit test/system",
|
||||
"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/",
|
||||
"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": {
|
||||
"bcrypt-nodejs": "^0.0.3",
|
||||
"axios": "^0.18.0",
|
||||
"bcrypt": "^3.0.6",
|
||||
"body-parser": "~1.18.2",
|
||||
"cross-env": "~5.1.4",
|
||||
"express": "~4.16.0",
|
||||
"jsonwebtoken": "^8.2.0",
|
||||
"mongoose": "~5.2.12",
|
||||
"km-moviedb": "^0.2.12",
|
||||
"node-cache": "^4.1.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"python-shell": "^0.5.0",
|
||||
"raven": "^2.4.2",
|
||||
"request": "^2.87.0",
|
||||
"request-promise": "^4.2",
|
||||
"sqlite3": "^4.0.0"
|
||||
},
|
||||
"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-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^5.0.4",
|
||||
"mocha": "^6.2.0",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^11.6.0",
|
||||
"raven": "^2.4.2",
|
||||
"supertest": "^3.0.0",
|
||||
"supertest-as-promised": "^4.0.1"
|
||||
"supertest-as-promised": "^4.0.1",
|
||||
"typescript": "^3.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,14 @@ CREATE TABLE IF NOT EXISTS requests(
|
||||
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(
|
||||
id TEXT UNIQUE,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
DROP TABLE IF EXISTS user;
|
||||
DROP TABLE IF EXISTS search_history;
|
||||
DROP TABLE IF EXISTS requests;
|
||||
DROP TABLE IF EXISTS request;
|
||||
|
||||
@@ -25,7 +25,7 @@ class SqliteDatabase {
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async run(sql, parameters) {
|
||||
run(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.run(sql, parameters, (error, result) => {
|
||||
if (error)
|
||||
@@ -41,7 +41,7 @@ class SqliteDatabase {
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async all(sql, parameters) {
|
||||
all(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.all(sql, parameters, (err, rows) => {
|
||||
if (err) {
|
||||
@@ -58,7 +58,7 @@ class SqliteDatabase {
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async get(sql, parameters) {
|
||||
get(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.get(sql, parameters, (err, rows) => {
|
||||
if (err) {
|
||||
@@ -75,7 +75,7 @@ class SqliteDatabase {
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async execute(sql) {
|
||||
execute(sql) {
|
||||
return new Promise(resolve => {
|
||||
this.connection.exec(sql, (err, database) => {
|
||||
if (err) {
|
||||
|
||||
@@ -12,7 +12,7 @@ function getMagnetFromURL(url) {
|
||||
resolve(url)
|
||||
|
||||
http.get(options, (res) => {
|
||||
if (res.statusCode == 301) {
|
||||
if (res.statusCode == 301 || res.statusCode == 302) {
|
||||
resolve(res.headers.location)
|
||||
}
|
||||
});
|
||||
@@ -21,12 +21,12 @@ function getMagnetFromURL(url) {
|
||||
|
||||
async function find(searchterm, callback) {
|
||||
const options = {
|
||||
pythonPath: '/usr/bin/python3',
|
||||
// pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
|
||||
args: [searchterm, '-s', 'jackett', '-f', '--print'],
|
||||
};
|
||||
pythonPath: '../torrent_search/env/bin/python3',
|
||||
scriptPath: '../torrent_search',
|
||||
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
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ async function callPythonAddMagnet(url, callback) {
|
||||
getMagnetFromURL(url)
|
||||
.then((magnet) => {
|
||||
const options = {
|
||||
pythonPath: '/usr/bin/python',
|
||||
// pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
|
||||
args: [magnet],
|
||||
pythonPath: '../delugeClient/env/bin/python3',
|
||||
scriptPath: '../delugeClient',
|
||||
args: ['add', magnet]
|
||||
};
|
||||
|
||||
PythonShell.run('../app/magnet.py', options, callback);
|
||||
PythonShell.run('deluge_cli.py', options, callback);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
@@ -51,19 +51,16 @@ async function callPythonAddMagnet(url, callback) {
|
||||
async function SearchPiratebay(query) {
|
||||
return await new Promise((resolve, reject) => find(query, (err, results) => {
|
||||
if (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('THERE WAS A FUCKING ERROR!\n', err);
|
||||
reject(Error('There was a error when searching for torrents'));
|
||||
}
|
||||
if (results) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('result', results);
|
||||
resolve(JSON.parse(results, null, '\t'));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async function AddMagnet(magnet) {
|
||||
async function AddMagnet(magnet, name, tmdb_id) {
|
||||
return await new Promise((resolve, reject) => callPythonAddMagnet(magnet, (err, results) => {
|
||||
if (err) {
|
||||
/* eslint-disable no-console */
|
||||
|
||||
20
seasoned_api/src/plex/convertPlexToEpisode.js
Normal file
20
seasoned_api/src/plex/convertPlexToEpisode.js
Normal file
@@ -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;
|
||||
15
seasoned_api/src/plex/convertPlexToMovie.js
Normal file
15
seasoned_api/src/plex/convertPlexToMovie.js
Normal file
@@ -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;
|
||||
13
seasoned_api/src/plex/convertPlexToShow.js
Normal file
13
seasoned_api/src/plex/convertPlexToShow.js
Normal file
@@ -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;
|
||||
90
seasoned_api/src/plex/plex.js
Normal file
90
seasoned_api/src/plex/plex.js
Normal file
@@ -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');
|
||||
|
||||
class PlexRepository {
|
||||
constructor(plexIP) {
|
||||
this.plexIP = plexIP;
|
||||
}
|
||||
|
||||
inPlex(tmdbResult) {
|
||||
return Promise.resolve()
|
||||
.then(() => this.search(tmdbResult.title))
|
||||
@@ -15,9 +19,10 @@ class PlexRepository {
|
||||
}
|
||||
|
||||
search(query) {
|
||||
console.log('searching:', query)
|
||||
const queryUri = encodeURIComponent(query)
|
||||
const uri = encodeURI(`http://${this.plexIP}:32400/search?query=${queryUri}`)
|
||||
const options = {
|
||||
uri: `http://10.0.0.44:32400/search?query=${query}`,
|
||||
uri: uri,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
@@ -26,6 +31,7 @@ class PlexRepository {
|
||||
|
||||
return rp(options)
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
throw new Error('Unable to search plex.')
|
||||
})
|
||||
.then(result => this.mapResults(result))
|
||||
@@ -39,6 +45,7 @@ class PlexRepository {
|
||||
tmdb.matchedInPlex = false
|
||||
}
|
||||
else {
|
||||
// console.log('plex and tmdb:', plexResult, '\n', tmdb)
|
||||
plexResult.results.map((plexItem) => {
|
||||
if (tmdb.title === plexItem.title && tmdb.year === plexItem.year)
|
||||
tmdb.matchedInPlex = true;
|
||||
@@ -52,7 +59,6 @@ class PlexRepository {
|
||||
mapResults(response) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
console.log('plexResponse:', response)
|
||||
if (!response.MediaContainer.hasOwnProperty('Metadata')) return [[], 0];
|
||||
|
||||
const mappedResults = response.MediaContainer.Metadata.filter((element) => {
|
||||
@@ -65,7 +71,7 @@ class PlexRepository {
|
||||
|
||||
nowPlaying() {
|
||||
const options = {
|
||||
uri: 'http://10.0.0.44:32400/status/sessions',
|
||||
uri: `http://${this.plexIP}:32400/status/sessions`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ const configuration = require('src/config/configuration').getInstance();
|
||||
const TMDB = require('src/tmdb/tmdb');
|
||||
const establishedDatabase = require('src/database/database');
|
||||
|
||||
const plexRepository = new PlexRepository();
|
||||
const plexRepository = new PlexRepository(configuration.get('plex', 'ip'));
|
||||
const cache = new Cache();
|
||||
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
||||
|
||||
@@ -86,7 +86,11 @@ class RequestRepository {
|
||||
}
|
||||
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) {
|
||||
|
||||
16
seasoned_api/src/plex/types/episode.js
Normal file
16
seasoned_api/src/plex/types/episode.js
Normal file
@@ -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;
|
||||
12
seasoned_api/src/plex/types/movie.js
Normal file
12
seasoned_api/src/plex/types/movie.js
Normal file
@@ -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;
|
||||
12
seasoned_api/src/plex/types/show.js
Normal file
12
seasoned_api/src/plex/types/show.js
Normal file
@@ -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;
|
||||
39
seasoned_api/src/plex/updateRequestsInPlex.js
Normal file
39
seasoned_api/src/plex/updateRequestsInPlex.js
Normal file
@@ -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
|
||||
169
seasoned_api/src/request/request.js
Normal file
169
seasoned_api/src/request/request.js
Normal file
@@ -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;
|
||||
34
seasoned_api/src/request/utils.js
Normal file
34
seasoned_api/src/request/utils.js
Normal file
@@ -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 }
|
||||
3
seasoned_api/src/tmdb/.babelrc
Normal file
3
seasoned_api/src/tmdb/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"]
|
||||
}
|
||||
@@ -18,12 +18,12 @@ class Cache {
|
||||
* @returns {Object}
|
||||
*/
|
||||
get(key) {
|
||||
return Promise.resolve()
|
||||
.then(() => this.database.get(this.queries.read, [key]))
|
||||
.then((row) => {
|
||||
assert(row, 'Could not find cache enrty with that key.');
|
||||
return JSON.parse(row.value);
|
||||
});
|
||||
return Promise.resolve()
|
||||
.then(() => this.database.get(this.queries.read, [key]))
|
||||
.then(row => {
|
||||
assert(row, 'Could not find cache entry with that key.');
|
||||
return JSON.parse(row.value);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
@@ -1,114 +1,241 @@
|
||||
const moviedb = require('km-moviedb');
|
||||
const convertTmdbToSeasoned = require('src/tmdb/convertTmdbToSeasoned');
|
||||
|
||||
const TMDB_METHODS = {
|
||||
upcoming: { movie: 'miscUpcomingMovies' },
|
||||
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' }
|
||||
};
|
||||
const { Movie, Show, Person, Credits, ReleaseDates } = require('src/tmdb/types');
|
||||
// const { tmdbInfo } = require('src/tmdb/types')
|
||||
|
||||
class TMDB {
|
||||
constructor(cache, apiKey, tmdbLibrary) {
|
||||
this.cache = cache;
|
||||
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
|
||||
this.cacheTags = {
|
||||
search: 'se',
|
||||
info: 'i',
|
||||
upcoming: 'u',
|
||||
discover: 'd',
|
||||
popular: 'p',
|
||||
nowplaying: 'n',
|
||||
similar: 'si',
|
||||
};
|
||||
}
|
||||
constructor(cache, apiKey, tmdbLibrary) {
|
||||
this.cache = cache;
|
||||
this.tmdbLibrary = tmdbLibrary || moviedb(apiKey);
|
||||
this.cacheTags = {
|
||||
multiSearch: 'mus',
|
||||
movieSearch: 'mos',
|
||||
showSearch: 'ss',
|
||||
personSearch: 'ps',
|
||||
movieInfo: 'mi',
|
||||
movieCredits: 'mc',
|
||||
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.
|
||||
* @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
|
||||
*/
|
||||
lookup(identifier, type = 'movie') {
|
||||
const query = { id: identifier };
|
||||
const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(TMDB_METHODS['info'][type], query))
|
||||
.catch(() => { throw new Error('Could not find a movie with that id.'); })
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then((response) => {
|
||||
try {
|
||||
return convertTmdbToSeasoned(response, type);
|
||||
} catch (parseError) {
|
||||
console.error(parseError);
|
||||
throw new Error('Could not parse movie.');
|
||||
}
|
||||
});
|
||||
}
|
||||
movieInfo(identifier) {
|
||||
const query = { id: identifier };
|
||||
const cacheKey = `${this.cacheTags.movieInfo}:${identifier}`;
|
||||
|
||||
/**
|
||||
* Retrive search results from TMDB.
|
||||
return this.cache.get(cacheKey)
|
||||
.catch(() => this.tmdb('movieInfo', query))
|
||||
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie info'))
|
||||
.then(movie => this.cache.set(cacheKey, movie, 1))
|
||||
.then(movie => Movie.convertFromTmdbResponse(movie))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {Number} page representing pagination of results
|
||||
* @param {String} type filter results by type (default multi)
|
||||
* @returns {Promise} dict with query results, current page and total_pages
|
||||
*/
|
||||
search(text, page = 1, type = 'multi') {
|
||||
const query = { query: text, page: page };
|
||||
const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(TMDB_METHODS['search'][type], query))
|
||||
.catch(() => { throw new Error('Could not search for movies/shows at tmdb.'); })
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then(response => this.mapResults(response));
|
||||
}
|
||||
movieSearch(query, page=1) {
|
||||
const tmdbquery = { query: query, page: page };
|
||||
const cacheKey = `${this.cacheTags.movieSearch}:${page}:${query}`;
|
||||
|
||||
/**
|
||||
* Fetches a given list from tmdb.
|
||||
* @param {String} listName Name of list
|
||||
* @param {String} type filter results by type (default movie)
|
||||
return this.cache.get(cacheKey)
|
||||
.catch(() => this.tmdb('searchMovie', tmdbquery))
|
||||
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie search results'))
|
||||
.then(response => this.cache.set(cacheKey, response, 1))
|
||||
.then(response => this.mapResults(response, 'movie'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive show search results from TMDB.
|
||||
* @param {String} text query you want to search for
|
||||
* @param {Number} page representing pagination of results
|
||||
* @returns {Promise} dict with query results, current page and total_pages
|
||||
*/
|
||||
listSearch(listName, type = 'movie', page = '1') {
|
||||
const query = { page: page };
|
||||
console.log(query);
|
||||
const cacheKey = `${this.cacheTags[listName]}:${type}:${page}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(TMDB_METHODS[listName][type], query))
|
||||
.catch(() => { throw new Error('Error fetching list from tmdb.'); })
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then(response => this.mapResults(response, type));
|
||||
}
|
||||
showSearch(query, page=1) {
|
||||
const tmdbquery = { query: query, page: page };
|
||||
const cacheKey = `${this.cacheTags.showSearch}:${page}:${query}`;
|
||||
|
||||
/**
|
||||
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.
|
||||
* @param {String} response from tmdb.
|
||||
* @param {String} The type declared in listSearch.
|
||||
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
|
||||
*/
|
||||
mapResults(response, type) {
|
||||
console.log(response.page);
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
const mappedResults = response.results.filter((element) => {
|
||||
return (element.media_type === 'movie' || element.media_type === 'tv' || element.media_type === undefined);
|
||||
}).map((element) => convertTmdbToSeasoned(element, type));
|
||||
return {
|
||||
results: mappedResults,
|
||||
page: response.page,
|
||||
total_pages: response.total_pages,
|
||||
total_results: response.total_results
|
||||
}
|
||||
})
|
||||
.catch((error) => { throw new Error(error); });
|
||||
}
|
||||
mapResults(response, type=undefined) {
|
||||
// console.log(response.results)
|
||||
// response.results.map(te => console.table(te))
|
||||
|
||||
let results = response.results.map(result => {
|
||||
if (type === 'movie' || result.media_type === 'movie') {
|
||||
const movie = Movie.convertFromTmdbResponse(result)
|
||||
return movie.createJsonResponse()
|
||||
} else if (type === 'show' || result.media_type === 'tv') {
|
||||
const show = Show.convertFromTmdbResponse(result)
|
||||
return show.createJsonResponse()
|
||||
} else if (type === 'person' || result.media_type === 'person') {
|
||||
const person = Person.convertFromTmdbResponse(result)
|
||||
return person.createJsonResponse()
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
results: results,
|
||||
page: response.page,
|
||||
total_results: response.total_results,
|
||||
total_pages: response.total_pages
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps moviedb library to support Promises.
|
||||
@@ -122,7 +249,7 @@ class TMDB {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
return resolve(reponse);
|
||||
resolve(reponse);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
7
seasoned_api/src/tmdb/tmdb.ts
Normal file
7
seasoned_api/src/tmdb/tmdb.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Movie } from './types'
|
||||
|
||||
Movie('str', 123)
|
||||
|
||||
|
||||
|
||||
module.exports = TMDB;
|
||||
7
seasoned_api/src/tmdb/types.js
Normal file
7
seasoned_api/src/tmdb/types.js
Normal file
@@ -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 }
|
||||
64
seasoned_api/src/tmdb/types.ts
Normal file
64
seasoned_api/src/tmdb/types.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
interface Movie {
|
||||
adult: boolean;
|
||||
backdrop: string;
|
||||
genres: Genre[];
|
||||
id: number;
|
||||
imdb_id: number;
|
||||
overview: string;
|
||||
popularity: number;
|
||||
poster: string;
|
||||
release_date: Date;
|
||||
rank: number;
|
||||
runtime: number;
|
||||
status: string;
|
||||
tagline: string;
|
||||
title: string;
|
||||
vote_count: number;
|
||||
}
|
||||
|
||||
interface Show {
|
||||
adult: boolean;
|
||||
backdrop: string;
|
||||
episodes: number;
|
||||
genres: Genre[];
|
||||
id: number;
|
||||
imdb_id: number;
|
||||
overview: string;
|
||||
popularity: number;
|
||||
poster: string;
|
||||
rank: number;
|
||||
runtime: number;
|
||||
seasons: number;
|
||||
status: string;
|
||||
tagline: string;
|
||||
title: string;
|
||||
vote_count: number;
|
||||
}
|
||||
|
||||
interface Person {
|
||||
birthday: Date;
|
||||
deathday: Date;
|
||||
id: number;
|
||||
known_for: string;
|
||||
name: string;
|
||||
poster: string;
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
adult: boolean;
|
||||
backdrop_path: string;
|
||||
id: number;
|
||||
original_title: string;
|
||||
release_date: Date;
|
||||
poster_path: string;
|
||||
popularity: number;
|
||||
vote_average: number;
|
||||
vote_counte: number;
|
||||
}
|
||||
|
||||
interface Genre {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export { Movie, Show, Person, Genre }
|
||||
75
seasoned_api/src/tmdb/types/credits.js
Normal file
75
seasoned_api/src/tmdb/types/credits.js
Normal file
@@ -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;
|
||||
62
seasoned_api/src/tmdb/types/movie.js
Normal file
62
seasoned_api/src/tmdb/types/movie.js
Normal file
@@ -0,0 +1,62 @@
|
||||
class Movie {
|
||||
constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined,
|
||||
releaseDate=undefined, rating=undefined, genres=undefined, productionStatus=undefined,
|
||||
tagline=undefined, runtime=undefined, imdb_id=undefined, popularity=undefined) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.year = year;
|
||||
this.overview = overview;
|
||||
this.poster = poster;
|
||||
this.backdrop = backdrop;
|
||||
this.releaseDate = releaseDate;
|
||||
this.rating = rating;
|
||||
this.genres = genres;
|
||||
this.productionStatus = productionStatus;
|
||||
this.tagline = tagline;
|
||||
this.runtime = runtime;
|
||||
this.imdb_id = imdb_id;
|
||||
this.popularity = popularity;
|
||||
this.type = 'movie';
|
||||
}
|
||||
|
||||
static convertFromTmdbResponse(response) {
|
||||
const { id, title, release_date, overview, poster_path, backdrop_path, vote_average, genres, status,
|
||||
tagline, runtime, imdb_id, popularity } = response;
|
||||
|
||||
const releaseDate = new Date(release_date);
|
||||
const year = releaseDate.getFullYear();
|
||||
const genreNames = genres ? genres.map(g => g.name) : undefined
|
||||
|
||||
return new Movie(id, title, year, overview, poster_path, backdrop_path, releaseDate, vote_average, genreNames, status,
|
||||
tagline, runtime, imdb_id, popularity)
|
||||
}
|
||||
|
||||
static convertFromPlexResponse(response) {
|
||||
// console.log('response', response)
|
||||
const { title, year, rating, tagline, summary } = response;
|
||||
const _ = undefined
|
||||
|
||||
return new Movie(null, title, year, summary, _, _, _, rating, _, _, tagline)
|
||||
}
|
||||
|
||||
createJsonResponse() {
|
||||
return {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
year: this.year,
|
||||
overview: this.overview,
|
||||
poster: this.poster,
|
||||
backdrop: this.backdrop,
|
||||
release_date: this.releaseDate,
|
||||
rating: this.rating,
|
||||
genres: this.genres,
|
||||
production_status: this.productionStatus,
|
||||
tagline: this.tagline,
|
||||
runtime: this.runtime,
|
||||
imdb_id: this.imdb_id,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Movie;
|
||||
37
seasoned_api/src/tmdb/types/person.js
Normal file
37
seasoned_api/src/tmdb/types/person.js
Normal file
@@ -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;
|
||||
78
seasoned_api/src/tmdb/types/releaseDates.js
Normal file
78
seasoned_api/src/tmdb/types/releaseDates.js
Normal file
@@ -0,0 +1,78 @@
|
||||
class ReleaseDates {
|
||||
constructor(id, releases) {
|
||||
this.id = id;
|
||||
this.releases = releases;
|
||||
}
|
||||
|
||||
static convertFromTmdbResponse(response) {
|
||||
const { id, results } = response;
|
||||
|
||||
const releases = results.map(countryRelease =>
|
||||
new Release(
|
||||
countryRelease.iso_3166_1,
|
||||
countryRelease.release_dates.map(rd => new ReleaseDate(rd.certification, rd.iso_639_1, rd.release_date, rd.type, rd.note))
|
||||
))
|
||||
|
||||
return new ReleaseDates(id, releases)
|
||||
}
|
||||
|
||||
createJsonResponse() {
|
||||
return {
|
||||
id: this.id,
|
||||
results: this.releases.map(release => release.createJsonResponse())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Release {
|
||||
constructor(country, releaseDates) {
|
||||
this.country = country;
|
||||
this.releaseDates = releaseDates;
|
||||
}
|
||||
|
||||
createJsonResponse() {
|
||||
return {
|
||||
country: this.country,
|
||||
release_dates: this.releaseDates.map(releaseDate => releaseDate.createJsonResponse())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReleaseDate {
|
||||
constructor(certification, language, releaseDate, type, note) {
|
||||
this.certification = certification;
|
||||
this.language = language;
|
||||
this.releaseDate = releaseDate;
|
||||
this.type = this.releaseTypeLookup(type);
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
releaseTypeLookup(releaseTypeKey) {
|
||||
const releaseTypeEnum = {
|
||||
1: 'Premier',
|
||||
2: 'Limited theatrical',
|
||||
3: 'Theatrical',
|
||||
4: 'Digital',
|
||||
5: 'Physical',
|
||||
6: 'TV'
|
||||
}
|
||||
if (releaseTypeKey <= Object.keys(releaseTypeEnum).length) {
|
||||
return releaseTypeEnum[releaseTypeKey]
|
||||
} else {
|
||||
// TODO log | Release type not defined, does this need updating?
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
createJsonResponse() {
|
||||
return {
|
||||
certification: this.certification,
|
||||
language: this.language,
|
||||
release_date: this.releaseDate,
|
||||
type: this.type,
|
||||
note: this.note
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReleaseDates;
|
||||
50
seasoned_api/src/tmdb/types/show.js
Normal file
50
seasoned_api/src/tmdb/types/show.js
Normal file
@@ -0,0 +1,50 @@
|
||||
class Show {
|
||||
constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined,
|
||||
seasons=undefined, episodes=undefined, rank=undefined, genres=undefined, status=undefined,
|
||||
runtime=undefined) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.year = year;
|
||||
this.overview = overview;
|
||||
this.poster = poster;
|
||||
this.backdrop = backdrop;
|
||||
this.seasons = seasons;
|
||||
this.episodes = episodes;
|
||||
this.rank = rank;
|
||||
this.genres = genres;
|
||||
this.productionStatus = status;
|
||||
this.runtime = runtime;
|
||||
this.type = 'show';
|
||||
}
|
||||
|
||||
static convertFromTmdbResponse(response) {
|
||||
const { id, name, first_air_date, overview, poster_path, backdrop_path, number_of_seasons, number_of_episodes,
|
||||
rank, genres, status, episode_run_time, popularity } = response;
|
||||
|
||||
const year = new Date(first_air_date).getFullYear()
|
||||
const genreNames = genres ? genres.map(g => g.name) : undefined
|
||||
|
||||
return new Show(id, name, year, overview, poster_path, backdrop_path, number_of_seasons, number_of_episodes,
|
||||
rank, genreNames, status, episode_run_time, popularity)
|
||||
}
|
||||
|
||||
createJsonResponse() {
|
||||
return {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
year: this.year,
|
||||
overview: this.overview,
|
||||
poster: this.poster,
|
||||
backdrop: this.backdrop,
|
||||
seasons: this.seasons,
|
||||
episodes: this.episodes,
|
||||
rank: this.rank,
|
||||
genres: this.genres,
|
||||
production_status: this.productionStatus,
|
||||
runtime: this.runtime,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Show;
|
||||
@@ -2,36 +2,44 @@ const User = require('src/user/user');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
class Token {
|
||||
constructor(user) {
|
||||
this.user = user;
|
||||
}
|
||||
constructor(user, admin=false) {
|
||||
this.user = user;
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Generate a new token.
|
||||
* @param {String} secret a cipher of the token
|
||||
* @returns {String}
|
||||
*/
|
||||
toString(secret) {
|
||||
return jwt.sign({ username: this.user.username }, secret);
|
||||
}
|
||||
toString(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.
|
||||
* @param {Token} jwtToken an encrypted token
|
||||
* @param {String} secret a cipher of the token
|
||||
* @returns {Token}
|
||||
*/
|
||||
static fromString(jwtToken, secret) {
|
||||
let username = null;
|
||||
static fromString(jwtToken, secret) {
|
||||
let username = null;
|
||||
|
||||
try {
|
||||
username = jwt.verify(jwtToken, secret).username;
|
||||
} catch (error) {
|
||||
throw new Error('The token is invalid.');
|
||||
}
|
||||
const user = new User(username);
|
||||
return new Token(user);
|
||||
}
|
||||
const token = jwt.verify(jwtToken, secret, { clockTolerance: 10000 })
|
||||
if (token.username === undefined || token.username === null)
|
||||
throw new Error('Malformed token')
|
||||
|
||||
username = token.username
|
||||
const user = new User(username)
|
||||
return new Token(user)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Token;
|
||||
|
||||
@@ -26,6 +26,7 @@ class UserRepository {
|
||||
if (error.name === 'AssertionError' || error.message.endsWith('user_name')) {
|
||||
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');
|
||||
|
||||
class UserSecurity {
|
||||
constructor(database) {
|
||||
this.userRepository = new UserRepository(database);
|
||||
}
|
||||
constructor(database) {
|
||||
this.userRepository = new UserRepository(database);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Create a new user in PlanFlix.
|
||||
* @param {User} user the new user you want to create
|
||||
* @param {String} clearPassword a password of the user
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createNewUser(user, clearPassword) {
|
||||
if (user.username.trim() === '') {
|
||||
throw new Error('The username is empty.');
|
||||
} else if (clearPassword.trim() === '') {
|
||||
throw new Error('The password is empty.');
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
.then(() => this.userRepository.create(user))
|
||||
.then(() => UserSecurity.hashPassword(clearPassword))
|
||||
.then(hash => this.userRepository.changePassword(user, hash));
|
||||
}
|
||||
}
|
||||
createNewUser(user, clearPassword) {
|
||||
if (user.username.trim() === '') {
|
||||
throw new Error('The username is empty.');
|
||||
} else if (clearPassword.trim() === '') {
|
||||
throw new Error('The password is empty.');
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
.then(() => this.userRepository.create(user))
|
||||
.then(() => UserSecurity.hashPassword(clearPassword))
|
||||
.then(hash => this.userRepository.changePassword(user, hash))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Login into PlanFlix.
|
||||
* @param {User} user the user you want to login
|
||||
* @param {String} clearPassword the user's password
|
||||
* @returns {Promise}
|
||||
*/
|
||||
login(user, clearPassword) {
|
||||
return Promise.resolve()
|
||||
.then(() => this.userRepository.retrieveHash(user))
|
||||
.then(hash => UserSecurity.compareHashes(hash, clearPassword))
|
||||
.catch(() => { throw new Error('Wrong username or password.'); });
|
||||
}
|
||||
login(user, clearPassword) {
|
||||
return Promise.resolve()
|
||||
.then(() => this.userRepository.retrieveHash(user))
|
||||
.then(hash => UserSecurity.compareHashes(hash, clearPassword))
|
||||
.catch(() => { throw new Error('Incorrect username or password.'); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare between a password and a hash password from database.
|
||||
* @param {String} hash the hash password from database
|
||||
* @param {String} clearPassword the user's password
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static compareHashes(hash, clearPassword) {
|
||||
return new Promise((resolve, reject) => {
|
||||
bcrypt.compare(clearPassword, hash, (error, matches) => {
|
||||
if (matches === true) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
* Compare between a password and a hash password from database.
|
||||
* @param {String} hash the hash password from database
|
||||
* @param {String} clearPassword the user's password
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static compareHashes(hash, clearPassword) {
|
||||
return new Promise((resolve, reject) => {
|
||||
bcrypt.compare(clearPassword, hash, (error, match) => {
|
||||
if (match)
|
||||
resolve()
|
||||
reject()
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Hashes a password.
|
||||
* @param {String} clearPassword the user's password
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static hashPassword(clearPassword) {
|
||||
return new Promise((resolve) => {
|
||||
bcrypt.hash(clearPassword, null, null, (error, hash) => {
|
||||
resolve(hash);
|
||||
});
|
||||
static hashPassword(clearPassword) {
|
||||
return new Promise((resolve) => {
|
||||
const saltRounds = 10;
|
||||
bcrypt.hash(clearPassword, saltRounds, (error, hash) => {
|
||||
resolve(hash);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserSecurity;
|
||||
|
||||
@@ -6,16 +6,14 @@ const mustBeAuthenticated = require('./middleware/mustBeAuthenticated');
|
||||
const mustBeAdmin = require('./middleware/mustBeAdmin');
|
||||
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.
|
||||
Raven.config(configuration.get('raven', 'DSN')).install();
|
||||
|
||||
const app = express(); // define our app using express
|
||||
app.use(Raven.requestHandler());
|
||||
// this will let us get the data from a POST
|
||||
// configure app to use bodyParser()
|
||||
app.use(bodyParser.json());
|
||||
// router.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
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.
|
||||
router.use((req, res, next) => {
|
||||
// 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) {
|
||||
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-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.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
|
||||
*/
|
||||
router.get('/v2/plex/search', require('./controllers/plex/search'));
|
||||
|
||||
/**
|
||||
* List
|
||||
*/
|
||||
router.get('/v1/plex/search', require('./controllers/plex/searchMedia.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/:mediaId', require('./controllers/plex/readRequest.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
|
||||
*/
|
||||
|
||||
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.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.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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
26
seasoned_api/src/webserver/controllers/movie/credits.js
Normal file
26
seasoned_api/src/webserver/controllers/movie/credits.js
Normal file
@@ -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;
|
||||
58
seasoned_api/src/webserver/controllers/movie/info.js
Normal file
58
seasoned_api/src/webserver/controllers/movie/info.js
Normal file
@@ -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;
|
||||
26
seasoned_api/src/webserver/controllers/movie/releaseDates.js
Normal file
26
seasoned_api/src/webserver/controllers/movie/releaseDates.js
Normal file
@@ -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;
|
||||
25
seasoned_api/src/webserver/controllers/person/info.js
Normal file
25
seasoned_api/src/webserver/controllers/person/info.js
Normal file
@@ -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) => {
|
||||
res.send(result);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ function updateRequested(req, res) {
|
||||
.then((result) => {
|
||||
res.send({ success: true, results: result });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(401).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
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 });
|
||||
})
|
||||
.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 configuration = require('src/config/configuration').getInstance();
|
||||
|
||||
const plexRepository = new PlexRepository();
|
||||
const plexRepository = new PlexRepository(configuration.get('plex', 'ip'));
|
||||
|
||||
function playingController(req, res) {
|
||||
plexRepository.nowPlaying()
|
||||
.then((movies) => {
|
||||
.then(movies => {
|
||||
res.send(movies);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ function readRequestController(req, res) {
|
||||
const mediaId = req.params.mediaId;
|
||||
const { type } = req.query;
|
||||
requestRepository.lookup(mediaId, type)
|
||||
.then((movies) => {
|
||||
.then(movies => {
|
||||
res.send(movies);
|
||||
}).catch((error) => {
|
||||
res.status(404).send({ success: false, error: error.message });
|
||||
}).catch(error => {
|
||||
res.status(404).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
25
seasoned_api/src/webserver/controllers/plex/search.js
Normal file
25
seasoned_api/src/webserver/controllers/plex/search.js
Normal file
@@ -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 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
|
||||
@@ -13,15 +14,15 @@ function searchMediaController(req, res) {
|
||||
const { query } = req.query;
|
||||
|
||||
plexRepository.search(query)
|
||||
.then((media) => {
|
||||
.then(media => {
|
||||
if (media !== undefined || media.length > 0) {
|
||||
res.send(media);
|
||||
} 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) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ function searchRequestController(req, res) {
|
||||
.then((searchResult) => {
|
||||
res.send(searchResult);
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error });
|
||||
.catch(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
|
||||
@@ -8,22 +21,31 @@ const requestRepository = new RequestRepository();
|
||||
* @param {Response} res
|
||||
* @returns {Callback}
|
||||
*/
|
||||
|
||||
function submitRequestController(req, res) {
|
||||
// This is the id that is the param of the url
|
||||
const id = req.params.mediaId;
|
||||
const type = req.query.type;
|
||||
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
const user_agent = req.headers['user-agent'];
|
||||
const user = req.loggedInUser;
|
||||
// This is the id that is the param of the url
|
||||
const id = req.params.mediaId;
|
||||
const type = req.query.type ? req.query.type.toLowerCase() : undefined
|
||||
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
const user_agent = req.headers['user-agent'];
|
||||
const user = req.loggedInUser;
|
||||
let mediaFunction = undefined
|
||||
|
||||
requestRepository.sendRequest(id, type, ip, user_agent, user)
|
||||
.then(() => {
|
||||
res.send({ success: true, message: 'Media item sucessfully requested!' });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
});
|
||||
if (type === 'movie') {
|
||||
console.log('movie')
|
||||
mediaFunction = tmdbMovieInfo
|
||||
} else if (type === 'show') {
|
||||
console.log('show')
|
||||
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;
|
||||
|
||||
@@ -18,7 +18,7 @@ function updateRequested(req, res) {
|
||||
res.send({ success: true });
|
||||
})
|
||||
.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;
|
||||
21
seasoned_api/src/webserver/controllers/request/getRequest.js
Normal file
21
seasoned_api/src/webserver/controllers/request/getRequest.js
Normal file
@@ -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;
|
||||
40
seasoned_api/src/webserver/controllers/search/movieSearch.js
Normal file
40
seasoned_api/src/webserver/controllers/search/movieSearch.js
Normal file
@@ -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;
|
||||
45
seasoned_api/src/webserver/controllers/search/multiSearch.js
Normal file
45
seasoned_api/src/webserver/controllers/search/multiSearch.js
Normal file
@@ -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;
|
||||
35
seasoned_api/src/webserver/controllers/search/showSearch.js
Normal file
35
seasoned_api/src/webserver/controllers/search/showSearch.js
Normal file
@@ -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);
|
||||
})
|
||||
.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);
|
||||
})
|
||||
.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' });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).send({ success: false, error: error.message });
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
26
seasoned_api/src/webserver/controllers/show/credits.js
Normal file
26
seasoned_api/src/webserver/controllers/show/credits.js
Normal file
@@ -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;
|
||||
56
seasoned_api/src/webserver/controllers/show/info.js
Normal file
56
seasoned_api/src/webserver/controllers/show/info.js
Normal file
@@ -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;
|
||||
|
||||
searchHistory.read(username)
|
||||
.then((searchQueries) => {
|
||||
.then(searchQueries => {
|
||||
res.send({ success: true, searchQueries });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(404).send({ success: false, error: error });
|
||||
.catch(error => {
|
||||
res.status(404).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ function loginController(req, res) {
|
||||
|
||||
userSecurity.login(user, password)
|
||||
.then(() => userRepository.checkAdmin(user))
|
||||
.then((checkAdmin) => {
|
||||
const token = new Token(user).toString(secret);
|
||||
const admin_state = checkAdmin === 1 ? true : false;
|
||||
res.send({ success: true, token, admin: admin_state });
|
||||
.then(checkAdmin => {
|
||||
const isAdmin = checkAdmin === 1 ? true : false;
|
||||
const token = new Token(user, isAdmin).toString(secret);
|
||||
res.send({ success: true, token });
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(401).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
res.status(401).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,15 @@ function registerController(req, res) {
|
||||
|
||||
userSecurity.createNewUser(user, password)
|
||||
.then(() => userRepository.checkAdmin(user))
|
||||
.then((checkAdmin) => {
|
||||
const token = new Token(user).toString(secret);
|
||||
const admin_state = checkAdmin === 1 ? true : false;
|
||||
.then(checkAdmin => {
|
||||
const isAdmin = checkAdmin === 1 ? true : false;
|
||||
const token = new Token(user, isAdmin).toString(secret);
|
||||
res.send({
|
||||
success: true, message: 'Welcome to Seasoned!', token, admin: admin_state,
|
||||
success: true, message: 'Welcome to Seasoned!', token
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(401).send({ success: false, error: error.message });
|
||||
.catch(error => {
|
||||
res.status(401).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,11 @@ function requestsController(req, res) {
|
||||
const user = req.loggedInUser;
|
||||
|
||||
requestRepository.userRequests(user)
|
||||
.then((requests) => {
|
||||
.then(requests => {
|
||||
res.send({ success: true, results: requests, total_results: requests.length });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
res.status(500).send({ success: false, error: error });
|
||||
.catch(error => {
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,16 @@ const Token = require('src/user/token');
|
||||
// curl -i -H "Authorization:[token]" localhost:31459/api/v1/user/history
|
||||
|
||||
const tokenToUser = (req, res, next) => {
|
||||
const rawToken = req.headers.authorization;
|
||||
if (rawToken) {
|
||||
try {
|
||||
const token = Token.fromString(rawToken, secret);
|
||||
req.loggedInUser = token.user;
|
||||
} catch (error) {
|
||||
req.loggedInUser = undefined;
|
||||
}
|
||||
}
|
||||
next();
|
||||
const rawToken = req.headers.authorization;
|
||||
if (rawToken) {
|
||||
try {
|
||||
const token = Token.fromString(rawToken, secret);
|
||||
req.loggedInUser = token.user;
|
||||
} catch (error) {
|
||||
req.loggedInUser = undefined;
|
||||
}
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = tokenToUser;
|
||||
|
||||
1
seasoned_api/test/fixtures/blade_runner_2049-info-success-response.json
vendored
Normal file
1
seasoned_api/test/fixtures/blade_runner_2049-info-success-response.json
vendored
Normal file
@@ -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}]
|
||||
6
seasoned_api/test/fixtures/empty-query-success-response.json
vendored
Normal file
6
seasoned_api/test/fixtures/empty-query-success-response.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"page":1,
|
||||
"results":[],
|
||||
"total_results":0,
|
||||
"total_pages":1
|
||||
}
|
||||
1
seasoned_api/test/fixtures/interstellar-query-movie-success-response.json
vendored
Normal file
1
seasoned_api/test/fixtures/interstellar-query-movie-success-response.json
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
16
seasoned_api/test/helpers/tmdbMock2.js
Normal file
16
seasoned_api/test/helpers/tmdbMock2.js
Normal file
@@ -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(() =>
|
||||
this.server = require('src/webserver/server'));
|
||||
|
||||
it('should listen on port 31459', (done) => {
|
||||
net.createConnection(31459, done);
|
||||
it('should listen on port 31400', (done) => {
|
||||
net.createConnection(31400, done);
|
||||
});
|
||||
|
||||
afterEach(() =>
|
||||
|
||||
@@ -15,6 +15,6 @@ describe('As a user I want error when registering existing username', () => {
|
||||
.post('/api/v1/user')
|
||||
.send({ username: 'test_user', password: 'password' })
|
||||
.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', () => {
|
||||
before(() => resetDatabase());
|
||||
before(() => createCacheEntry('p:movie:1', popularMoviesSuccess));
|
||||
before(() => createCacheEntry('pm:1', popularMoviesSuccess));
|
||||
|
||||
it('should return 200 with the information', () =>
|
||||
request(app)
|
||||
.get('/api/v1/tmdb/list/popular')
|
||||
.get('/api/v2/movie/popular')
|
||||
.expect(200)
|
||||
.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', () => {
|
||||
before(() => resetDatabase());
|
||||
before(() => createCacheEntry('p:show:1', popularShowsSuccess));
|
||||
before(() => createCacheEntry('pt:1', popularShowsSuccess));
|
||||
|
||||
it('should return 200 with the information', () =>
|
||||
request(app)
|
||||
.get('/api/v1/tmdb/list/popular?type=show')
|
||||
.get('/api/v2/show/popular')
|
||||
.expect(200)
|
||||
.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 createUser = require('test/helpers/createUser');
|
||||
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', () => {
|
||||
before(() => {
|
||||
return resetDatabase()
|
||||
.then(() => createUser('test_user', 'test@gmail.com', 'password'));
|
||||
before(async () => {
|
||||
await resetDatabase()
|
||||
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', () =>
|
||||
request(app)
|
||||
.post('/api/v1/plex/request/329865')
|
||||
.set('Authorization', createToken('test_user', 'secret'))
|
||||
.post('/api/v2/request')
|
||||
.set('authorization', createToken('test_user', 'secret'))
|
||||
.send({ id: 335984, type: 'movie' })
|
||||
.expect(200)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,15 +2,15 @@ const createCacheEntry = require('test/helpers/createCacheEntry');
|
||||
const resetDatabase = require('test/helpers/resetDatabase');
|
||||
const request = require('supertest-as-promised');
|
||||
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', () => {
|
||||
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', () =>
|
||||
request(app)
|
||||
.get('/api/v1/tmdb/search?query=interstellar&page=1')
|
||||
.get('/api/v2/search/movie?query=interstellar&page=1')
|
||||
.expect(200)
|
||||
);
|
||||
});
|
||||
|
||||
31
seasoned_api/test/unit/tmdb/testConvertTmdbToMovie.js
Normal file
31
seasoned_api/test/unit/tmdb/testConvertTmdbToMovie.js
Normal file
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
31
seasoned_api/test/unit/tmdb/testTmdb.disabled
Normal file
31
seasoned_api/test/unit/tmdb/testTmdb.disabled
Normal file
@@ -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")
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user