54 Commits

Author SHA1 Message Date
488da889d8 Current state of graphql implementation. Some things are broken. Focused on getting progress from deluge linked with requests and requested torrents. Warning! the payload size of this action is 1MB pr second. This also heavily dependes on un-tracked changes in both delugeClient and the frontend for the visualization. 2019-11-21 22:32:08 +01:00
8da7c159b1 WIP graphql to get requests, torrents and working on requests with torrents by username. 2019-11-11 16:54:54 +01:00
ea5bc36956 Merge pull request #111 from KevinMidboe/api/v2
Api/v2
2019-11-04 18:01:15 +01:00
fd475265c1 Updated test to reflect changes to all error response objects. (changed from returning message in error key to message. 2019-11-04 17:54:08 +01:00
b0804f8a08 Merge branch 'api/v2' of github.com:kevinmidboe/seasonedShows into api/v2 2019-11-04 00:57:45 +01:00
6b737b8ab4 Updated all controller responses to return message not error on errors. 2019-11-04 00:57:27 +01:00
5623344666 Merge branch 'master' into api/v2 2019-11-03 20:57:25 +01:00
f8cc19b510 Added rating and release_date when parsing movies and fixed respective tests 2019-11-03 20:56:46 +01:00
c589457a6c Removed unused mongoose package 2019-11-03 20:55:54 +01:00
b802a7b62b Moved, renamed, re-did and added a lot of stuff. Getting ready for the v2 upgrade 2019-11-03 20:33:30 +01:00
879a02b388 Finished movie credits and release dates 2019-11-03 16:01:19 +01:00
bc3d4881bd New release_dates endpoint for movie 2019-11-03 15:43:35 +01:00
ef8d4d90b2 Removed console log 2019-11-03 15:08:42 +01:00
d2d396bb7a Set cache TTL for credits to 1 day 2019-11-03 15:07:11 +01:00
500b75eaf6 We know there could be a 401 response so we handle it 2019-11-03 15:04:14 +01:00
9308d4ea9b Credits endpoint for movies 2019-11-03 15:02:45 +01:00
6c2c81a1a1 Updated movieInfo controller to also handle requesting release_dates as query parameter 2019-10-04 22:36:39 +02:00
90aa4d2485 Rewrote the movie & show list controller to be more abstract and easier to extend later 2019-10-04 21:21:52 +02:00
0ca3f81bf8 listController first defines all async functions as constant variables then module exports them all as a dict 2019-10-04 20:55:39 +02:00
b9831c6b3d Forgot to reasing variables after copy-pasting them in convertTmdbToMovie 2019-10-04 20:37:06 +02:00
4781e9ae65 Merge pull request #119 from KevinMidboe/snyk-fix-a43be890852096db7a469faa6c44f8ef
[Snyk] Fix for 3 vulnerable dependencies
2019-07-27 02:17:50 +02:00
snyk-test
eb0881f19e fix: client/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-JSYAML-173999
- https://snyk.io/vuln/SNYK-JS-JSYAML-174129
- https://snyk.io/vuln/npm:mem:20180117
2019-07-27 00:16:09 +00:00
bc4d73821d Upgraded webpack-dev-server to not have a screaming vulnerability 2019-07-27 02:05:38 +02:00
ab6144eb81 Update yarn lock, updated coveralls and mocha run under coverage command now uses required babel register 2019-07-27 02:03:11 +02:00
c3d87e2200 Merge pull request #117 from KevinMidboe/snyk-fix-9429d5dfb86996c66ac12aef1a5178b1
[Snyk] Fix for 2 vulnerable dependencies
2019-07-27 01:58:33 +02:00
e391ce7ef9 Merge pull request #112 from KevinMidboe/snyk-fix-5oeu5e
[Snyk] Fix for 4 vulnerable dependencies
2019-07-27 01:58:07 +02:00
ca707078d9 Merge branch 'master' into snyk-fix-5oeu5e 2019-07-27 01:57:08 +02:00
snyk-test
53228a2662 fix: client/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-JSYAML-173999
- https://snyk.io/vuln/SNYK-JS-JSYAML-174129
2019-07-26 23:56:40 +00:00
2a9fa27341 Merge pull request #113 from KevinMidboe/snyk-fix-25dne6
[Snyk] Fix for 1 vulnerable dependencies
2019-07-27 01:56:24 +02:00
3068281461 Update README.md 2019-07-27 01:55:43 +02:00
81e9fe5b15 Testing for a running application is a bit weird, disabling it for now 2019-07-27 01:42:44 +02:00
5d2e375213 Upgraded node version for travis from 8 to 11 2019-07-27 01:34:15 +02:00
7ede37039a info-success-response is now a list so need this reflected in this test also. Changed the port we test for to whats in our config/test.jsono 2019-07-27 01:30:08 +02:00
8e23ae5a27 Added babel for ES6 functionality. In this case the new import statements 2019-07-27 01:28:41 +02:00
04ba094a14 Throw more errors and cleanup some unmerged code 2019-07-26 21:55:59 +02:00
23f9911237 Throw errors when failing to create user 2019-07-26 21:54:13 +02:00
3b27af1f83 Error handling for themoviedb api response codes that are not 200. Started with 401 and 404. See issue #116 for info. 2019-07-26 21:52:20 +02:00
afb7af46b8 The test uses the cached to not need to query themoviedb, but the function that would in prod now saves a Class containing movie result and extras such as credits. 2019-07-26 21:09:58 +02:00
6ba8ca2add Updated search query response 2019-07-26 21:07:26 +02:00
135375cb94 instead of "describe" "xdescribe" was defined which made the test pending in results. This has now been resolved. 2019-07-26 21:06:45 +02:00
e5d5bdefd6 Updated cache key and cleaned up formatting 2019-07-26 21:05:45 +02:00
6f9ca9e067 Added package and commands for generating documentation and upgraded mocha 2019-07-26 20:56:33 +02:00
c42195d242 Removed swap file 2019-07-25 00:53:40 +02:00
a5aaf1bfca Merge branch 'api/v2' of github.com:KevinMidboe/seasonedShows into api/v2 2019-07-25 00:53:16 +02:00
af7b1f2424 Script for updating all requested and downloading request status to downloaded if exist in plex 2019-07-25 00:48:16 +02:00
6aba9774c6 When requesting all request elements we now also return the page as int not string 2019-07-25 00:47:17 +02:00
e19cfb5870 Updated formatting 2019-07-25 00:24:04 +02:00
144b27f128 Renamed token variable from user to username 2019-07-25 00:23:32 +02:00
12afbf6364 Tokens can also have a admin property. When admin is defined its included in the jwt token. 2019-07-25 00:13:28 +02:00
8a5ab204e1 Change node bcrypt package from bcrypt-nodejs to bcrypt. Change response message on invalid username/pass and changed to bcrypt syntax for compare and hash. 2019-07-24 23:02:06 +02:00
3f04d9bc56 Update script for updating all plex statuses of requestes. 2019-07-24 23:02:06 +02:00
de50805d1e Handle both status code 301 and 302 from jackett 2019-07-15 18:16:46 +02:00
snyk-bot
4eaa60b044 fix: seasoned_api/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-MPATH-72672
2018-12-13 03:19:03 +00:00
snyk-bot
7db8f752c5 fix: seasoned_api/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/npm:base64url:20180511
- https://snyk.io/vuln/npm:cryptiles:20180710
- https://snyk.io/vuln/npm:extend:20180424
- https://snyk.io/vuln/npm:stringstream:20180511
2018-12-08 03:59:04 +00:00
77 changed files with 5674 additions and 1656 deletions

View File

@@ -1,5 +1,5 @@
language: node_js
node_js: '8.7.0'
node_js: '11.9.0'
git:
submodules: true
script:

View File

@@ -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="">

View File

@@ -12,7 +12,7 @@
},
"dependencies": {
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^0.28.4",
"css-loader": "^1.0.0",
"html-webpack-plugin": "^2.28.0",
"path": "^0.12.7",
"react": "^15.6.1",
@@ -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": {

View File

@@ -7,39 +7,51 @@
},
"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/unit test/system",
"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": {
"axios": "^0.18.0",
"bcrypt-nodejs": "^0.0.3",
"bcrypt": "^3.0.6",
"body-parser": "~1.18.2",
"cross-env": "~5.1.4",
"express": "~4.16.0",
"jsonwebtoken": "^8.0.1",
"km-moviedb": "^0.2.13",
"mongoose": "~5.0.11",
"express-graphql": "^0.9.0",
"express-reload": "^1.2.0",
"graphql": "^14.5.8",
"jsonwebtoken": "^8.2.0",
"km-moviedb": "^0.2.12",
"node-cache": "^4.1.1",
"node-fetch": "^2.6.0",
"python-shell": "^0.5.0",
"request": "^2.85.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"
}
}

View File

@@ -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) {

View File

@@ -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,7 +21,7 @@ function getMagnetFromURL(url) {
async function find(searchterm, callback) {
const options = {
pythonPath: '../torrent_search/env/bin/python3.6',
pythonPath: '../torrent_search/env/bin/python3',
scriptPath: '../torrent_search',
args: [searchterm, '-s', 'jackett', '-f', '--print']
}
@@ -35,7 +35,7 @@ async function callPythonAddMagnet(url, callback) {
getMagnetFromURL(url)
.then((magnet) => {
const options = {
pythonPath: '../delugeClient/env/bin/python3.6',
pythonPath: '../delugeClient/env/bin/python3',
scriptPath: '../delugeClient',
args: ['add', magnet]
};
@@ -51,13 +51,10 @@ async function callPythonAddMagnet(url, callback) {
async function SearchPiratebay(query) {
return await new Promise((resolve, reject) => find(query, (err, results) => {
if (err) {
/* eslint-disable no-console */
console.log('THERE WAS A FUCKING ERROR!\n', err);
reject(Error('There was a error when searching for torrents'));
}
if (results) {
/* eslint-disable no-console */
console.log('result', results);
resolve(JSON.parse(results, null, '\t'));
}
}));

View File

@@ -1,59 +1,89 @@
const axios = require('axios')
const fetch = require('node-fetch')
const convertPlexToMovie = require('src/plex/convertPlexToMovie')
const convertPlexToShow = require('src/plex/convertPlexToShow')
const convertPlexToEpisode = require('src/plex/convertPlexToEpisode')
const { Movie, Show, Person } = require('src/tmdb/types');
// const { Movie, }
// TODO? import class definitions to compare types ?
// what would typescript do?
class Plex {
constructor(ip) {
constructor(ip, port=32400) {
this.plexIP = ip
this.plexPort = 32400
this.plexPort = port
}
matchTmdbAndPlexMedia(plex, tmdb) {
if (plex === undefined || tmdb === undefined)
return false
const sanitize = (string) => string.toLowerCase()
const matchTitle = sanitize(plex.title) === sanitize(tmdb.title)
const matchYear = plex.year === tmdb.year
return matchTitle && matchYear
}
existsInPlex(tmdbMovie) {
return Promise.resolve()
.then(() => this.search(tmdbMovie.title))
// TODO handle this when whitelist of local ip is not set in plex
.catch((error) => { console.error('Unable to search plex')})
.then((plexMovies) => {
const matches = plexMovies.some((plexMovie) => {
return tmdbMovie.title === plexMovie.title && tmdbMovie.type === plexMovie.type
})
return this.search(tmdbMovie.title)
.then(plexMovies => plexMovies.some(plex => this.matchTmdbAndPlexMedia(plex, tmdbMovie)))
}
tmdbMovie.existsInPlex = matches
return tmdbMovie
})
successfullResponse(response) {
const { status, statusText } = response
if (status === 200) {
return response.json()
} else {
throw { message: statusText, status: status }
}
}
search(query) {
const url = `http://${this.plexIP}:${this.plexPort}/hubs/search?query=${query}`
const options = {
baseURL: `http://${this.plexIP}:${this.plexPort}`,
url: '/hubs/search',
params: { query: query },
responseType: 'json',
timeout: 3000
timeout: 2000,
headers: { 'Accept': 'application/json' }
}
return Promise.resolve()
.then(() => axios.request(options))
.catch((error) => { throw new Error(`Unable to search plex library`, error) })
.then(response => this.mapResults(response))
return fetch(url, options)
.then(this.successfullResponse)
.then(this.mapResults)
.catch(error => {
if (error.type === 'request-timeout') {
throw { message: 'Plex did not respond', status: 408, success: false }
}
throw error
})
}
mapResults(response) {
return response.data.MediaContainer.Hub.reduce((result, hub) => {
if (hub.type === 'movie' && hub.Metadata !== undefined) {
return [...result, ...hub.Metadata.map(convertPlexToMovie)]
}
else if (hub.type === 'show' && hub.Metadata !== undefined) {
return [...result, ...hub.Metadata.map(convertPlexToShow)]
}
else if (hub.type === 'episode' && hub.Metadata !== undefined) {
return [...result, ...hub.Metadata.map(convertPlexToEpisode)]
}
if (response === undefined || response.MediaContainer === undefined) {
console.log('response was not valid to map', response)
return []
}
return result
}, [])
return response.MediaContainer.Hub
.filter(category => category.size > 0)
.map(category => {
if (category.type === 'movie') {
return category.Metadata.map(movie => {
const ovie = Movie.convertFromPlexResponse(movie)
return ovie.createJsonResponse()
})
} else if (category.type === 'show') {
return category.Metadata.map(convertPlexToShow)
} else if (category.type === 'episode') {
return category.Metadata.map(convertPlexToEpisode)
}
})
.filter(result => result !== undefined)
.flat()
}
}

View File

@@ -6,7 +6,6 @@ class Show {
this.rating = null;
this.seasons = null;
this.episodes = null;
this.type = 'show';
}
}

View 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

View File

@@ -23,6 +23,7 @@ class RequestRepository {
downloaded: '(select status from requests where id is request.id and type is request.type limit 1)',
// deluge: '(select status from deluge_torrent where id is request.id and type is request.type limit 1)',
// fetchAllFilterStatus: 'select * from request where '
readWithoutUserData: 'select id, title, year, type, status, date from requests where id is ? and type is ?',
read: 'select id, title, year, type, status, requested_by, ip, date, user_agent from requests where id is ? and type is ?'
};
}
@@ -106,11 +107,17 @@ class RequestRepository {
* @returns {Promise}
*/
getRequestByIdAndType(id, type) {
return Promise.resolve()
.then(() => this.database.get(this.queries.read, [id, type]))
return this.database.get(this.queries.readWithoutUserData, [id, type])
.then(row => {
assert(row, 'Could not find request item with that id and type')
return JSON.stringify(row)
return {
id: row.id,
title: row.title,
year: row.year,
type: row.type,
status: row.status,
requested_date: new Date(row.date)
}
})
}
@@ -124,6 +131,7 @@ class RequestRepository {
*/
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]
@@ -144,11 +152,15 @@ class RequestRepository {
const totalRequests = sqliteResponse['totalRequests']
const totalPages = Math.ceil(totalRequests / 26)
return [ rows.map(item => { item.poster = item.poster_path; return item }), totalPages ]
return [ rows.map(item => {
item.poster = item.poster_path; delete item.poster_path;
item.backdrop = item.background_path; delete item.background_path;
return item
}), totalPages, totalRequests ]
return Promise.all(this.mapToTmdbByType(rows))
})
.then(([result, totalPages]) => Promise.resolve({
results: result, total_results: result.length, page: page, total_pages: totalPages
.then(([result, totalPages, totalRequests]) => Promise.resolve({
results: result, total_results: totalRequests, page: page, total_pages: totalPages
}))
.catch(error => { console.log(error);throw error })
}

View File

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

View File

@@ -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);
})
}
/**

View File

@@ -1,49 +0,0 @@
const Movie = require('src/tmdb/types/movie');
const tmdbSwitcher = (tmdbMovie, property) => tmdbMovie[property]
function convertTmdbToMovie(tmdbMovie, credits=undefined) {
const movie = new Movie(tmdbMovie.id, tmdbMovie.title)
movie.overview = tmdbMovie.overview;
movie.rank = tmdbMovie.vote_average;
if (credits) {
movie.credits = credits;
}
if (tmdbMovie.release_date !== undefined) {
movie.release_date = new Date(tmdbMovie.release_date);
movie.year = movie.release_date.getFullYear();
}
if (tmdbMovie.poster_path !== undefined) {
movie.poster = tmdbMovie.poster_path;
}
if (tmdbMovie.backdrop_path !== undefined) {
movie.backdrop = tmdbMovie.backdrop_path;
}
if (tmdbMovie.status !== undefined) {
movie.status = tmdbMovie.status;
}
if (tmdbMovie.genres !== undefined) {
movie.genres = tmdbMovie.genres.map(genre => genre.name);
}
if (tmdbMovie.tagline !== undefined) {
movie.tagline = tmdbMovie.tagline;
}
if (tmdbMovie.runtime !== undefined) {
movie.runtime = tmdbMovie.runtime;
}
if (tmdbMovie.imdb_id !== undefined) {
movie.imdb_id = tmdbMovie.imdb_id;
}
return movie;
}
module.exports = convertTmdbToMovie;

View File

@@ -1,30 +0,0 @@
const Person = require('src/tmdb/types/person');
const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie');
function convertTmdbToPerson(tmdbPerson, cast=undefined) {
const person = new Person(tmdbPerson.id, tmdbPerson.name);
if (tmdbPerson.profile_path !== undefined) {
person.poster = tmdbPerson.profile_path;
}
if (tmdbPerson.birthday !== undefined) {
person.birthday = new Date(tmdbPerson.birthday);
}
if (tmdbPerson.deathday !== undefined) {
person.deathday = tmdbPerson.deathday;
}
if (tmdbPerson.known_for !== undefined) {
person.known_for = tmdbPerson.known_for.map(convertTmdbToMovie);
}
if (cast) {
person.cast = cast.cast;
}
return person;
}
module.exports = convertTmdbToPerson;

View File

@@ -1,62 +0,0 @@
const TMDB = require('src/media_classes/tmdb');
const Movie = require('src/types/movie');
function translateYear(tmdbReleaseDate) {
return new Date(tmdbReleaseDate).getFullYear();
}
function translateGenre(tmdbGenres) {
return tmdbGenres.map(genre => genre.name);
}
function convertType(tmdbType) {
if (tmdbType === 'tv') return 'show';
return undefined;
}
function convertTmdbToMovie(tmdb) {
const year =
const movie = new Movie();
let seasoned = undefined;
if (tmdb.id && tmdb.title && tmdb.release_date) {
const year = tmdb.release_date.getFullYear();
seasoned = new Movie(tmdb.id, tmdb.title, year);
}
else if (tmdb.id && tmdb.name && tmdb.first_air_date) {
const year = tmdb.first_air_date.getFullYear();
seasoned = new Show(tmdb.id, tmdb.name, year);
seasoned.seasons = tmdb.number_of_season;
seasoned.episodes = tmdb.episodes;
return
}
}
const title = tmdb.title || tmdb.name;
const year = translateYear(tmdb.release_date || tmdb.first_air_date);
const type = manualType || convertType(tmdb.type) || 'movie';
const id = tmdb.id;
const summary = tmdb.overview;
const poster_path = tmdb.poster_path;
const background_path = tmdb.backdrop_path;
const popularity = tmdb.popularity;
const score = tmdb.vote_average;
// const genres = translateGenre(tmdb.genres);
const release_status = tmdb.status;
const tagline = tmdb.tagline;
const seasons = tmdb.number_of_seasons;
const episodes = tmdb.episodes;
const seasoned = new TMDB(
title, year, type, id, summary, poster_path, background_path,
popularity, score, release_status, tagline, seasons, episodes
);
// seasoned.print()
return seasoned;
}
module.exports = convertTmdbToSeasoned;

View File

@@ -1,41 +0,0 @@
const Show = require('src/tmdb/types/show');
function convertTmdbToShow(tmdbShow, credits=undefined) {
const show = new Show(tmdbShow.id, tmdbShow.name)
show.seasons = tmdbShow.number_of_seasons;
show.episodes = tmdbShow.number_of_episodes;
show.overview = tmdbShow.overview;
show.rank = tmdbShow.vote_average;
if (credits) {
show.credits = credits
}
if (tmdbShow.genres !== undefined) {
show.genres = tmdbShow.genres.map(genre => genre.name);
}
if (tmdbShow.first_air_date !== undefined) {
show.first_air_date = new Date(tmdbShow.first_air_date);
show.year = show.first_air_date.getFullYear();
}
if (tmdbShow.poster_path !== undefined) {
show.poster = tmdbShow.poster_path;
}
if (tmdbShow.backdrop_path !== undefined) {
show.backdrop = tmdbShow.backdrop_path;
}
if (tmdbShow.status !== undefined) {
show.status = tmdbShow.status;
}
if (tmdbShow.episode_run_time !== undefined) {
show.runtime = tmdbShow.runtime;
}
return show;
}
module.exports = convertTmdbToShow;

View File

@@ -1,7 +1,7 @@
const moviedb = require('km-moviedb');
const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie');
const convertTmdbToShow = require('src/tmdb/convertTmdbToShow');
const convertTmdbToPerson = require('src/tmdb/convertTmdbToPerson');
const { Movie, Show, Person, Credits, ReleaseDates } = require('src/tmdb/types');
// const { tmdbInfo } = require('src/tmdb/types')
class TMDB {
constructor(cache, apiKey, tmdbLibrary) {
@@ -12,8 +12,11 @@ class TMDB {
movieSearch: 'mos',
showSearch: 'ss',
personSearch: 'ps',
movieInfo: 'mi',
showInfo: 'si',
movieInfo: 'mi',
movieCredits: 'mc',
movieReleaseDates: 'mrd',
showInfo: 'si',
showCredits: 'sc',
personInfo: 'pi',
miscNowPlayingMovies: 'npm',
miscPopularMovies: 'pm',
@@ -25,70 +28,54 @@ class TMDB {
};
}
/**
* Retrieve a specific movie by id from TMDB.
* @param {Number} identifier of the movie you want to retrieve
* @param {String} type filter results by type (default movie).
* @returns {Promise} succeeds if movie was found
*/
lookup(identifier, type = 'movie') {
const query = { id: identifier };
const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(TMDB_METHODS['info'][type], query))
.catch(() => { throw new Error('Could not find a movie with that id.'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => {
try {
return convertTmdbToSeasoned(response, type);
} catch (parseError) {
console.error(parseError);
throw new Error('Could not parse movie.');
}
});
}
/**
* Retrive search results from TMDB.
* @param {String} text query you want to search for
* @param {Number} page representing pagination of results
* @param {String} type filter results by type (default multi)
* @returns {Promise} dict with query results, current page and total_pages
*/
search(text, page = 1, type = 'multi') {
const query = { query: text, page: page };
const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(TMDB_METHODS['search'][type], query))
.catch(() => { throw new Error('Could not search for movies/shows at tmdb.'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapResults(response));
}
/**
* Retrieve a specific movie by id from TMDB.
* @param {Number} identifier of the movie you want to retrieve
* @param {String} type filter results by type (default movie).
* @param {Boolean} add credits (cast & crew) for movie
* @param {Boolean} add release dates for every country
* @returns {Promise} succeeds if movie was found
*/
movieInfo(identifier, credits=false) {
movieInfo(identifier) {
const query = { id: identifier };
const cacheKey = `${this.cacheTags.movieInfo}:${identifier}:${credits}`;
const cacheKey = `${this.cacheTags.movieInfo}:${identifier}`;
const requests = [this.tmdb('movieInfo', query)]
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))
}
if (credits) {
requests.push(this.tmdb('movieCredits', query))
}
/**
* 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 Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => Promise.all(requests))
.catch((error) => { console.log(error); throw new Error('Could not find a movie with that id.'); })
.then(([movies, credits]) => this.cache.set(cacheKey, [movies, credits]))
.then(([movies, credits]) => convertTmdbToMovie(movies, credits))
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))
}
/**
@@ -97,22 +84,26 @@ class TMDB {
* @param {String} type filter results by type (default show).
* @returns {Promise} succeeds if show was found
*/
showInfo(identifier, credits=false) {
showInfo(identifier) {
const query = { id: identifier };
const cacheKey = `${this.cacheTags.showInfo}:${identifier}:${credits}`;
const cacheKey = `${this.cacheTags.showInfo}:${identifier}`;
const requests = [this.tmdb('tvInfo', query)]
return this.cache.get(cacheKey)
.catch(() => this.tmdb('tvInfo', query))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'tv info'))
.then(show => this.cache.set(cacheKey, show, 1))
.then(show => Show.convertFromTmdbResponse(show))
}
if (credits) {
requests.push(this.tmdb('tvCredits', query))
}
showCredits(identifier) {
const query = { id: identifier }
const cacheKey = `${this.cacheTags.showCredits}:${identifier}`
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => Promise.all(requests))
.catch(() => { throw new Error('Could not find a show with that id.'); })
.then(([shows, credits]) => this.cache.set(cacheKey, [shows, credits]))
.then(([shows, credits]) => convertTmdbToShow(shows, credits))
return this.cache.get(cacheKey)
.catch(() => this.tmdb('tvCredits', query))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'show credits'))
.then(credits => this.cache.set(cacheKey, credits, 1))
.then(credits => Credits.convertFromTmdbResponse(credits))
}
/**
@@ -125,25 +116,20 @@ class TMDB {
const query = { id: identifier };
const cacheKey = `${this.cacheTags.personInfo}:${identifier}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => Promise.all([this.tmdb('personInfo', query), this.tmdb('personCombinedCredits', query)]))
.catch(() => { throw new Error('Could not find a person with that id.'); })
.then(([person, cast]) => this.cache.set(cacheKey, [person, cast]))
.then(([person, cast]) => convertTmdbToPerson(person, cast))
.catch(err => new Error('Unable to convert result to person', err))
return this.cache.get(cacheKey)
.catch(() => this.tmdb('personInfo', query))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'person info'))
.then(person => this.cache.set(cacheKey, person, 1))
.then(person => Person.convertFromTmdbResponse(person))
}
multiSearch(search_query, page=1) {
const query = { query: search_query, page: page };
const cacheKey = `${this.cacheTags.multiSearch}:${page}:${search_query}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
return this.cache.get(cacheKey)
.catch(() => this.tmdb('searchMulti', query))
.catch(() => { throw new Error('Could not complete search to tmdb'); })
.then(response => this.cache.set(cacheKey, response))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'search results'))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response));
}
@@ -157,13 +143,11 @@ class TMDB {
const tmdbquery = { query: query, page: page };
const cacheKey = `${this.cacheTags.movieSearch}:${page}:${query}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb('searchMovie', tmdbquery))
.catch(() => { throw new Error('Could not complete movie search to tmdb'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapAndCreateResponse(response, convertTmdbToMovie))
.catch((error) => { console.log(error); throw new Error('Could not parse movie search result') })
return this.cache.get(cacheKey)
.catch(() => this.tmdb('searchMovie', tmdbquery))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'movie search results'))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response, 'movie'))
}
/**
@@ -175,13 +159,12 @@ class TMDB {
showSearch(query, page=1) {
const tmdbquery = { query: query, page: page };
const cacheKey = `${this.cacheTags.showSearch}:${page}:${query}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb('searchTv', tmdbquery))
.catch(() => { throw new Error('Could not complete show search to tmdb'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapAndCreateResponse(response, convertTmdbToShow))
.catch((error) => { console.log(error); throw new Error('Could not parse show search result') })
return this.cache.get(cacheKey)
.catch(() => this.tmdb('searchTv', tmdbquery))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'tv search results'))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response, 'show'))
}
/**
@@ -191,78 +174,36 @@ class TMDB {
* @returns {Promise} dict with query results, current page and total_pages
*/
personSearch(query, page=1) {
const tmdbquery = { query: query, page: page };
const tmdbquery = { query: query, page: page, include_adult: true };
const cacheKey = `${this.cacheTags.personSearch}:${page}:${query}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb('searchPerson', tmdbquery))
.catch(() => { throw new Error('Could not complete person search to tmdb'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapAndCreateResponse(response, convertTmdbToPerson))
.catch((error) => { console.log(error); throw new Error('Could not parse person search result') })
}
mapAndCreateResponse(response, resultConvertFunction) {
// console.log(response)
return {
results: response.results.map(resultConvertFunction),
page: response.page,
total_results: response.total_results,
total_pages: response.total_pages
}
return this.cache.get(cacheKey)
.catch(() => this.tmdb('searchPerson', tmdbquery))
.catch(tmdbError => tmdbErrorResponse(tmdbError, 'person search results'))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response, 'person'))
}
movieList(listname, page = 1) {
const query = { page: page };
const cacheKey = `${this.cacheTags[listname]}:${page}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
return this.cache.get(cacheKey)
.catch(() => this.tmdb(listname, query))
.catch(() => { throw new Error('Unable to get movie list from tmdb')})
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapAndCreateResponse(response, convertTmdbToMovie));
.catch(tmdbError => this.tmdbErrorResponse(tmdbError, 'movie list ' + listname))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response, 'movie'))
}
showList(listname, page = 1) {
const query = { page: page };
const cacheKey = `${this.cacheTags[listname]}:${page}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
return this.cache.get(cacheKey)
.catch(() => this.tmdb(listname, query))
.catch(() => { throw new Error('Unable to get show list from tmdb')})
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapAndCreateResponse(response, convertTmdbToShow));
}
/**
* Fetches a given list from tmdb.
* @param {String} listName Name of list
* @param {String} type filter results by type (default movie)
* @param {Number} page representing pagination of results
* @returns {Promise} dict with query results, current page and total_pages
*/
listSearch(listName, type = 'movie', page = '1') {
const query = { page: page };
console.log(query);
const cacheKey = `${this.cacheTags[listName]}:${type}:${page}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb(TMDB_METHODS[listName][type], query))
.catch(() => { throw new Error('Error fetching list from tmdb.'); })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapResults(response, type));
}
popular(type='movie', page=1) {
const query = { type: type, page: page };
const cacheKey = `${this.cacheTags.popular}:${type}:${page}`;
return Promise.resolve()
.then(() => this.cache.get(cacheKey))
.catch(() => this.tmdb('miscPopularMovies', query))
.catch((e) => { throw new Error(`Error fetching popular list of type ${type} : ${e}`) })
.then(response => this.cache.set(cacheKey, response))
.then(response => this.mapResults(response, type));
.catch(tmdbError => this.tmdbErrorResponse(tmdbError, 'show list ' + listname))
.then(response => this.cache.set(cacheKey, response, 1))
.then(response => this.mapResults(response, 'show'))
}
/**
@@ -271,14 +212,20 @@ class TMDB {
* @param {String} The type declared in listSearch.
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
*/
mapResults(response, _) {
let results = response.results.map((result) => {
if (result.media_type === 'movie') {
return convertTmdbToMovie(result);
} else if (result.media_type === 'tv') {
return convertTmdbToShow(result);
} else if (result.media_type === 'person') {
return convertTmdbToPerson(result);
mapResults(response, type=undefined) {
// console.log(response.results)
// response.results.map(te => console.table(te))
let results = response.results.map(result => {
if (type === 'movie' || result.media_type === 'movie') {
const movie = Movie.convertFromTmdbResponse(result)
return movie.createJsonResponse()
} else if (type === 'show' || result.media_type === 'tv') {
const show = Show.convertFromTmdbResponse(result)
return show.createJsonResponse()
} else if (type === 'person' || result.media_type === 'person') {
const person = Person.convertFromTmdbResponse(result)
return person.createJsonResponse()
}
})
@@ -312,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;

View File

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

View 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 }

View File

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

View 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;

View File

@@ -1,20 +1,62 @@
class Movie {
constructor(id, title, year=null, overview=null, poster=null, backdrop=null, rank=null, genres=null, status=null,
tagline=null, runtime=null, imdb_id=null) {
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.rank = rank;
this.releaseDate = releaseDate;
this.rating = rating;
this.genres = genres;
this.status = status;
this.productionStatus = productionStatus;
this.tagline = tagline;
this.runtime = runtime;
this.imdb_id = imdb_id;
this.popularity = popularity;
this.type = 'movie';
}
static convertFromTmdbResponse(response) {
const { id, title, release_date, overview, poster_path, backdrop_path, 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;

View File

@@ -1,13 +1,37 @@
class Person {
constructor(id, name, poster=null, birthday=null, deathday=null, known_for=null) {
constructor(id, name, poster=undefined, birthday=undefined, deathday=undefined,
adult=undefined, knownForDepartment=undefined) {
this.id = id;
this.name = name;
this.poster = poster;
this.birthday = birthday;
this.deathday = deathday;
this.known_for = known_for;
this.adult = adult;
this.knownForDepartment = knownForDepartment;
this.type = 'person';
}
static convertFromTmdbResponse(response) {
const { id, name, poster, birthday, deathday, adult, known_for_department } = response;
const birthDay = new Date(birthday)
const deathDay = deathday ? new Date(deathday) : null
return new Person(id, name, poster, birthDay, deathDay, adult, known_for_department)
}
createJsonResponse() {
return {
id: this.id,
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;

View File

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

View File

@@ -1,20 +1,50 @@
class Show {
constructor(id, title, year=null, seasons=null, episodes=null, overview=null, rank=null, genres=null,
poster=null, backdrop=null, status=null, runtime=null) {
constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined,
seasons=undefined, episodes=undefined, rank=undefined, genres=undefined, status=undefined,
runtime=undefined) {
this.id = id;
this.title = title;
this.year = year;
this.seasons = seasons;
this.episodes = episodes;
this.overview = overview;
this.rank = rank;
this.genres = genres;
this.poster = poster;
this.backdrop = backdrop;
this.status = status;
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;

View File

@@ -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;

View File

@@ -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)
});
}

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
const express = require('express');
// const reload = require('express-reload');
const Raven = require('raven');
const bodyParser = require('body-parser');
const tokenToUser = require('./middleware/tokenToUser');
@@ -23,13 +24,11 @@ const allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080'];
app.use(bodyParser.urlencoded({ extended: true }));
/* Decode the Authorization header if provided */
router.use(tokenToUser);
app.use(tokenToUser);
// TODO: Should have a separate middleware/router for handling headers.
router.use((req, res, next) => {
// TODO add logging of all incoming
console.log('Request: ', req.originalUrl);
const origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader('Access-Control-Allow-Origin', origin);
@@ -50,6 +49,32 @@ app.use(function onError(err, req, res, next) {
res.end(res.sentry + '\n');
});
/**
* GraphQL
*/
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
// var schema = buildSchema(`
// type Query {
// hello: String
// }`);
const schema = require('./graphql/requests.js');
const roots = { hello: () => 'Hello world!' };
app.use('/graphql', graphqlHTTP({
schema: schema.schema,
graphiql: true
}))
/**
* User
*/
@@ -79,9 +104,14 @@ 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', require('./controllers/info/movieInfo.js'));
router.get('/v2/show/:id', require('./controllers/info/showInfo.js'));
router.get('/v2/person/:id', require('./controllers/info/personInfo.js'));
router.get('/v2/movie/:id/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
*/
@@ -113,13 +143,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
*/

View File

@@ -1,30 +0,0 @@
const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const Plex = require('src/plex/plex');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const plex = new Plex(configuration.get('plex', 'ip'));
/**
* Controller: Retrieve information for a movie
* @param {Request} req http request variable
* @param {Response} res
* @returns {Callback}
*/
async function movieInfoController(req, res) {
const movieId = req.params.id;
const { credits } = req.query;
const movie = await tmdb.movieInfo(movieId, credits);
plex.existsInPlex(movie)
.catch((error) => { console.log('Error when searching plex'); })
.then(() => {
console.log('movie', movie)
res.send(movie);
}).catch((error) => {
res.status(404).send({ success: false, error: error.message });
});
}
module.exports = movieInfoController;

View File

@@ -1,31 +0,0 @@
const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const Plex = require('src/plex/plex');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const plex = new Plex(configuration.get('plex', 'ip'));
/**
* Controller: Retrieve information for a show
* @param {Request} req http request variable
* @param {Response} res
* @returns {Callback}
*/
async function showInfoController(req, res) {
const showId = req.params.id;
const { credits } = req.query;
const show = await tmdb.showInfo(showId, credits);
plex.existsInPlex(show)
.catch((error) => { console.log('Error when searching plex'); })
.then(() => {
console.log('show', show)
res.send(show);
}).catch((error) => {
res.status(404).send({ success: false, error: error.message });
});
}
module.exports = showInfoController;

View File

@@ -16,70 +16,55 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
// + newly created (tv/latest).
// + movie/latest
//
function handleError(error, res) {
const { status, message } = error;
function getTmdbMovieList(res, listname, page) {
Promise.resolve()
.then(() => tmdb.movieList(listname, page))
.then((response) => res.send(response))
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
})
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 getTmdbShowList(res, listname, page) {
Promise.resolve()
.then(() => tmdb.showList(listname, page))
.then((response) => res.send(response))
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
})
function handleListResponse(response, res) {
return res.send(response)
.catch(error => handleError(error, res))
}
exports.nowPlayingMovies = (req, res) => {
function fetchTmdbList(req, res, listname, type) {
const { page } = req.query;
const listname = 'miscNowPlayingMovies'
getTmdbMovieList(res, listname, page);
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)
}
exports.popularMovies = (req, res) => {
const { page } = req.query;
const listname = 'miscPopularMovies'
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')
getTmdbMovieList(res, listname, page);
}
exports.topRatedMovies = (req, res) => {
const { page } = req.query;
const listname = 'miscTopRatedMovies'
getTmdbMovieList(res, listname, page);
}
exports.upcomingMovies = (req, res) => {
const { page } = req.query;
const listname = 'miscUpcomingMovies'
getTmdbMovieList(res, listname, page);
}
exports.nowPlayingShows = (req, res) => {
const { page } = req.query;
const listname = 'tvOnTheAir'
getTmdbShowList(res, listname, page);
}
exports.popularShows = (req, res) => {
const { page } = req.query;
const listname = 'miscPopularTvs'
getTmdbShowList(res, listname, page);
}
exports.topRatedShows = (req, res) => {
const { page } = req.query;
const listname = 'miscTopRatedTvs'
getTmdbShowList(res, listname, page);
module.exports = {
nowPlayingMovies,
popularMovies,
topRatedMovies,
upcomingMovies,
nowPlayingShows,
popularShows,
topRatedShows
}

View 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;

View 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;

View 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;

View File

@@ -13,11 +13,12 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
function personInfoController(req, res) {
const personId = req.params.id;
tmdb.personInfo(personId)
.then((person) => {
res.send(person);
}).catch((error) => {
res.status(404).send({ success: false, error: error.message });
.then(person => res.send(person.createJsonResponse()))
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -5,11 +5,11 @@ 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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -11,14 +11,14 @@ const plex = new Plex(configuration.get('plex', 'ip'));
function searchPlexController(req, res) {
const { query, type } = req.query;
plex.search(query, type)
.then((movies) => {
.then(movies => {
if (movies.length > 0) {
res.send(movies);
} else {
res.status(404).send({ success: false, error: 'Search query did not give any results from plex.'})
res.status(404).send({ success: false, message: 'Search query did not give any results from plex.'})
}
}).catch((error) => {
res.status(500).send({ success: false, error: error.message });
}).catch(error => {
res.status(500).send({ success: false, message: error.message });
});
}

View File

@@ -14,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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -37,7 +37,7 @@ function submitRequestController(req, res) {
console.log('show')
mediaFunction = tmdbShowInfo
} else {
res.status(422).send({ success: false, error: 'Incorrect type. Allowed types: "movie" or "show"'})
res.status(422).send({ success: false, message: 'Incorrect type. Allowed types: "movie" or "show"'})
}
if (mediaFunction === undefined) { res.status(200); return }
@@ -45,7 +45,7 @@ function submitRequestController(req, res) {
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, error: err.message }))
.catch(err => res.status(500).send({ success: false, message: err.message }))
}
module.exports = submitRequestController;

View File

@@ -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 });
});
}

View File

@@ -18,9 +18,9 @@ function fetchAllRequests(req, res) {
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, error: error.message });
.then(result => res.send(result))
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
}

View File

@@ -11,11 +11,10 @@ function fetchAllRequests(req, res) {
const id = req.params.id;
const { type } = req.query;
Promise.resolve()
.then(() => request.getRequestByIdAndType(id, type))
.then((result) => res.send(result))
.catch((error) => {
res.status(404).send({ success: false, error: error.message });
request.getRequestByIdAndType(id, type)
.then(result => res.send(result))
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
}

View File

@@ -22,30 +22,31 @@ const tmdbShowInfo = (id) => {
*/
function requestTmdbIdController(req, res) {
const { id, type } = req.body
console.log('body', req.body)
console.log('id & type', id, type)
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') {
console.log('movie')
mediaFunction = tmdbMovieInfo
} else if (type === 'show') {
console.log('show')
mediaFunction = tmdbShowInfo
} else {
res.status(422).send({ success: false, error: 'Incorrect type. Allowed types: "movie" or "show"'})
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))
// .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.status(501).send({ success: false, error: error.message });
.catch(error => {
res.send({ success: false, message: error.message });
})
}

View File

@@ -1,7 +1,7 @@
const SearchHistory = require('src/searchHistory/searchHistory');
const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const SearchHistory = require('src/searchHistory/searchHistory');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const searchHistory = new SearchHistory();
@@ -16,20 +16,25 @@ function movieSearchController(req, res) {
const user = req.loggedInUser;
const { query, page } = req.query;
Promise.resolve()
.then(() => {
if (user) {
return searchHistory.create(user, query);
}
return null
})
.then(() => tmdb.movieSearch(query, page))
.then((movies) => {
res.send(movies);
})
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
});
if (user) {
return searchHistory.create(user, query);
}
tmdb.movieSearch(query, page)
.then(movieSearchResults => res.send(movieSearchResults))
.catch(error => {
const { status, message } = error;
if (status && message) {
res.status(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;

View File

@@ -1,11 +1,18 @@
const SearchHistory = require('src/searchHistory/searchHistory');
const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const SearchHistory = require('src/searchHistory/searchHistory');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const searchHistory = new SearchHistory();
function checkAndCreateJsonResponse(result) {
if (typeof result['createJsonResponse'] === 'function') {
return result.createJsonResponse()
}
return result
}
/**
* Controller: Search for multi (movies, shows and people by query and pagey
* @param {Request} req http request variable
@@ -16,20 +23,23 @@ function multiSearchController(req, res) {
const user = req.loggedInUser;
const { query, page } = req.query;
Promise.resolve()
.then(() => {
if (user) {
return searchHistory.create(user, query);
if (user) {
searchHistory.create(user, query)
}
return tmdb.multiSearch(query, page)
.then(multiSearchResults => res.send(multiSearchResults))
.catch(error => {
const { status, message } = error;
if (status && message) {
res.status(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}` })
}
return null
})
.then(() => tmdb.multiSearch(query, page))
.then((result) => {
res.send(result);
})
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
});
}
module.exports = multiSearchController;

View File

@@ -1,7 +1,7 @@
const SearchHistory = require('src/searchHistory/searchHistory');
const configuration = require('src/config/configuration').getInstance();
const Cache = require('src/tmdb/cache');
const TMDB = require('src/tmdb/tmdb');
const SearchHistory = require('src/searchHistory/searchHistory');
const cache = new Cache();
const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
const searchHistory = new SearchHistory();
@@ -16,20 +16,27 @@ function personSearchController(req, res) {
const user = req.loggedInUser;
const { query, page } = req.query;
Promise.resolve()
.then(() => {
if (user) {
return searchHistory.create(user, query);
}
return null
})
.then(() => tmdb.personSearch(query, page))
.then((person) => {
res.send(person);
})
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
});
if (user) {
return searchHistory.create(user, query);
}
tmdb.personSearch(query, page)
.then((person) => {
res.send(person);
})
.catch(error => {
const { status, message } = error;
if (status && message) {
res.status(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;

View File

@@ -27,8 +27,8 @@ function showSearchController(req, res) {
.then((shows) => {
res.send(shows);
})
.catch((error) => {
res.status(500).send({ success: false, error: error.message });
.catch(error => {
res.status(500).send({ success: false, message: error.message });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View 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;

View 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -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 });
});
}

View File

@@ -0,0 +1,294 @@
const graphql = require("graphql");
const establishedDatabase = require('src/database/database');
const fetch = require('node-fetch');
const TorrentType = new graphql.GraphQLObjectType({
name: "Torrent",
fields: {
magnet: { type: graphql.GraphQLString },
torrent_name: { type: graphql.GraphQLString},
tmdb_id: { type: graphql.GraphQLString }
}
});
const RequestType = new graphql.GraphQLObjectType({
name: "Request",
fields: {
id: { type: graphql.GraphQLID },
title: { type: graphql.GraphQLString },
year: { type: graphql.GraphQLInt},
poster_path: { type: graphql.GraphQLString },
background_path: { type: graphql.GraphQLString },
requested_by: { type: graphql.GraphQLString },
ip: { type: graphql.GraphQLString },
date: { type: graphql.GraphQLString },
status: { type: graphql.GraphQLString },
user_agent: { type: graphql.GraphQLString },
type: { type: graphql.GraphQLString }
}
});
const RequestsType = new graphql.GraphQLObjectType({
name: 'Requests',
type: graphql.GraphQLList(RequestType),
resolve: (root, args, context, info) => {
return establishedDatabase.all("SELECT * FROM requests;")
.catch(error => console.error("something went wrong fetching 'all' query. Error:", error))
}
})
const ProgressType = new graphql.GraphQLObjectType({
name: 'TorrentProgress',
fields: {
eta: { type: graphql.GraphQLInt },
finished: { type: graphql.GraphQLBoolean },
key: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
progress: { type: graphql.GraphQLFloat },
state: { type: graphql.GraphQLString },
Torrent: {
type: TorrentType,
resolve(parent) {
console.log('prante: ', parent.name)
console.log(parent.name.slice(0,10))
return establishedDatabase.get(`select magnet, torrent_name, tmdb_id from requested_torrent where torrent_name like (?);`, [parent.name.slice(0, 10) + '%'])
}
},
Requested: {
type: graphql.GraphQLList(RequestType),
// resolve: () => fetch('https://api.kevinmidboe.com/api/v2/request?page=1/').then(resp => resp.json())
// .then(data => {
// // console.log('data', data)
// return data.results
// })
resolve: (parent) => {
return establishedDatabase.all("SELECT * FROM requests;")
}
}
}
})
const TorrentsRequestedType = new graphql.GraphQLObjectType({
name: 'TorrentsRequested',
fields: {
magnet: { type: graphql.GraphQLString },
torrent_name: { type: graphql.GraphQLString },
tmdb_id: { type: graphql.GraphQLString },
date_added: { type: graphql.GraphQLString },
Request: {
type: RequestType,
// resolve: () => fetch('https://api.kevinmidboe.com/api/v2/request?page=1/').then(resp => resp.json())
// .then(data => {
// return data.results
// })
resolve(parentValue, args) {
return establishedDatabase.get('select * from requests where id = (?);', [parentValue.tmdb_id])
}
},
Progress: {
type: ProgressType,
resolve(parentValue, args) {
return fetch('http://localhost:5000/')
.then(resp => resp.json())
// .then(data => { console.log('data', data); return data.filter(download => download.name === parentValue.torrent_name) })
}
}
}
})
// create a graphql query to select all and by id
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
//first query to select all
Requests: {
type: graphql.GraphQLList(RequestType),
resolve: (root, args, context, info) => {
return establishedDatabase.all("SELECT * FROM requests;")
.catch(error => console.error("something went wrong fetching 'all' query. Error:", error))
}
},
Progress: {
type: graphql.GraphQLList(ProgressType),
resolve: (root, args, context, info) => {
console.log('user', context.loggedInUser)
return fetch('http://localhost:5000')
.then(resp => resp.json())
}
},
ProgressRequested: {
type: graphql.GraphQLList(ProgressType),
resolve: (root, args, context, info) => {
console.log('root & args', root, args)
}
},
TorrentsRequested: {
type: graphql.GraphQLList(TorrentsRequestedType),
resolve: (root, args, context, info) => {
return establishedDatabase.all("SELECT * FROM requested_torrent;")
.then(data => data.filter(request => { if (request.tmdb_id === '83666') { console.log('request', request, root);}; return request }))
.catch(error => console.error("something went wrong fetching 'all' query. Error:", error))
}
},
DownloadingRequestByName: {
type: RequestType,
args:{
name:{
type: new graphql.GraphQLNonNull(graphql.GraphQLString)
}
},
resolve: (root, { name }, context, info) => {
return establishedDatabase.all("SELECT * FROM requests where requested_by = (?);", [name])
.catch(error => console.error("something went wrong fetching 'all' query. Error:", error))
}
},
//second query to select by id
Request:{
type: RequestType,
args:{
id:{
type: new graphql.GraphQLNonNull(graphql.GraphQLID)
}
},
resolve: (root, {id}, context, info) => {
return establishedDatabase.get("SELECT * FROM requests WHERE id = (?);",[id])
.catch(error => console.error(`something went wrong fetching by id: '${ id }'. Error: ${ error }`))
}
},
Torrents: {
type: graphql.GraphQLList(TorrentType),
resolve: (root, {id}, context, info) => {
// console.log('parent', parent)
return establishedDatabase.all("SELECT * FROM requested_torrent")
.catch(error => console.error(`something went wrong fetching all torrents. Error: ${ error }`))
}
},
Torrent: {
type: TorrentType,
args: {
id: {
type: new graphql.GraphQLNonNull(graphql.GraphQLID)
}
},
resolve: (parent, {id}, context, info) => {
// console.log('searcing from parent', parent)
return establishedDatabase.get("SELECT * FROM requested_torrent WHERE tmdb_id = (?);", [id])
}
}
}
});
//mutation type is a type of object to modify data (INSERT,DELETE,UPDATE)
// var mutationType = new graphql.GraphQLObjectType({
// name: 'Mutation',
// fields: {
// //mutation for creacte
// createPost: {
// //type of object to return after create in SQLite
// type: PostType,
// //argument of mutation creactePost to get from request
// args: {
// title: {
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// description:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// createDate:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// author:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// }
// },
// resolve: (root, {title, description, createDate, author}) => {
// return new Promise((resolve, reject) => {
// //raw SQLite to insert a new post in post table
// database.run('INSERT INTO Posts (title, description, createDate, author) VALUES (?,?,?,?);', [title, description, createDate, author], (err) => {
// if(err) {
// reject(null);
// }
// database.get("SELECT last_insert_rowid() as id", (err, row) => {
// resolve({
// id: row["id"],
// title: title,
// description: description,
// createDate:createDate,
// author: author
// });
// });
// });
// })
// }
// },
// //mutation for update
// updatePost: {
// //type of object to return afater update in SQLite
// type: graphql.GraphQLString,
// //argument of mutation creactePost to get from request
// args:{
// id:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLID)
// },
// title: {
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// description:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// createDate:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// },
// author:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLString)
// }
// },
// resolve: (root, {id, title, description, createDate, author}) => {
// return new Promise((resolve, reject) => {
// //raw SQLite to update a post in post table
// database.run('UPDATE Posts SET title = (?), description = (?), createDate = (?), author = (?) WHERE id = (?);', [title, description, createDate, author, id], (err) => {
// if(err) {
// reject(err);
// }
// resolve(`Post #${id} updated`);
// });
// })
// }
// },
// //mutation for update
// deletePost: {
// //type of object resturn after delete in SQLite
// type: graphql.GraphQLString,
// args:{
// id:{
// type: new graphql.GraphQLNonNull(graphql.GraphQLID)
// }
// },
// resolve: (root, {id}) => {
// return new Promise((resolve, reject) => {
// //raw query to delete from post table by id
// database.run('DELETE from Posts WHERE id =(?);', [id], (err) => {
// if(err) {
// reject(err);
// }
// resolve(`Post #${id} deleted`);
// });
// })
// }
// }
// }
// });
//define schema with post object, queries, and mustation
const schema = new graphql.GraphQLSchema({
query: queryType,
// mutation: mutationType
});
//export schema to use on index.js
module.exports = {
schema
}

View File

@@ -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;

View File

@@ -1 +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}
[{"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}]

View File

@@ -0,0 +1,6 @@
{
"page":1,
"results":[],
"total_results":0,
"total_pages":1
}

File diff suppressed because one or more lines are too long

View 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;

View File

@@ -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(() =>

View File

@@ -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"}'))
);
});

View File

@@ -7,17 +7,17 @@ const createToken = require('test/helpers/createToken');
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('mi:335984', infoMovieSuccess));
before(() => createCacheEntry('mi:335984:false', infoMovieSuccess));
it('should return 200 when item is requested', () =>
request(app)
.post('/api/v2/request')
.set('authorization', createToken('test_user', 'secret'))
.send({ id: 335984, type: 'movie' })
.set('Authorization', createToken('test_user', 'secret'))
.expect(200)
);
});

View File

@@ -1,28 +1,29 @@
const assert = require('assert');
const convertTmdbToMovie = require('src/tmdb/convertTmdbToMovie');
// 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);
beforeEach(() => [this.bladeRunnerTmdbMovie] = bladeRunnerQuerySuccess);
it('should translate the tmdb release date to movie year', () => {
const bladeRunner = convertTmdbToMovie(this.bladeRunnerTmdbMovie);
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
assert.strictEqual(bladeRunner.year, 2017);
});
it('should translate the tmdb release date to instance of Date', () => {
const bladeRunner = convertTmdbToMovie(this.bladeRunnerTmdbMovie);
assert(bladeRunner.release_date instanceof Date);
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
assert(bladeRunner.releaseDate instanceof Date);
});
it('should translate the tmdb title to title', () => {
const bladeRunner = convertTmdbToMovie(this.bladeRunnerTmdbMovie);
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
assert.equal(bladeRunner.title, 'Blade Runner 2049');
});
it('should translate the tmdb vote_average to rank', () => {
const bladeRunner = convertTmdbToMovie(this.bladeRunnerTmdbMovie);
assert.equal(bladeRunner.rank, 7.3);
it('should translate the tmdb vote_average to rating', () => {
const bladeRunner = Movie.convertFromTmdbResponse(this.bladeRunnerTmdbMovie);
assert.equal(bladeRunner.rating, 7.3);
});

View File

@@ -21,7 +21,7 @@ describe('TMDB', function test() {
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-api-key', this.mockMoviedb);
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