Fix: Linter warnings #137
| @@ -1,14 +1,20 @@ | ||||
| { | ||||
| 	"root": true, | ||||
| 	"parserOptions": { | ||||
| 		"ecmaVersion": 2020, | ||||
| 		"sourceType": "module" | ||||
| 	}, | ||||
| 	"extends": [ | ||||
| 		"eslint-config-airbnb-base", | ||||
| 		"plugin:prettier/recommended" | ||||
| 	], | ||||
| 	"extends": ["eslint-config-airbnb-base", "plugin:prettier/recommended"], | ||||
| 	"rules": { | ||||
| 		"no-underscore-dangle": "off", | ||||
| 		"no-shadow": "off" | ||||
| 		"max-classes-per-file": 1, | ||||
| 		"no-empty": [ | ||||
| 			2, | ||||
| 			{ | ||||
| 				"allowEmptyCatch": true | ||||
| 			} | ||||
| 		], | ||||
| 		"no-promise-executor-return": 1, | ||||
| 		"no-shadow": "off", | ||||
| 		"no-underscore-dangle": "off" | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										30
									
								
								src/cache/redis.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								src/cache/redis.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,32 +1,31 @@ | ||||
| const { promisify } = require("util"); | ||||
| const configuration = require("../config/configuration").getInstance(); | ||||
|  | ||||
| let client; | ||||
|  | ||||
| try { | ||||
|   const redis = require("redis"); | ||||
|   console.log("Trying to connect with redis.."); | ||||
|   const redis = require("redis"); // eslint-disable-line global-require | ||||
|   console.log("Trying to connect with redis.."); // eslint-disable-line no-console | ||||
|   const host = configuration.get("redis", "host"); | ||||
|   const port = configuration.get("redis", "port"); | ||||
|  | ||||
|   console.log(`redis://${host}:${port}`); | ||||
|   console.log(`redis://${host}:${port}`); // eslint-disable-line no-console | ||||
|   client = redis.createClient({ | ||||
|     url: `redis://${host}:${port}` | ||||
|   }); | ||||
|  | ||||
|   client.on("connect", () => console.log("Redis connection established!")); | ||||
|   client.on("connect", () => console.log("Redis connection established!")); // eslint-disable-line no-console | ||||
|  | ||||
|   client.on("error", function (err) { | ||||
|   client.on("error", () => { | ||||
|     client.quit(); | ||||
|     console.error("Unable to connect to redis, setting up redis-mock."); | ||||
|     console.error("Unable to connect to redis, setting up redis-mock."); // eslint-disable-line no-console | ||||
|  | ||||
|     client = { | ||||
|       get: function () { | ||||
|         console.log("redis-dummy get", arguments[0]); | ||||
|       get(command) { | ||||
|         console.log(`redis-dummy get: ${command}`); // eslint-disable-line no-console | ||||
|         return Promise.resolve(); | ||||
|       }, | ||||
|       set: function () { | ||||
|         console.log("redis-dummy set", arguments[0]); | ||||
|       set(command) { | ||||
|         console.log(`redis-dummy set: ${command}`); // eslint-disable-line no-console | ||||
|         return Promise.resolve(); | ||||
|       } | ||||
|     }; | ||||
| @@ -38,10 +37,11 @@ function set(key, value, TTL = 10800) { | ||||
|  | ||||
|   const json = JSON.stringify(value); | ||||
|   client.set(key, json, (error, reply) => { | ||||
|     if (reply == "OK") { | ||||
|     if (reply === "OK") { | ||||
|       // successfully set value with key, now set TTL for key | ||||
|       client.expire(key, TTL, e => { | ||||
|         if (e) | ||||
|           // eslint-disable-next-line no-console | ||||
|           console.error( | ||||
|             "Unexpected error while setting expiration for key:", | ||||
|             key, | ||||
| @@ -55,14 +55,14 @@ function set(key, value, TTL = 10800) { | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| function get() { | ||||
| function get(key) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|     client.get(key, (error, reply) => { | ||||
|       if (reply == null) { | ||||
|       if (reply === null) { | ||||
|         return reject(); | ||||
|       } | ||||
|  | ||||
|       resolve(JSON.parse(reply)); | ||||
|       return resolve(JSON.parse(reply)); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| const path = require("path"); | ||||
| const Field = require("./field.js"); | ||||
| const Field = require("./field"); | ||||
|  | ||||
| let instance = null; | ||||
|  | ||||
| class Config { | ||||
|   constructor() { | ||||
|     this.location = Config.determineLocation(); | ||||
|     // eslint-disable-next-line import/no-dynamic-require, global-require | ||||
|     this.fields = require(`${this.location}`); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| class EnvironmentVariables { | ||||
|    constructor(variables) { | ||||
|       this.variables = variables || process.env; | ||||
|    } | ||||
|   constructor(variables) { | ||||
|     this.variables = variables || process.env; | ||||
|   } | ||||
|  | ||||
|    get(variable) { | ||||
|       return this.variables[variable]; | ||||
|    } | ||||
|   get(variable) { | ||||
|     return this.variables[variable]; | ||||
|   } | ||||
|  | ||||
|    has(variable) { | ||||
|       return this.get(variable) !== undefined; | ||||
|    } | ||||
|   has(variable) { | ||||
|     return this.get(variable) !== undefined; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = EnvironmentVariables; | ||||
|   | ||||
| @@ -1,49 +1,53 @@ | ||||
| const Filters = require('./filters.js'); | ||||
| const EnvironmentVariables = require('./environmentVariables.js'); | ||||
| const Filters = require("./filters"); | ||||
| const EnvironmentVariables = require("./environmentVariables"); | ||||
|  | ||||
| class Field { | ||||
|    constructor(rawValue, environmentVariables) { | ||||
|       this.rawValue = rawValue; | ||||
|       this.filters = new Filters(rawValue); | ||||
|       this.valueWithoutFilters = this.filters.removeFiltersFromValue(); | ||||
|       this.environmentVariables = new EnvironmentVariables(environmentVariables); | ||||
|    } | ||||
|  | ||||
|    get value() { | ||||
|       if (this.filters.isEmpty()) { | ||||
|          return this.valueWithoutFilters; | ||||
|       } | ||||
|  | ||||
|       if (this.filters.has('base64') && !this.filters.has('env')) { | ||||
|          return Field.base64Decode(this.valueWithoutFilters); | ||||
|       } | ||||
|  | ||||
|       if (this.environmentVariables.has(this.valueWithoutFilters) && | ||||
|           this.environmentVariables.get(this.valueWithoutFilters) === '') { | ||||
|          return undefined; | ||||
|       } | ||||
|  | ||||
|       if (!this.filters.has('base64') && this.filters.has('env')) { | ||||
|          if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|             return this.environmentVariables.get(this.valueWithoutFilters); | ||||
|          } | ||||
|          return undefined; | ||||
|       } | ||||
|  | ||||
|       if (this.filters.has('env') && this.filters.has('base64')) { | ||||
|          if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|             const encodedEnvironmentVariable = this.environmentVariables.get(this.valueWithoutFilters); | ||||
|             return Field.base64Decode(encodedEnvironmentVariable); | ||||
|          } | ||||
|          return undefined; | ||||
|       } | ||||
|   constructor(rawValue, environmentVariables) { | ||||
|     this.rawValue = rawValue; | ||||
|     this.filters = new Filters(rawValue); | ||||
|     this.valueWithoutFilters = this.filters.removeFiltersFromValue(); | ||||
|     this.environmentVariables = new EnvironmentVariables(environmentVariables); | ||||
|   } | ||||
|  | ||||
|   get value() { | ||||
|     if (this.filters.isEmpty()) { | ||||
|       return this.valueWithoutFilters; | ||||
|    } | ||||
|     } | ||||
|  | ||||
|    static base64Decode(string) { | ||||
|       return new Buffer(string, 'base64').toString('utf-8'); | ||||
|    } | ||||
|     if (this.filters.has("base64") && !this.filters.has("env")) { | ||||
|       return Field.base64Decode(this.valueWithoutFilters); | ||||
|     } | ||||
|  | ||||
|     if ( | ||||
|       this.environmentVariables.has(this.valueWithoutFilters) && | ||||
|       this.environmentVariables.get(this.valueWithoutFilters) === "" | ||||
|     ) { | ||||
|       return undefined; | ||||
|     } | ||||
|  | ||||
|     if (!this.filters.has("base64") && this.filters.has("env")) { | ||||
|       if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|         return this.environmentVariables.get(this.valueWithoutFilters); | ||||
|       } | ||||
|       return undefined; | ||||
|     } | ||||
|  | ||||
|     if (this.filters.has("env") && this.filters.has("base64")) { | ||||
|       if (this.environmentVariables.has(this.valueWithoutFilters)) { | ||||
|         const encodedEnvironmentVariable = this.environmentVariables.get( | ||||
|           this.valueWithoutFilters | ||||
|         ); | ||||
|         return Field.base64Decode(encodedEnvironmentVariable); | ||||
|       } | ||||
|       return undefined; | ||||
|     } | ||||
|  | ||||
|     return this.valueWithoutFilters; | ||||
|   } | ||||
|  | ||||
|   static base64Decode(string) { | ||||
|     return Buffer.from(string, "base64").toString("utf-8"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Field; | ||||
|   | ||||
| @@ -1,34 +1,34 @@ | ||||
| class Filters { | ||||
|    constructor(value) { | ||||
|       this.value = value; | ||||
|       this.delimiter = '|'; | ||||
|    } | ||||
|   constructor(value) { | ||||
|     this.value = value; | ||||
|     this.delimiter = "|"; | ||||
|   } | ||||
|  | ||||
|    get filters() { | ||||
|       return this.value.split(this.delimiter).slice(0, -1); | ||||
|    } | ||||
|   get filters() { | ||||
|     return this.value.split(this.delimiter).slice(0, -1); | ||||
|   } | ||||
|  | ||||
|    isEmpty() { | ||||
|       return !this.hasValidType() || this.value.length === 0; | ||||
|    } | ||||
|   isEmpty() { | ||||
|     return !this.hasValidType() || this.value.length === 0; | ||||
|   } | ||||
|  | ||||
|    has(filter) { | ||||
|       return this.filters.includes(filter); | ||||
|    } | ||||
|   has(filter) { | ||||
|     return this.filters.includes(filter); | ||||
|   } | ||||
|  | ||||
|    hasValidType() { | ||||
|       return (typeof this.value === 'string'); | ||||
|    } | ||||
|   hasValidType() { | ||||
|     return typeof this.value === "string"; | ||||
|   } | ||||
|  | ||||
|    removeFiltersFromValue() { | ||||
|       if (this.hasValidType() === false) { | ||||
|          return this.value; | ||||
|       } | ||||
|   removeFiltersFromValue() { | ||||
|     if (this.hasValidType() === false) { | ||||
|       return this.value; | ||||
|     } | ||||
|  | ||||
|       let filtersCombined = this.filters.join(this.delimiter); | ||||
|       filtersCombined += this.filters.length >= 1 ? this.delimiter : ''; | ||||
|       return this.value.replace(filtersCombined, ''); | ||||
|    } | ||||
|     let filtersCombined = this.filters.join(this.delimiter); | ||||
|     filtersCombined += this.filters.length >= 1 ? this.delimiter : ""; | ||||
|     return this.value.replace(filtersCombined, ""); | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Filters; | ||||
|   | ||||
| @@ -72,12 +72,11 @@ class SqliteDatabase { | ||||
|   /** | ||||
|    * Run a SQL query against the database and retrieve the status. | ||||
|    * @param {String} sql SQL query | ||||
|    * @param {Array} parameters in the SQL query | ||||
|    * @returns {Promise} | ||||
|    */ | ||||
|   execute(sql) { | ||||
|     return new Promise(resolve => { | ||||
|       this.connection.exec(sql, (err, database) => { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       this.connection.exec(sql, err => { | ||||
|         if (err) { | ||||
|           console.log("ERROR: ", err); | ||||
|           reject(err); | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
|  | ||||
| class GitRepository { | ||||
|    static dumpHook(body) { | ||||
|       /* eslint-disable no-console */ | ||||
|       console.log(body); | ||||
|    } | ||||
|   static dumpHook(body) { | ||||
|     /* eslint-disable no-console */ | ||||
|     console.log(body); | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = GitRepository; | ||||
|   | ||||
| @@ -1,19 +1,18 @@ | ||||
|  | ||||
| class Media { | ||||
|    constructor(title, year, type) { | ||||
|       this.title = title; | ||||
|       this.year = year; | ||||
|       this.type = type; | ||||
|    } | ||||
|   constructor(title, year, type) { | ||||
|     this.title = title; | ||||
|     this.year = year; | ||||
|     this.type = type; | ||||
|   } | ||||
|  | ||||
|    toString() { | ||||
|       return `N: ${this.title} | Y: ${this.year} | T: ${this.type}`; | ||||
|    } | ||||
|   toString() { | ||||
|     return `N: ${this.title} | Y: ${this.year} | T: ${this.type}`; | ||||
|   } | ||||
|  | ||||
|    print() { | ||||
|       /* eslint-disable no-console */ | ||||
|       console.log(this.toString()); | ||||
|    } | ||||
|   print() { | ||||
|     /* eslint-disable no-console */ | ||||
|     console.log(this.toString()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Media; | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| class MediaInfo { | ||||
|    constructor() { | ||||
|       this.duration = undefined; | ||||
|       this.height = undefined; | ||||
|       this.width = undefined; | ||||
|       this.bitrate = undefined; | ||||
|       this.resolution = undefined; | ||||
|       this.framerate = undefined; | ||||
|       this.protocol = undefined; | ||||
|       this.container = undefined; | ||||
|       this.audioCodec = undefined; | ||||
|    } | ||||
|   constructor() { | ||||
|     this.duration = undefined; | ||||
|     this.height = undefined; | ||||
|     this.width = undefined; | ||||
|     this.bitrate = undefined; | ||||
|     this.resolution = undefined; | ||||
|     this.framerate = undefined; | ||||
|     this.protocol = undefined; | ||||
|     this.container = undefined; | ||||
|     this.audioCodec = undefined; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = MediaInfo; | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| class Player { | ||||
|    constructor(device, address) { | ||||
|       this.device = device; | ||||
|       this.ip = address; | ||||
|       this.platform = undefined; | ||||
|       this.product = undefined; | ||||
|       this.title = undefined; | ||||
|       this.state = undefined; | ||||
|    } | ||||
|   constructor(device, address) { | ||||
|     this.device = device; | ||||
|     this.ip = address; | ||||
|     this.platform = undefined; | ||||
|     this.product = undefined; | ||||
|     this.title = undefined; | ||||
|     this.state = undefined; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Player; | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| const Media = require("./media"); | ||||
|  | ||||
| class Plex extends Media { | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| const Media = require("./media"); | ||||
|  | ||||
| class TMDB extends Media { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| class User { | ||||
|    constructor(id, title) { | ||||
|       this.id = id; | ||||
|       this.title = title; | ||||
|    } | ||||
|   constructor(id, title) { | ||||
|     this.id = id; | ||||
|     this.title = title; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = User; | ||||
|   | ||||
| @@ -1,12 +1,21 @@ | ||||
| const request = require("request"); | ||||
| const configuration = require("../config/configuration").getInstance(); | ||||
|  | ||||
| class SMSUnexpectedError extends Error { | ||||
|   constructor(errorMessage) { | ||||
|     const message = "Unexpected error from sms provider."; | ||||
|     super(message); | ||||
|  | ||||
|     this.errorMessage = errorMessage; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const sendSMS = message => { | ||||
|   const apiKey = configuration.get("sms", "apikey"); | ||||
|  | ||||
|   if (!apiKey) { | ||||
|     console.warning("api key for sms not set, cannot send sms."); | ||||
|     return null; | ||||
|     console.warning("api key for sms not set, cannot send sms."); // eslint-disable-line no-console | ||||
|     return Promise.resolve(null); | ||||
|   } | ||||
|  | ||||
|   const sender = configuration.get("sms", "sender"); | ||||
| @@ -23,10 +32,9 @@ const sendSMS = message => { | ||||
|           recipients: [{ msisdn: `47${recipient}` }] | ||||
|         } | ||||
|       }, | ||||
|       function (err, r, body) { | ||||
|         console.log(err ? err : body); | ||||
|         console.log("sms provider response:", body); | ||||
|         resolve(); | ||||
|       (err, r, body) => { | ||||
|         if (err) reject(new SMSUnexpectedError(err || body)); | ||||
|         resolve(body); | ||||
|       } | ||||
|     ); | ||||
|   }); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| const assert = require("assert"); | ||||
| const http = require("http"); | ||||
| const { URL } = require("url"); | ||||
| const PythonShell = require("python-shell"); | ||||
| @@ -8,12 +7,12 @@ const establishedDatabase = require("../database/database"); | ||||
| const cache = require("../cache/redis"); | ||||
|  | ||||
| function getMagnetFromURL(url) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|   return new Promise(resolve => { | ||||
|     const options = new URL(url); | ||||
|     if (options.protocol.includes("magnet")) resolve(url); | ||||
|  | ||||
|     http.get(options, res => { | ||||
|       if (res.statusCode == 301 || res.statusCode == 302) { | ||||
|       if (res.statusCode === 301 || res.statusCode === 302) { | ||||
|         resolve(res.headers.location); | ||||
|       } | ||||
|     }); | ||||
| @@ -43,13 +42,14 @@ async function callPythonAddMagnet(url, callback) { | ||||
|       PythonShell.run("deluge_cli.py", options, callback); | ||||
|     }) | ||||
|     .catch(err => { | ||||
|       console.log(err); | ||||
|       throw new Error(err); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function SearchPiratebay(query) { | ||||
|   if (query && query.includes("+")) { | ||||
| async function SearchPiratebay(_query) { | ||||
|   let query = String(_query); | ||||
|  | ||||
|   if (query?.includes("+")) { | ||||
|     query = query.replace("+", "%20"); | ||||
|   } | ||||
|  | ||||
| @@ -62,7 +62,7 @@ async function SearchPiratebay(query) { | ||||
|       .catch(() => | ||||
|         find(query, (err, results) => { | ||||
|           if (err) { | ||||
|             console.log("THERE WAS A FUCKING ERROR!\n", err); | ||||
|             console.log("THERE WAS A FUCKING ERROR!\n", err); // eslint-disable-line no-console | ||||
|             reject(Error("There was a error when searching for torrents")); | ||||
|           } | ||||
|  | ||||
| @@ -76,8 +76,8 @@ async function SearchPiratebay(query) { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| async function AddMagnet(magnet, name, tmdb_id) { | ||||
|   return await new Promise((resolve, reject) => | ||||
| function AddMagnet(magnet, name, tmdbId) { | ||||
|   return new Promise((resolve, reject) => | ||||
|     callPythonAddMagnet(magnet, (err, results) => { | ||||
|       if (err) { | ||||
|         /* eslint-disable no-console */ | ||||
| @@ -87,13 +87,12 @@ async function AddMagnet(magnet, name, tmdb_id) { | ||||
|       /* eslint-disable no-console */ | ||||
|       console.log("result/error:", err, results); | ||||
|  | ||||
|       database = establishedDatabase; | ||||
|       insert_query = | ||||
|         "INSERT INTO requested_torrent(magnet,torrent_name,tmdb_id) \ | ||||
|          VALUES (?,?,?)"; | ||||
|       const database = establishedDatabase; | ||||
|       const insertQuery = | ||||
|         "INSERT INTO requested_torrent(magnet,torrent_name,tmdb_id) VALUES (?,?,?)"; | ||||
|  | ||||
|       let response = database.run(insert_query, [magnet, name, tmdb_id]); | ||||
|       console.log("Response from requsted_torrent insert: " + response); | ||||
|       const response = database.run(insertQuery, [magnet, name, tmdbId]); | ||||
|       console.log(`Response from requsted_torrent insert: ${response}`); | ||||
|  | ||||
|       resolve({ success: true }); | ||||
|     }) | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| const Plex = require("../media_classes/plex"); | ||||
|  | ||||
| function translateAdded(date_string) { | ||||
| @@ -5,10 +7,10 @@ function translateAdded(date_string) { | ||||
| } | ||||
|  | ||||
| function convertPlexToSeasoned(plex) { | ||||
|   const title = plex.title; | ||||
|   const year = plex.year; | ||||
|   const type = plex.type; | ||||
|   const summary = plex.summary; | ||||
|   const { title } = plex; | ||||
|   const { year } = plex; | ||||
|   const { type } = plex; | ||||
|   const { summary } = plex; | ||||
|   const poster_path = plex.thumb; | ||||
|   const background_path = plex.art; | ||||
|   const added = translateAdded(plex.addedAt); | ||||
| @@ -27,7 +29,6 @@ function convertPlexToSeasoned(plex) { | ||||
|     seasons, | ||||
|     episodes | ||||
|   ); | ||||
|   // seasoned.print(); | ||||
|   return seasoned; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| const configuration = require("../config/configuration").getInstance(); | ||||
|  | ||||
| function hookDumpController(req, res) { | ||||
|   console.log(req); | ||||
| } | ||||
|  | ||||
| module.exports = hookDumpController; | ||||
| @@ -1,25 +1,25 @@ | ||||
| class mailTemplate { | ||||
|    constructor(mediaItem) { | ||||
|       this.mediaItem = mediaItem; | ||||
|       this.posterURL = 'https://image.tmdb.org/t/p/w600'; | ||||
|    } | ||||
|   constructor(mediaItem) { | ||||
|     this.mediaItem = mediaItem; | ||||
|     this.posterURL = "https://image.tmdb.org/t/p/w600"; | ||||
|   } | ||||
|  | ||||
|    toText() { | ||||
|       return `${this.mediaItem.title} (${this.mediaItem.year})`; // plain text body | ||||
|    } | ||||
|   toText() { | ||||
|     return `${this.mediaItem.title} (${this.mediaItem.year})`; // plain text body | ||||
|   } | ||||
|  | ||||
|    toHTML() { | ||||
|       const info = { | ||||
|          name: this.mediaItem.title, | ||||
|          year: `(${this.mediaItem.year})`, | ||||
|          poster: this.posterURL + this.mediaItem.poster, | ||||
|       }; | ||||
|   toHTML() { | ||||
|     const info = { | ||||
|       name: this.mediaItem.title, | ||||
|       year: `(${this.mediaItem.year})`, | ||||
|       poster: this.posterURL + this.mediaItem.poster | ||||
|     }; | ||||
|  | ||||
|       return ` | ||||
|     return ` | ||||
|          <h1>${info.name} ${info.year}</h1> | ||||
|          <img src="${info.poster}"> | ||||
|       `; | ||||
|    } | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = mailTemplate; | ||||
|   | ||||
							
								
								
									
										203
									
								
								src/plex/plex.js
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								src/plex/plex.js
									
									
									
									
									
								
							| @@ -2,58 +2,101 @@ const fetch = require("node-fetch"); | ||||
| const convertPlexToMovie = require("./convertPlexToMovie"); | ||||
| const convertPlexToShow = require("./convertPlexToShow"); | ||||
| const convertPlexToEpisode = require("./convertPlexToEpisode"); | ||||
|  | ||||
| const { Movie, Show, Person } = require("../tmdb/types"); | ||||
|  | ||||
| const redisCache = require("../cache/redis"); | ||||
|  | ||||
| const sanitize = string => string.toLowerCase().replace(/[^\w]/gi, ""); | ||||
| class PlexRequestTimeoutError extends Error { | ||||
|   constructor() { | ||||
|     const message = "Timeout: Plex did not respond."; | ||||
|  | ||||
| function fixedEncodeURIComponent(str) { | ||||
|   return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { | ||||
|     return "%" + c.charCodeAt(0).toString(16).toUpperCase(); | ||||
|   }); | ||||
|     super(message); | ||||
|     this.statusCode = 408; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const matchingTitleAndYear = (plex, tmdb) => { | ||||
|   let matchingTitle, matchingYear; | ||||
| class PlexUnexpectedError extends Error { | ||||
|   constructor(plexError = null) { | ||||
|     const message = "Unexpected plex error occured."; | ||||
|  | ||||
|   if (plex["title"] != null && tmdb["title"] != null) { | ||||
|     super(message); | ||||
|     this.statusCode = 500; | ||||
|     this.plexError = plexError; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const sanitize = string => string.toLowerCase().replace(/[^\w]/gi, ""); | ||||
| const matchingTitleAndYear = (plex, tmdb) => { | ||||
|   let matchingTitle; | ||||
|   let matchingYear; | ||||
|  | ||||
|   if (plex?.title && tmdb?.title) { | ||||
|     const plexTitle = sanitize(plex.title); | ||||
|     const tmdbTitle = sanitize(tmdb.title); | ||||
|     matchingTitle = plexTitle == tmdbTitle; | ||||
|     matchingTitle = matchingTitle | ||||
|       ? matchingTitle | ||||
|       : plexTitle.startsWith(tmdbTitle); | ||||
|     matchingTitle = plexTitle === tmdbTitle; | ||||
|     matchingTitle = matchingTitle || plexTitle.startsWith(tmdbTitle); | ||||
|   } else matchingTitle = false; | ||||
|  | ||||
|   if (plex["year"] != null && tmdb["year"] != null) | ||||
|     matchingYear = plex.year == tmdb.year; | ||||
|   if (plex?.year && tmdb?.year) matchingYear = plex.year === tmdb.year; | ||||
|   else matchingYear = false; | ||||
|  | ||||
|   return matchingTitle && matchingYear; | ||||
| }; | ||||
|  | ||||
| const successfullResponse = response => { | ||||
|   if (response && response["MediaContainer"]) return response; | ||||
| function fixedEncodeURIComponent(str) { | ||||
|   return encodeURIComponent(str).replace(/[!'()*]/g, c => { | ||||
|     return `%${c.charCodeAt(0).toString(16).toUpperCase()}`; | ||||
|   }); | ||||
| } | ||||
|  | ||||
|   if ( | ||||
|     response == null || | ||||
|     response["status"] == null || | ||||
|     response["statusText"] == null | ||||
|   ) { | ||||
|     throw Error("Unable to decode response"); | ||||
|   } | ||||
| function matchTmdbAndPlexMedia(plex, tmdb) { | ||||
|   let match; | ||||
|  | ||||
|   const { status, statusText } = response; | ||||
|   if (plex === null || tmdb === null) return false; | ||||
|  | ||||
|   if (status === 200) { | ||||
|     return response.json(); | ||||
|   if (plex instanceof Array) { | ||||
|     const possibleMatches = plex.map(plexItem => | ||||
|       matchingTitleAndYear(plexItem, tmdb) | ||||
|     ); | ||||
|     match = possibleMatches.includes(true); | ||||
|   } else { | ||||
|     throw { message: statusText, status: status }; | ||||
|     match = matchingTitleAndYear(plex, tmdb); | ||||
|   } | ||||
|  | ||||
|   return match; | ||||
| } | ||||
|  | ||||
| const successfullResponse = response => { | ||||
|   const { status, statusText } = response; | ||||
|   if (status !== 200) { | ||||
|     throw new PlexUnexpectedError(statusText); | ||||
|   } | ||||
|  | ||||
|   if (response?.MediaContainer) return response; | ||||
|  | ||||
|   return response.json(); | ||||
| }; | ||||
|  | ||||
| function mapResults(response) { | ||||
|   if (response?.MediaContainer?.Hub === null) { | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
|   return response.MediaContainer.Hub.filter(category => category.size > 0) | ||||
|     .map(category => { | ||||
|       if (category.type === "movie") { | ||||
|         return category.Metadata.map(convertPlexToMovie); | ||||
|       } | ||||
|       if (category.type === "show") { | ||||
|         return category.Metadata.map(convertPlexToShow); | ||||
|       } | ||||
|       if (category.type === "episode") { | ||||
|         return category.Metadata.map(convertPlexToEpisode); | ||||
|       } | ||||
|  | ||||
|       return null; | ||||
|     }) | ||||
|     .filter(result => result !== null); | ||||
| } | ||||
|  | ||||
| class Plex { | ||||
|   constructor(ip, port = 32400, cache = null) { | ||||
|     this.plexIP = ip; | ||||
| @@ -77,50 +120,29 @@ class Plex { | ||||
|     return new Promise((resolve, reject) => | ||||
|       this.cache | ||||
|         .get(cacheKey) | ||||
|         .then(machineInfo => resolve(machineInfo["machineIdentifier"])) | ||||
|         .then(machineInfo => resolve(machineInfo?.machineIdentifier)) | ||||
|         .catch(() => fetch(url, options)) | ||||
|         .then(response => response.json()) | ||||
|         .then(machineInfo => | ||||
|           this.cache.set(cacheKey, machineInfo["MediaContainer"], 2628000) | ||||
|           this.cache.set(cacheKey, machineInfo.MediaContainer, 2628000) | ||||
|         ) | ||||
|         .then(machineInfo => resolve(machineInfo["machineIdentifier"])) | ||||
|         .then(machineInfo => resolve(machineInfo?.machineIdentifier)) | ||||
|         .catch(error => { | ||||
|           if (error != undefined && error.type === "request-timeout") { | ||||
|             reject({ | ||||
|               message: "Plex did not respond", | ||||
|               status: 408, | ||||
|               success: false | ||||
|             }); | ||||
|           if (error?.type === "request-timeout") { | ||||
|             reject(new PlexRequestTimeoutError()); | ||||
|           } | ||||
|  | ||||
|           reject(error); | ||||
|           reject(new PlexUnexpectedError()); | ||||
|         }) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   matchTmdbAndPlexMedia(plex, tmdb) { | ||||
|     let match; | ||||
|  | ||||
|     if (plex == null || tmdb == null) return false; | ||||
|  | ||||
|     if (plex instanceof Array) { | ||||
|       let possibleMatches = plex.map(plexItem => | ||||
|         matchingTitleAndYear(plexItem, tmdb) | ||||
|       ); | ||||
|       match = possibleMatches.includes(true); | ||||
|     } else { | ||||
|       match = matchingTitleAndYear(plex, tmdb); | ||||
|     } | ||||
|  | ||||
|     return match; | ||||
|   } | ||||
|  | ||||
|   async existsInPlex(tmdb) { | ||||
|     const plexMatch = await this.findPlexItemByTitleAndYear( | ||||
|       tmdb.title, | ||||
|       tmdb.year | ||||
|     ); | ||||
|     return plexMatch ? true : false; | ||||
|     return !!plexMatch; | ||||
|   } | ||||
|  | ||||
|   findPlexItemByTitleAndYear(title, year) { | ||||
| @@ -128,10 +150,10 @@ class Plex { | ||||
|  | ||||
|     return this.search(title).then(plexResults => { | ||||
|       const matchesInPlex = plexResults.map(plex => | ||||
|         this.matchTmdbAndPlexMedia(plex, query) | ||||
|         matchTmdbAndPlexMedia(plex, query) | ||||
|       ); | ||||
|       const matchesIndex = matchesInPlex.findIndex(el => el === true); | ||||
|       return matchesInPlex != -1 ? plexResults[matchesIndex] : null; | ||||
|       return matchesInPlex !== -1 ? plexResults[matchesIndex] : null; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @@ -147,10 +169,10 @@ class Plex { | ||||
|       matchingObjectInPlexPromise | ||||
|     ]).then(([machineIdentifier, matchingObjectInPlex]) => { | ||||
|       if ( | ||||
|         matchingObjectInPlex == false || | ||||
|         matchingObjectInPlex == null || | ||||
|         matchingObjectInPlex["key"] == null || | ||||
|         machineIdentifier == null | ||||
|         matchingObjectInPlex === false || | ||||
|         matchingObjectInPlex === null || | ||||
|         matchingObjectInPlex.key === null || | ||||
|         machineIdentifier === null | ||||
|       ) | ||||
|         return false; | ||||
|  | ||||
| @@ -176,18 +198,14 @@ class Plex { | ||||
|         .catch(() => fetch(url, options)) // else fetch fresh data | ||||
|         .then(successfullResponse) | ||||
|         .then(results => this.cache.set(cacheKey, results, 21600)) // 6 hours | ||||
|         .then(this.mapResults) | ||||
|         .then(mapResults) | ||||
|         .then(resolve) | ||||
|         .catch(error => { | ||||
|           if (error != undefined && error.type === "request-timeout") { | ||||
|             reject({ | ||||
|               message: "Plex did not respond", | ||||
|               status: 408, | ||||
|               success: false | ||||
|             }); | ||||
|           if (error?.type === "request-timeout") { | ||||
|             reject(new PlexRequestTimeoutError()); | ||||
|           } | ||||
|  | ||||
|           reject(error); | ||||
|           reject(new PlexUnexpectedError()); | ||||
|         }) | ||||
|     ); | ||||
|   } | ||||
| @@ -200,40 +218,13 @@ class Plex { | ||||
|     const query = title; | ||||
|     const cacheKey = `${this.cacheTags.search}/${query}*`; | ||||
|  | ||||
|     this.cache.del( | ||||
|       cacheKey, | ||||
|       (error, | ||||
|       response => { | ||||
|         if (response == 1) return true; | ||||
|  | ||||
|         // TODO improve cache key matching by lowercasing it on the backend. | ||||
|         // what do we actually need to check for if the key was deleted or not | ||||
|         // it might be an error or another response code. | ||||
|         console.log("Unable to delete, key might not exists"); | ||||
|       }) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   mapResults(response) { | ||||
|     if ( | ||||
|       response == null || | ||||
|       response.MediaContainer == null || | ||||
|       response.MediaContainer.Hub == null | ||||
|     ) { | ||||
|       return []; | ||||
|     } | ||||
|  | ||||
|     return response.MediaContainer.Hub.filter(category => category.size > 0) | ||||
|       .map(category => { | ||||
|         if (category.type === "movie") { | ||||
|           return category.Metadata; | ||||
|         } else if (category.type === "show") { | ||||
|           return category.Metadata.map(convertPlexToShow); | ||||
|         } else if (category.type === "episode") { | ||||
|           return category.Metadata.map(convertPlexToEpisode); | ||||
|         } | ||||
|       }) | ||||
|       .filter(result => result !== undefined); | ||||
|     this.cache.del(cacheKey, (error, response) => { | ||||
|       // TODO improve cache key matching by lowercasing it on the backend. | ||||
|       // what do we actually need to check for if the key was deleted or not | ||||
|       // it might be an error or another response code. | ||||
|       console.log("Unable to delete, key might not exists"); | ||||
|       return response === 1; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,18 +1,55 @@ | ||||
| const rp = require("request-promise"); | ||||
| const convertPlexToSeasoned = require("./convertPlexToSeasoned"); | ||||
| const convertPlexToStream = require("./convertPlexToStream"); | ||||
| const rp = require("request-promise"); | ||||
|  | ||||
| // eslint-disable-next-line | ||||
| function addAttributeIfTmdbInPlex(_tmdb, plexResult) { | ||||
|   const tmdb = { ..._tmdb }; | ||||
|  | ||||
|   if (plexResult?.results?.length > 0) { | ||||
|     plexResult.results.map(plexItem => { | ||||
|       tmdb.matchedInPlex = | ||||
|         tmdb.title === plexItem.title && tmdb.year === plexItem.year; | ||||
|       return tmdb; | ||||
|     }); | ||||
|   } else { | ||||
|     tmdb.matchedInPlex = false; | ||||
|   } | ||||
|  | ||||
|   return Promise.resolve(tmdb); | ||||
| } | ||||
|  | ||||
| function mapResults(response) { | ||||
|   return Promise.resolve() | ||||
|     .then(() => { | ||||
|       if (!response?.MediaContainer?.Metadata) return [[], 0]; | ||||
|  | ||||
|       const mappedResults = response.MediaContainer.Metadata.filter(element => { | ||||
|         return element.type === "movie" || element.type === "show"; | ||||
|       }).map(element => convertPlexToSeasoned(element)); | ||||
|       return [mappedResults, mappedResults.length]; | ||||
|     }) | ||||
|     .catch(error => { | ||||
|       throw new Error(error); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| class PlexRepository { | ||||
|   constructor(plexIP) { | ||||
|     this.plexIP = plexIP; | ||||
|   } | ||||
|  | ||||
|   inPlex(tmdbResult) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => this.search(tmdbResult.title)) | ||||
|       .then(plexResult => this.compareTmdbToPlex(tmdbResult, plexResult)) | ||||
|       .catch(error => { | ||||
|         console.log(error); | ||||
|   inPlex(_tmdbResult) { | ||||
|     const tmdbResult = { ..._tmdbResult }; | ||||
|     this.search(tmdbResult.title) | ||||
|       .then(plexResult => addAttributeIfTmdbInPlex(tmdbResult, plexResult)) | ||||
|       .catch(() => { | ||||
|         /** | ||||
|          * If something crashes with search from this function it probably | ||||
|          * fine to set the `matchedInPlex` attribute to false and return | ||||
|          * original tmdb object | ||||
|          * */ | ||||
|  | ||||
|         tmdbResult.matchedInPlex = false; | ||||
|         return tmdbResult; | ||||
|       }); | ||||
| @@ -24,7 +61,7 @@ class PlexRepository { | ||||
|       `http://${this.plexIP}:32400/search?query=${queryUri}` | ||||
|     ); | ||||
|     const options = { | ||||
|       uri: uri, | ||||
|       uri, | ||||
|       headers: { | ||||
|         Accept: "application/json" | ||||
|       }, | ||||
| @@ -32,50 +69,13 @@ class PlexRepository { | ||||
|     }; | ||||
|  | ||||
|     return rp(options) | ||||
|       .catch(error => { | ||||
|         console.log(error); | ||||
|         throw new Error("Unable to search plex."); | ||||
|       }) | ||||
|       .then(result => this.mapResults(result)) | ||||
|       .then(result => mapResults(result)) | ||||
|       .then(([mappedResults, resultCount]) => ({ | ||||
|         results: mappedResults, | ||||
|         total_results: resultCount | ||||
|       })); | ||||
|   } | ||||
|  | ||||
|   compareTmdbToPlex(tmdb, plexResult) { | ||||
|     return Promise.resolve().then(() => { | ||||
|       if (plexResult.results.length === 0) { | ||||
|         tmdb.matchedInPlex = false; | ||||
|       } else { | ||||
|         // console.log('plex and tmdb:', plexResult, '\n',  tmdb) | ||||
|         plexResult.results.map(plexItem => { | ||||
|           if (tmdb.title === plexItem.title && tmdb.year === plexItem.year) | ||||
|             tmdb.matchedInPlex = true; | ||||
|           return tmdb; | ||||
|         }); | ||||
|       } | ||||
|       return tmdb; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   mapResults(response) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => { | ||||
|         if (!response.MediaContainer.hasOwnProperty("Metadata")) return [[], 0]; | ||||
|  | ||||
|         const mappedResults = response.MediaContainer.Metadata.filter( | ||||
|           element => { | ||||
|             return element.type === "movie" || element.type === "show"; | ||||
|           } | ||||
|         ).map(element => convertPlexToSeasoned(element)); | ||||
|         return [mappedResults, mappedResults.length]; | ||||
|       }) | ||||
|       .catch(error => { | ||||
|         throw new Error(error); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   nowPlaying() { | ||||
|     const options = { | ||||
|       uri: `http://${this.plexIP}:32400/status/sessions`, | ||||
|   | ||||
| @@ -28,15 +28,15 @@ class RequestRepository { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   search(query, type, page) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => tmdb.search(query, type, page)) | ||||
|   static search(query, type, page) { | ||||
|     return tmdb | ||||
|       .search(query, type, page) | ||||
|       .catch(error => Error(`error in the house${error}`)); | ||||
|   } | ||||
|  | ||||
|   lookup(identifier, type = "movie") { | ||||
|     return Promise.resolve() | ||||
|       .then(() => tmdb.lookup(identifier, type)) | ||||
|     return tmdb | ||||
|       .lookup(identifier, type) | ||||
|       .then(tmdbMovie => this.checkID(tmdbMovie)) | ||||
|       .then(tmdbMovie => plexRepository.inPlex(tmdbMovie)) | ||||
|       .catch(error => { | ||||
| @@ -44,19 +44,17 @@ class RequestRepository { | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   checkID(tmdbMovie) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => | ||||
|         this.database.get(this.queries.checkIfIdRequested, [ | ||||
|           tmdbMovie.id, | ||||
|           tmdbMovie.type | ||||
|         ]) | ||||
|       ) | ||||
|   checkID(_tmdbMovie) { | ||||
|     const tmdbMovie = _tmdbMovie; | ||||
|  | ||||
|     return this.database | ||||
|       .get(this.queries.checkIfIdRequested, [tmdbMovie.id, tmdbMovie.type]) | ||||
|       .then((result, error) => { | ||||
|         if (error) { | ||||
|           throw new Error(error); | ||||
|         } | ||||
|         tmdbMovie.requested = result ? true : false; | ||||
|  | ||||
|         tmdbMovie.requested = !!result; | ||||
|         return tmdbMovie; | ||||
|       }); | ||||
|   } | ||||
| @@ -66,45 +64,42 @@ class RequestRepository { | ||||
|    * @param {identifier, type} the id of the media object and type of media must be defined | ||||
|    * @returns {Promise} If nothing has gone wrong. | ||||
|    */ | ||||
|   sendRequest(identifier, type, ip, user_agent, user) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => tmdb.lookup(identifier, type)) | ||||
|       .then(movie => { | ||||
|         const username = user === undefined ? undefined : user.username; | ||||
|         // Add request to database | ||||
|         return this.database.run(this.queries.insertRequest, [ | ||||
|           movie.id, | ||||
|           movie.title, | ||||
|           movie.year, | ||||
|           movie.poster_path, | ||||
|           movie.background_path, | ||||
|           username, | ||||
|           ip, | ||||
|           user_agent, | ||||
|           movie.type | ||||
|         ]); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   fetchRequested(status, page = "1", type = "%") { | ||||
|     return Promise.resolve().then(() => { | ||||
|       if ( | ||||
|         status === "requested" || | ||||
|         status === "downloading" || | ||||
|         status === "downloaded" | ||||
|       ) | ||||
|         return this.database.all(this.queries.fetchRequestedItemsByStatus, [ | ||||
|           status, | ||||
|           type, | ||||
|           page | ||||
|         ]); | ||||
|       else return this.database.all(this.queries.fetchRequestedItems, page); | ||||
|   sendRequest(identifier, type, ip, userAgent, user) { | ||||
|     return tmdb.lookup(identifier, type).then(movie => { | ||||
|       const username = user === undefined ? undefined : user.username; | ||||
|       // Add request to database | ||||
|       return this.database.run(this.queries.insertRequest, [ | ||||
|         movie.id, | ||||
|         movie.title, | ||||
|         movie.year, | ||||
|         movie.poster_path, | ||||
|         movie.background_path, | ||||
|         username, | ||||
|         ip, | ||||
|         userAgent, | ||||
|         movie.type | ||||
|       ]); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   fetchRequested(status, page = "1", type = "%") { | ||||
|     if ( | ||||
|       status === "requested" || | ||||
|       status === "downloading" || | ||||
|       status === "downloaded" | ||||
|     ) | ||||
|       return this.database.all(this.queries.fetchRequestedItemsByStatus, [ | ||||
|         status, | ||||
|         type, | ||||
|         page | ||||
|       ]); | ||||
|  | ||||
|     return this.database.all(this.queries.fetchRequestedItems, page); | ||||
|   } | ||||
|  | ||||
|   userRequests(username) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => this.database.all(this.queries.userRequests, username)) | ||||
|     return this.database | ||||
|       .all(this.queries.userRequests, username) | ||||
|       .catch(error => { | ||||
|         if (String(error).includes("no such column")) { | ||||
|           throw new Error("Username not found"); | ||||
| @@ -113,8 +108,11 @@ class RequestRepository { | ||||
|       }) | ||||
|       .then(result => { | ||||
|         // TODO do a correct mapping before sending, not just a dump of the database | ||||
|         result.map(item => (item.poster = item.poster_path)); | ||||
|         return result; | ||||
|         return result.map(_item => { | ||||
|           const item = { ..._item }; | ||||
|           item.poster = item.poster_path; | ||||
|           return item; | ||||
|         }); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| class convertStreamToPlayback { | ||||
|    constructor(plexStream) { | ||||
|       this.bitrate = plexStream.bitrate; | ||||
|       this.width = plexStream.width; | ||||
|       this.height = plexStream.height; | ||||
|       this.decision = plexStream.decision; | ||||
|       this.audioProfile = plexStream.audioProfile; | ||||
|       this.videoProfile = plexStream.videoProfile; | ||||
|       this.duration = plexStream.duration; | ||||
|       this.container = plexStream.container; | ||||
|    } | ||||
|   constructor(plexStream) { | ||||
|     this.bitrate = plexStream.bitrate; | ||||
|     this.width = plexStream.width; | ||||
|     this.height = plexStream.height; | ||||
|     this.decision = plexStream.decision; | ||||
|     this.audioProfile = plexStream.audioProfile; | ||||
|     this.videoProfile = plexStream.videoProfile; | ||||
|     this.duration = plexStream.duration; | ||||
|     this.container = plexStream.container; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = convertStreamToPlayback; | ||||
|   | ||||
| @@ -8,9 +8,9 @@ class Episode { | ||||
|     this.summary = null; | ||||
|     this.rating = null; | ||||
|     this.views = null; | ||||
|     this.aired = null;    | ||||
|     this.type = 'episode'; | ||||
|     this.aired = null; | ||||
|     this.type = "episode"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Episode; | ||||
| module.exports = Episode; | ||||
|   | ||||
| @@ -5,8 +5,8 @@ class Movie { | ||||
|     this.summary = null; | ||||
|     this.rating = null; | ||||
|     this.tagline = null; | ||||
|     this.type = 'movie'; | ||||
|     this.type = "movie"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Movie; | ||||
| module.exports = Movie; | ||||
|   | ||||
| @@ -9,4 +9,4 @@ class Show { | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Show; | ||||
| module.exports = Show; | ||||
|   | ||||
| @@ -1,9 +1,17 @@ | ||||
| const assert = require("assert"); | ||||
| const configuration = require("../config/configuration").getInstance(); | ||||
| const TMDB = require("../tmdb/tmdb"); | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| // const configuration = require("../config/configuration").getInstance(); | ||||
| // const TMDB = require("../tmdb/tmdb"); | ||||
| const establishedDatabase = require("../database/database"); | ||||
| const utils = require("./utils"); | ||||
|  | ||||
| // const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
|  | ||||
| // function mapToTmdbByType(rows) { | ||||
| //   return rows.map(row => { | ||||
| //     if (row.type === "movie") return tmdb.movieInfo(row.id); | ||||
| //     if (row.type === "show") return tmdb.showInfo(row.id); | ||||
| //     return null; | ||||
| //   }); | ||||
| // } | ||||
|  | ||||
| class RequestRepository { | ||||
|   constructor(database) { | ||||
| @@ -18,85 +26,24 @@ class RequestRepository { | ||||
|         'select count(*) as totalRequests from requests where status != "downloaded"', | ||||
|       totalRequestsFilteredStatus: | ||||
|         "select count(*) as totalRequests from requests where status = ?", | ||||
|       fetchAllSort: `select id, type from request order by ? ?`, | ||||
|       fetchAllFilter: `select id, type from request where ? is "?"`, | ||||
|       fetchAllQuery: `select id, type from request where title like "%?%" or year like "%?%"`, | ||||
|       fetchAllFilterAndSort: `select id, type from request where ? is "?" order by ? ?`, | ||||
|       downloaded: | ||||
|         "(select status from requests where id is request.id and type is request.type limit 1)", | ||||
|       // fetchAllSort: `select id, type from request order by ? ?`, | ||||
|       // fetchAllFilter: `select id, type from request where ? is "?"`, | ||||
|       // fetchAllQuery: `select id, type from request where title like "%?%" or year like "%?%"`, | ||||
|       // fetchAllFilterAndSort: `select id, type from request where ? is "?" order by ? ?`, | ||||
|       // downloaded: "(select status from requests where id is request.id and type is request.type limit 1)", | ||||
|       // deluge: '(select status from deluge_torrent where id is request.id and type is request.type limit 1)', | ||||
|       // fetchAllFilterStatus: 'select * from request where ' | ||||
|       readWithoutUserData: | ||||
|         "select id, title, year, type, status, date from requests where id is ? and type is ?", | ||||
|       // readWithoutUserData: "select id, title, year, type, status, date from requests where id is ? and type is ?", | ||||
|       read: "select id, title, year, type, status, requested_by, ip, date, user_agent from requests where id is ? and type is ?" | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   sortAndFilterToDbQuery(by, direction, filter, query) { | ||||
|     let dbQuery = undefined; | ||||
|  | ||||
|     if (query !== undefined) { | ||||
|       const dbParams = [query, query]; | ||||
|       const dbquery = this.queries.fetchAllQuery; | ||||
|  | ||||
|       dbQuery = dbquery | ||||
|         .split("") | ||||
|         .map(char => (char === "?" ? dbParams.shift() : char)) | ||||
|         .join(""); | ||||
|     } else if (by !== undefined && filter !== undefined) { | ||||
|       const paramToColumnAndValue = { | ||||
|         movie: ["type", "movie"], | ||||
|         show: ["type", "show"] | ||||
|       }; | ||||
|       const dbParams = paramToColumnAndValue[filter].concat([by, direction]); | ||||
|       const query = this.queries.fetchAllFilterAndSort; | ||||
|  | ||||
|       dbQuery = query | ||||
|         .split("") | ||||
|         .map(char => (char === "?" ? dbParams.shift() : char)) | ||||
|         .join(""); | ||||
|     } else if (by !== undefined) { | ||||
|       const dbParams = [by, direction]; | ||||
|       const query = this.queries.fetchAllSort; | ||||
|  | ||||
|       dbQuery = query | ||||
|         .split("") | ||||
|         .map(char => (char === "?" ? dbParams.shift() : char)) | ||||
|         .join(""); | ||||
|     } else if (filter !== undefined) { | ||||
|       const paramToColumnAndValue = { | ||||
|         movie: ["type", "movie"], | ||||
|         show: ["type", "show"], | ||||
|         downloaded: [this.queries.downloaded, "downloaded"] | ||||
|         // downloading: [this.database.delugeStatus, 'downloading'] | ||||
|       }; | ||||
|       const dbParams = paramToColumnAndValue[filter]; | ||||
|       const query = this.queries.fetchAllFilter; | ||||
|  | ||||
|       dbQuery = query | ||||
|         .split("") | ||||
|         .map(char => (char === "?" ? dbParams.shift() : char)) | ||||
|         .join(""); | ||||
|     } else { | ||||
|       dbQuery = this.queries.fetchAll; | ||||
|     } | ||||
|  | ||||
|     return dbQuery; | ||||
|   } | ||||
|  | ||||
|   mapToTmdbByType(rows) { | ||||
|     return rows.map(row => { | ||||
|       if (row.type === "movie") return tmdb.movieInfo(row.id); | ||||
|       else if (row.type === "show") return tmdb.showInfo(row.id); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Add tmdb movie|show to requests | ||||
|    * @param {tmdb} tmdb class of movie|show to add | ||||
|    * @returns {Promise} | ||||
|    */ | ||||
|   requestFromTmdb(tmdb, ip, user_agent, username) { | ||||
|   requestFromTmdb(tmdb, ip, userAgent, username) { | ||||
|     return Promise.resolve() | ||||
|       .then(() => this.database.get(this.queries.read, [tmdb.id, tmdb.type])) | ||||
|       .then(row => | ||||
| @@ -111,7 +58,7 @@ class RequestRepository { | ||||
|           tmdb.backdrop, | ||||
|           username, | ||||
|           ip, | ||||
|           user_agent, | ||||
|           userAgent, | ||||
|           tmdb.type | ||||
|         ]) | ||||
|       ) | ||||
| @@ -122,7 +69,6 @@ class RequestRepository { | ||||
|         ) { | ||||
|           throw new Error("This id is already requested", error.message); | ||||
|         } | ||||
|         console.log("Error @ request.addTmdb:", error); | ||||
|         throw new Error("Could not add request"); | ||||
|       }); | ||||
|   } | ||||
| @@ -152,20 +98,12 @@ class RequestRepository { | ||||
|   /** | ||||
|    * Fetch all requests with optional sort and filter params | ||||
|    * @param {String} what we are sorting by | ||||
|    * @param {String} direction that can be either 'asc' or 'desc', default 'asc'. | ||||
|    * @param {String} params to filter by | ||||
|    * @param {String} query param to filter result on. Filters on title and year | ||||
|    * @returns {Promise} | ||||
|    */ | ||||
|   fetchAll( | ||||
|     page = 1, | ||||
|     sort_by = undefined, | ||||
|     sort_direction = "asc", | ||||
|     filter = undefined, | ||||
|     query = undefined | ||||
|   ) { | ||||
|   fetchAll(_page = 1, filter = null) { | ||||
|     // TODO implemented sort and filter | ||||
|     page = parseInt(page); | ||||
|     const page = parseInt(_page, 10); | ||||
|     let fetchQuery = this.queries.fetchAll; | ||||
|     let fetchTotalResults = this.queries.totalRequests; | ||||
|     let fetchParams = [page]; | ||||
| @@ -176,26 +114,24 @@ class RequestRepository { | ||||
|         filter === "downloaded" || | ||||
|         filter === "requested") | ||||
|     ) { | ||||
|       console.log("tes"); | ||||
|       fetchQuery = this.queries.fetchAllFilteredStatus; | ||||
|       fetchTotalResults = this.queries.totalRequestsFilteredStatus; | ||||
|       fetchParams = [filter, page]; | ||||
|     } else { | ||||
|       filter = undefined; | ||||
|     } | ||||
|  | ||||
|     return Promise.resolve() | ||||
|       .then(dbQuery => this.database.all(fetchQuery, fetchParams)) | ||||
|     return this.database | ||||
|       .all(fetchQuery, fetchParams) | ||||
|       .then(async rows => { | ||||
|         const sqliteResponse = await this.database.get( | ||||
|           fetchTotalResults, | ||||
|           filter ? filter : undefined | ||||
|           filter || null | ||||
|         ); | ||||
|         const totalRequests = sqliteResponse["totalRequests"]; | ||||
|         const { totalRequests } = sqliteResponse; | ||||
|         const totalPages = Math.ceil(totalRequests / 26); | ||||
|  | ||||
|         return [ | ||||
|           rows.map(item => { | ||||
|           rows.map(_item => { | ||||
|             const item = _item; | ||||
|             item.poster = item.poster_path; | ||||
|             delete item.poster_path; | ||||
|             item.backdrop = item.background_path; | ||||
| @@ -205,18 +141,18 @@ class RequestRepository { | ||||
|           totalPages, | ||||
|           totalRequests | ||||
|         ]; | ||||
|         return Promise.all(this.mapToTmdbByType(rows)); | ||||
|  | ||||
|         // return mapToTmdbByType(rows); | ||||
|       }) | ||||
|       .then(([result, totalPages, totalRequests]) => | ||||
|         Promise.resolve({ | ||||
|           results: result, | ||||
|           total_results: totalRequests, | ||||
|           page: page, | ||||
|           page, | ||||
|           total_pages: totalPages | ||||
|         }) | ||||
|       ) | ||||
|       .catch(error => { | ||||
|         console.log(error); | ||||
|         throw error; | ||||
|       }); | ||||
|   } | ||||
|   | ||||
| @@ -1,34 +1,48 @@ | ||||
| // TODO : test title and date are valid matches to columns in the database | ||||
| const validSortParams = ['title', 'date'] | ||||
| const validSortDirs = ['asc', 'desc'] | ||||
| const validFilterParams = ['movie', 'show', 'seeding', 'downloading', 'paused', 'finished', 'downloaded'] | ||||
| const validSortParams = ["title", "date"]; | ||||
| const validSortDirs = ["asc", "desc"]; | ||||
| const validFilterParams = [ | ||||
|   "movie", | ||||
|   "show", | ||||
|   "seeding", | ||||
|   "downloading", | ||||
|   "paused", | ||||
|   "finished", | ||||
|   "downloaded" | ||||
| ]; | ||||
|  | ||||
| function validSort(by, direction) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|     if (by === undefined) { | ||||
|       resolve() | ||||
|       resolve(); | ||||
|     } | ||||
|  | ||||
|     if (validSortParams.includes(by) && validSortDirs.includes(direction)) { | ||||
|       resolve() | ||||
|       resolve(); | ||||
|     } else { | ||||
|       reject(new Error(`invalid sort parameter, must be of: ${validSortParams} with optional sort directions: ${validSortDirs} appended with ':'`)) | ||||
|       reject( | ||||
|         new Error( | ||||
|           `invalid sort parameter, must be of: ${validSortParams} with optional sort directions: ${validSortDirs} appended with ':'` | ||||
|         ) | ||||
|       ); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function validFilter(filter_param) { | ||||
| function validFilter(filterParam) { | ||||
|   return new Promise((resolve, reject) => { | ||||
|      if (filter_param === undefined) { | ||||
|       resolve() | ||||
|     if (filterParam === undefined) { | ||||
|       resolve(); | ||||
|     } | ||||
|  | ||||
|     if (filter_param && validFilterParams.includes(filter_param)) { | ||||
|       resolve() | ||||
|     if (filterParam && validFilterParams.includes(filterParam)) { | ||||
|       resolve(); | ||||
|     } else { | ||||
|       reject(new Error(`filter parameteres must be of type: ${validFilterParams}`)) | ||||
|       reject( | ||||
|         new Error(`filter parameteres must be of type: ${validFilterParams}`) | ||||
|       ); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| module.exports = { validSort, validFilter } | ||||
| module.exports = { validSort, validFilter }; | ||||
|   | ||||
| @@ -1,5 +1,15 @@ | ||||
| const establishedDatabase = require("../database/database"); | ||||
|  | ||||
| class SearchHistoryCreateDatabaseError extends Error { | ||||
|   constructor(message = "an unexpected error occured", errorResponse = null) { | ||||
|     super(message); | ||||
|  | ||||
|     this.source = "database"; | ||||
|     this.statusCode = 500; | ||||
|     this.errorResponse = errorResponse; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class SearchHistory { | ||||
|   constructor(database) { | ||||
|     this.database = database || establishedDatabase; | ||||
| @@ -16,18 +26,12 @@ class SearchHistory { | ||||
|    * @returns {Promise} | ||||
|    */ | ||||
|   read(user) { | ||||
|     return new Promise((resolve, reject) => | ||||
|       this.database | ||||
|         .all(this.queries.read, user) | ||||
|         .then((result, error) => { | ||||
|           if (error) throw new Error(error); | ||||
|           resolve(result.map(row => row.search_query)); | ||||
|         }) | ||||
|         .catch(error => { | ||||
|           console.log("Error when fetching history from database:", error); | ||||
|           reject("Unable to get history."); | ||||
|         }) | ||||
|     ); | ||||
|     return this.database | ||||
|       .all(this.queries.read, user) | ||||
|       .then(result => result.map(row => row.search_query)) | ||||
|       .catch(error => { | ||||
|         throw new Error("Unable to get history.", error); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -41,15 +45,12 @@ class SearchHistory { | ||||
|       .run(this.queries.create, [searchQuery, username]) | ||||
|       .catch(error => { | ||||
|         if (error.message.includes("FOREIGN")) { | ||||
|           throw new Error("Could not create search history."); | ||||
|           throw new SearchHistoryCreateDatabaseError( | ||||
|             "Could not create search history." | ||||
|           ); | ||||
|         } | ||||
|  | ||||
|         throw { | ||||
|           success: false, | ||||
|           status: 500, | ||||
|           message: "An unexpected error occured", | ||||
|           source: "database" | ||||
|         }; | ||||
|         throw new SearchHistoryCreateDatabaseError(); | ||||
|       }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| class Stray { | ||||
|    constructor(id) { | ||||
|       this.id = id; | ||||
|    } | ||||
|   constructor(id) { | ||||
|     this.id = id; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Stray; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const assert = require("assert"); | ||||
| const pythonShell = require("python-shell"); | ||||
| const Stray = require("./stray"); | ||||
| const establishedDatabase = require("../database/database"); | ||||
| const pythonShell = require("python-shell"); | ||||
|  | ||||
| class StrayRepository { | ||||
|   constructor(database) { | ||||
|   | ||||
| @@ -1,5 +1,19 @@ | ||||
| const fetch = require("node-fetch"); | ||||
|  | ||||
| class TautulliUnexpectedError extends Error { | ||||
|   constructor(errorMessage) { | ||||
|     const message = "Unexpected error fetching from tautulli."; | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 500; | ||||
|     this.errorMessage = errorMessage; | ||||
|   } | ||||
| } | ||||
|  | ||||
| function logTautulliError(error) { | ||||
|   throw new TautulliUnexpectedError(error); | ||||
| } | ||||
|  | ||||
| class Tautulli { | ||||
|   constructor(apiKey, ip, port) { | ||||
|     this.apiKey = apiKey; | ||||
| @@ -7,67 +21,59 @@ class Tautulli { | ||||
|     this.port = port; | ||||
|   } | ||||
|  | ||||
|   buildUrlWithCmdAndUserid(cmd, user_id) { | ||||
|   buildUrlWithCmdAndUserid(cmd, userId) { | ||||
|     const url = new URL("api/v2", `http://${this.ip}:${this.port}`); | ||||
|     url.searchParams.append("apikey", this.apiKey); | ||||
|     url.searchParams.append("cmd", cmd); | ||||
|     url.searchParams.append("user_id", user_id); | ||||
|     url.searchParams.append("user_id", userId); | ||||
|  | ||||
|     return url; | ||||
|   } | ||||
|  | ||||
|   logTautulliError(error) { | ||||
|     console.error("error fetching from tautulli"); | ||||
|  | ||||
|     throw error; | ||||
|   } | ||||
|  | ||||
|   getPlaysByDayOfWeek(plex_userid, days, y_axis) { | ||||
|   getPlaysByDayOfWeek(plexUserId, days, yAxis) { | ||||
|     const url = this.buildUrlWithCmdAndUserid( | ||||
|       "get_plays_by_dayofweek", | ||||
|       plex_userid | ||||
|       plexUserId | ||||
|     ); | ||||
|     url.searchParams.append("time_range", days); | ||||
|     url.searchParams.append("y_axis", y_axis); | ||||
|     url.searchParams.append("y_axis", yAxis); | ||||
|  | ||||
|     return fetch(url.href) | ||||
|       .then(resp => resp.json()) | ||||
|       .catch(error => this.logTautulliError(error)); | ||||
|       .catch(error => logTautulliError(error)); | ||||
|   } | ||||
|  | ||||
|   getPlaysByDays(plex_userid, days, y_axis) { | ||||
|     const url = this.buildUrlWithCmdAndUserid("get_plays_by_date", plex_userid); | ||||
|   getPlaysByDays(plexUserId, days, yAxis) { | ||||
|     const url = this.buildUrlWithCmdAndUserid("get_plays_by_date", plexUserId); | ||||
|     url.searchParams.append("time_range", days); | ||||
|     url.searchParams.append("y_axis", y_axis); | ||||
|     url.searchParams.append("y_axis", yAxis); | ||||
|  | ||||
|     return fetch(url.href) | ||||
|       .then(resp => resp.json()) | ||||
|       .catch(error => this.logTautulliError(error)); | ||||
|       .catch(error => logTautulliError(error)); | ||||
|   } | ||||
|  | ||||
|   watchTimeStats(plex_userid) { | ||||
|   watchTimeStats(plexUserId) { | ||||
|     const url = this.buildUrlWithCmdAndUserid( | ||||
|       "get_user_watch_time_stats", | ||||
|       plex_userid | ||||
|       plexUserId | ||||
|     ); | ||||
|     url.searchParams.append("grouping", 0); | ||||
|  | ||||
|     return fetch(url.href) | ||||
|       .then(resp => resp.json()) | ||||
|       .catch(error => this.logTautulliError(error)); | ||||
|       .catch(error => logTautulliError(error)); | ||||
|   } | ||||
|  | ||||
|   viewHistory(plex_userid) { | ||||
|     const url = this.buildUrlWithCmdAndUserid("get_history", plex_userid); | ||||
|   viewHistory(plexUserId) { | ||||
|     const url = this.buildUrlWithCmdAndUserid("get_history", plexUserId); | ||||
|  | ||||
|     url.searchParams.append("start", 0); | ||||
|     url.searchParams.append("length", 50); | ||||
|  | ||||
|     console.log("fetching url", url.href); | ||||
|  | ||||
|     return fetch(url.href) | ||||
|       .then(resp => resp.json()) | ||||
|       .catch(error => this.logTautulliError(error)); | ||||
|       .catch(error => logTautulliError(error)); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										285
									
								
								src/tmdb/tmdb.js
									
									
									
									
									
								
							
							
						
						
									
										285
									
								
								src/tmdb/tmdb.js
									
									
									
									
									
								
							| @@ -3,27 +3,86 @@ const redisCache = require("../cache/redis"); | ||||
|  | ||||
| const { Movie, Show, Person, Credits, ReleaseDates } = require("./types"); | ||||
|  | ||||
| const tmdbErrorResponse = (error, typeString = undefined) => { | ||||
|   if (error.status === 404) { | ||||
|     let message = error.response.body.status_message; | ||||
| class TMDBNotFoundError extends Error { | ||||
|   constructor(message) { | ||||
|     super(message); | ||||
|  | ||||
|     throw { | ||||
|       status: 404, | ||||
|       message: message.slice(0, -1) + " in tmdb." | ||||
|     }; | ||||
|     this.statusCode = 404; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class TMDBUnauthorizedError extends Error { | ||||
|   constructor(message = "TMDB returned access denied, requires api token.") { | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 401; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class TMDBUnexpectedError extends Error { | ||||
|   constructor(type, errorMessage) { | ||||
|     const message = `An unexpected error occured while fetching ${type} from tmdb`; | ||||
|     super(message); | ||||
|  | ||||
|     this.errorMessage = errorMessage; | ||||
|     this.statusCode = 500; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class TMDBNotReachableError extends Error { | ||||
|   constructor( | ||||
|     message = "TMDB api not reachable, check your internet connection" | ||||
|   ) { | ||||
|     super(message); | ||||
|   } | ||||
| } | ||||
|  | ||||
| const tmdbErrorResponse = (error, type = null) => { | ||||
|   if (error.status === 404) { | ||||
|     const message = error.response.body.status_message; | ||||
|  | ||||
|     throw new TMDBNotFoundError(`${message.slice(0, -1)} in tmdb.`); | ||||
|   } else if (error.status === 401) { | ||||
|     throw { | ||||
|       status: 401, | ||||
|       message: error.response.body.status_message | ||||
|     }; | ||||
|     throw new TMDBUnauthorizedError(error?.response?.body?.status_message); | ||||
|   } else if (error?.code === "ENOTFOUND") { | ||||
|     throw new TMDBNotReachableError(); | ||||
|   } | ||||
|  | ||||
|   throw { | ||||
|     status: 500, | ||||
|     message: `An unexpected error occured while fetching ${typeString} from tmdb` | ||||
|   }; | ||||
|   throw new TMDBUnexpectedError(type, error); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Maps our response from tmdb api to a movie/show object. | ||||
|  * @param {String} response from tmdb. | ||||
|  * @param {String} The type declared in listSearch. | ||||
|  * @returns {Promise} dict with tmdb results, mapped as movie/show objects. | ||||
|  */ | ||||
| function mapResults(response, type = null) { | ||||
|   const results = response?.results?.map(result => { | ||||
|     if (type === "movie" || result.media_type === "movie") { | ||||
|       const movie = Movie.convertFromTmdbResponse(result); | ||||
|       return movie.createJsonResponse(); | ||||
|     } | ||||
|     if (type === "show" || result.media_type === "tv") { | ||||
|       const show = Show.convertFromTmdbResponse(result); | ||||
|       return show.createJsonResponse(); | ||||
|     } | ||||
|     if (type === "person" || result.media_type === "person") { | ||||
|       const person = Person.convertFromTmdbResponse(result); | ||||
|       return person.createJsonResponse(); | ||||
|     } | ||||
|  | ||||
|     return {}; | ||||
|   }); | ||||
|  | ||||
|   return { | ||||
|     results, | ||||
|     page: response?.page, | ||||
|     total_results: response?.total_results, | ||||
|     total_pages: response?.total_pages | ||||
|   }; | ||||
| } | ||||
|  | ||||
| class TMDB { | ||||
|   constructor(apiKey, cache, tmdbLibrary) { | ||||
|     this.tmdbLibrary = tmdbLibrary || moviedb(apiKey); | ||||
| @@ -53,15 +112,17 @@ class TMDB { | ||||
|     this.defaultTTL = 86400; | ||||
|   } | ||||
|  | ||||
|   getFromCacheOrFetchFromTmdb(cacheKey, tmdbMethod, query) { | ||||
|     return new Promise((resolve, reject) => | ||||
|       this.cache | ||||
|         .get(cacheKey) | ||||
|         .then(resolve) | ||||
|         .catch(() => this.tmdb(tmdbMethod, query)) | ||||
|         .then(resolve) | ||||
|         .catch(error => reject(tmdbErrorResponse(error, tmdbMethod))) | ||||
|     ); | ||||
|   async getFromCacheOrFetchFromTmdb(cacheKey, tmdbMethod, query) { | ||||
|     try { | ||||
|       const result = await this.cache.get(cacheKey); | ||||
|       if (!result) throw new Error(); | ||||
|  | ||||
|       return result; | ||||
|     } catch { | ||||
|       return this.tmdb(tmdbMethod, query) | ||||
|         .then(result => this.cache.set(cacheKey, result, this.defaultTTL)) | ||||
|         .catch(error => tmdbErrorResponse(error, tmdbMethod)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -75,9 +136,9 @@ class TMDB { | ||||
|     const query = { id: identifier }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.movieInfo}:${identifier}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "movieInfo", query) | ||||
|       .then(movie => this.cache.set(cacheKey, movie, this.defaultTTL)) | ||||
|       .then(movie => Movie.convertFromTmdbResponse(movie)); | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "movieInfo", query).then( | ||||
|       movie => Movie.convertFromTmdbResponse(movie) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -89,9 +150,11 @@ class TMDB { | ||||
|     const query = { id: identifier }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.movieCredits}:${identifier}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "movieCredits", query) | ||||
|       .then(credits => this.cache.set(cacheKey, credits, this.defaultTTL)) | ||||
|       .then(credits => Credits.convertFromTmdbResponse(credits)); | ||||
|     return this.getFromCacheOrFetchFromTmdb( | ||||
|       cacheKey, | ||||
|       "movieCredits", | ||||
|       query | ||||
|     ).then(credits => Credits.convertFromTmdbResponse(credits)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -107,11 +170,7 @@ class TMDB { | ||||
|       cacheKey, | ||||
|       "movieReleaseDates", | ||||
|       query | ||||
|     ) | ||||
|       .then(releaseDates => | ||||
|         this.cache.set(cacheKey, releaseDates, this.defaultTTL) | ||||
|       ) | ||||
|       .then(releaseDates => ReleaseDates.convertFromTmdbResponse(releaseDates)); | ||||
|     ).then(releaseDates => ReleaseDates.convertFromTmdbResponse(releaseDates)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -124,18 +183,18 @@ class TMDB { | ||||
|     const query = { id: identifier }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.showInfo}:${identifier}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvInfo", query) | ||||
|       .then(show => this.cache.set(cacheKey, show, this.defaultTTL)) | ||||
|       .then(show => Show.convertFromTmdbResponse(show)); | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvInfo", query).then( | ||||
|       show => Show.convertFromTmdbResponse(show) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   showCredits(identifier) { | ||||
|     const query = { id: identifier }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.showCredits}:${identifier}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvCredits", query) | ||||
|       .then(credits => this.cache.set(cacheKey, credits, this.defaultTTL)) | ||||
|       .then(credits => Credits.convertFromTmdbResponse(credits)); | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "tvCredits", query).then( | ||||
|       credits => Credits.convertFromTmdbResponse(credits) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -148,9 +207,9 @@ class TMDB { | ||||
|     const query = { id: identifier }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.personInfo}:${identifier}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "personInfo", query) | ||||
|       .then(person => this.cache.set(cacheKey, person, this.defaultTTL)) | ||||
|       .then(person => Person.convertFromTmdbResponse(person)); | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "personInfo", query).then( | ||||
|       person => Person.convertFromTmdbResponse(person) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   personCredits(identifier) { | ||||
| @@ -161,18 +220,18 @@ class TMDB { | ||||
|       cacheKey, | ||||
|       "personCombinedCredits", | ||||
|       query | ||||
|     ) | ||||
|       .then(credits => this.cache.set(cacheKey, credits, this.defaultTTL)) | ||||
|       .then(credits => Credits.convertFromTmdbResponse(credits)); | ||||
|     ).then(credits => Credits.convertFromTmdbResponse(credits)); | ||||
|   } | ||||
|  | ||||
|   multiSearch(search_query, page = 1, include_adult = true) { | ||||
|     const query = { query: search_query, page, include_adult }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.multiSearch}:${page}:${search_query}:${include_adult}`; | ||||
|   multiSearch(searchQuery, page = 1, includeAdult = true) { | ||||
|     const query = { query: searchQuery, page, include_adult: includeAdult }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.multiSearch}:${page}:${searchQuery}:${includeAdult}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchMulti", query) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response)); | ||||
|     return this.getFromCacheOrFetchFromTmdb( | ||||
|       cacheKey, | ||||
|       "searchMulti", | ||||
|       query | ||||
|     ).then(response => mapResults(response)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -181,13 +240,19 @@ class TMDB { | ||||
|    * @param {Number} page representing pagination of results | ||||
|    * @returns {Promise} dict with query results, current page and total_pages | ||||
|    */ | ||||
|   movieSearch(search_query, page = 1, include_adult = true) { | ||||
|     const tmdbquery = { query: search_query, page, include_adult }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.movieSearch}:${page}:${search_query}:${include_adult}`; | ||||
|   movieSearch(searchQuery, page = 1, includeAdult = true) { | ||||
|     const tmdbquery = { | ||||
|       query: searchQuery, | ||||
|       page, | ||||
|       include_adult: includeAdult | ||||
|     }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.movieSearch}:${page}:${searchQuery}:${includeAdult}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchMovie", tmdbquery) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response, "movie")); | ||||
|     return this.getFromCacheOrFetchFromTmdb( | ||||
|       cacheKey, | ||||
|       "searchMovie", | ||||
|       tmdbquery | ||||
|     ).then(response => mapResults(response, "movie")); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -196,13 +261,19 @@ class TMDB { | ||||
|    * @param {Number} page representing pagination of results | ||||
|    * @returns {Promise} dict with query results, current page and total_pages | ||||
|    */ | ||||
|   showSearch(search_query, page = 1, include_adult = true) { | ||||
|     const tmdbquery = { query: search_query, page, include_adult }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.showSearch}:${page}:${search_query}:${include_adult}`; | ||||
|   showSearch(searchQuery, page = 1, includeAdult = true) { | ||||
|     const tmdbquery = { | ||||
|       query: searchQuery, | ||||
|       page, | ||||
|       include_adult: includeAdult | ||||
|     }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.showSearch}:${page}:${searchQuery}:${includeAdult}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchTv", tmdbquery) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response, "show")); | ||||
|     return this.getFromCacheOrFetchFromTmdb( | ||||
|       cacheKey, | ||||
|       "searchTv", | ||||
|       tmdbquery | ||||
|     ).then(response => mapResults(response, "show")); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -211,59 +282,37 @@ class TMDB { | ||||
|    * @param {Number} page representing pagination of results | ||||
|    * @returns {Promise} dict with query results, current page and total_pages | ||||
|    */ | ||||
|   personSearch(search_query, page = 1, include_adult = true) { | ||||
|     const tmdbquery = { query: search_query, page, include_adult }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.personSearch}:${page}:${search_query}:${include_adult}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, "searchPerson", tmdbquery) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response, "person")); | ||||
|   } | ||||
|  | ||||
|   movieList(listname, page = 1) { | ||||
|     const query = { page: page }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags[listname]}:${page}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, listname, query) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response, "movie")); | ||||
|   } | ||||
|  | ||||
|   showList(listname, page = 1) { | ||||
|     const query = { page: page }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags[listname]}:${page}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, listName, query) | ||||
|       .then(response => this.cache.set(cacheKey, response, this.defaultTTL)) | ||||
|       .then(response => this.mapResults(response, "show")); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Maps our response from tmdb api to a movie/show object. | ||||
|    * @param {String} response from tmdb. | ||||
|    * @param {String} The type declared in listSearch. | ||||
|    * @returns {Promise} dict with tmdb results, mapped as movie/show objects. | ||||
|    */ | ||||
|   mapResults(response, type = undefined) { | ||||
|     let results = response.results.map(result => { | ||||
|       if (type === "movie" || result.media_type === "movie") { | ||||
|         const movie = Movie.convertFromTmdbResponse(result); | ||||
|         return movie.createJsonResponse(); | ||||
|       } else if (type === "show" || result.media_type === "tv") { | ||||
|         const show = Show.convertFromTmdbResponse(result); | ||||
|         return show.createJsonResponse(); | ||||
|       } else if (type === "person" || result.media_type === "person") { | ||||
|         const person = Person.convertFromTmdbResponse(result); | ||||
|         return person.createJsonResponse(); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|       results: results, | ||||
|       page: response.page, | ||||
|       total_results: response.total_results, | ||||
|       total_pages: response.total_pages | ||||
|   personSearch(searchQuery, page = 1, includeAdult = true) { | ||||
|     const tmdbquery = { | ||||
|       query: searchQuery, | ||||
|       page, | ||||
|       include_adult: includeAdult | ||||
|     }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags.personSearch}:${page}:${searchQuery}:${includeAdult}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb( | ||||
|       cacheKey, | ||||
|       "searchPerson", | ||||
|       tmdbquery | ||||
|     ).then(response => mapResults(response, "person")); | ||||
|   } | ||||
|  | ||||
|   movieList(listName, page = 1) { | ||||
|     const query = { page }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags[listName]}:${page}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, listName, query).then( | ||||
|       response => mapResults(response, "movie") | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   showList(listName, page = 1) { | ||||
|     const query = { page }; | ||||
|     const cacheKey = `tmdb/${this.cacheTags[listName]}:${page}`; | ||||
|  | ||||
|     return this.getFromCacheOrFetchFromTmdb(cacheKey, listName, query).then( | ||||
|       response => mapResults(response, "show") | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -278,7 +327,7 @@ class TMDB { | ||||
|         if (error) { | ||||
|           return reject(error); | ||||
|         } | ||||
|         resolve(reponse); | ||||
|         return resolve(reponse); | ||||
|       }; | ||||
|  | ||||
|       if (!argument) { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| const Movie = require('./types/movie.js') | ||||
| const Show = require('./types/show.js') | ||||
| const Person = require('./types/person.js') | ||||
| const Credits = require('./types/credits.js') | ||||
| const ReleaseDates = require('./types/releaseDates.js') | ||||
| const Movie = require("./types/movie"); | ||||
| const Show = require("./types/show"); | ||||
| const Person = require("./types/person"); | ||||
| const Credits = require("./types/credits"); | ||||
| const ReleaseDates = require("./types/releaseDates"); | ||||
|  | ||||
| module.exports = { Movie, Show, Person, Credits, ReleaseDates } | ||||
| module.exports = { Movie, Show, Person, Credits, ReleaseDates }; | ||||
|   | ||||
| @@ -61,4 +61,4 @@ interface Genre { | ||||
|   name: string; | ||||
| } | ||||
|  | ||||
| export { Movie, Show, Person, Genre } | ||||
| export { Movie, Show, Person, Genre }; | ||||
|   | ||||
| @@ -1,65 +1,9 @@ | ||||
| import Movie from "./movie"; | ||||
| import Show from "./show"; | ||||
| /* eslint-disable camelcase */ | ||||
| const Movie = require("./movie"); | ||||
| const Show = require("./show"); | ||||
|  | ||||
| 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 => { | ||||
|       if (cast["media_type"]) { | ||||
|         if (cast.media_type === "movie") { | ||||
|           return CreditedMovie.convertFromTmdbResponse(cast); | ||||
|         } else if (cast.media_type === "tv") { | ||||
|           return CreditedShow.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return new CastMember( | ||||
|         cast.character, | ||||
|         cast.gender, | ||||
|         cast.id, | ||||
|         cast.name, | ||||
|         cast.profile_path | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     const allCrew = crew.map(crew => { | ||||
|       if (cast["media_type"]) { | ||||
|         if (cast.media_type === "movie") { | ||||
|           return CreditedMovie.convertFromTmdbResponse(cast); | ||||
|         } else if (cast.media_type === "tv") { | ||||
|           return CreditedShow.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return 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 CreditedMovie extends Movie {} | ||||
| class CreditedShow extends Show {} | ||||
|  | ||||
| class CastMember { | ||||
|   constructor(character, gender, id, name, profile_path) { | ||||
| @@ -107,7 +51,66 @@ class CrewMember { | ||||
|   } | ||||
| } | ||||
|  | ||||
| class CreditedMovie extends Movie {} | ||||
| class CreditedShow extends Show {} | ||||
| 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 => { | ||||
|       if (cast.media_type) { | ||||
|         if (cast.media_type === "movie") { | ||||
|           return CreditedMovie.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|         if (cast.media_type === "tv") { | ||||
|           return CreditedShow.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return new CastMember( | ||||
|         cast.character, | ||||
|         cast.gender, | ||||
|         cast.id, | ||||
|         cast.name, | ||||
|         cast.profile_path | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     const allCrew = crew.map(crew => { | ||||
|       if (cast.media_type) { | ||||
|         if (cast.media_type === "movie") { | ||||
|           return CreditedMovie.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|         if (cast.media_type === "tv") { | ||||
|           return CreditedShow.convertFromTmdbResponse(cast); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return 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()) | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = Credits; | ||||
|   | ||||
| @@ -1,7 +1,22 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| class Movie { | ||||
|   constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined, | ||||
|               releaseDate=undefined, rating=undefined, genres=undefined, productionStatus=undefined, | ||||
|               tagline=undefined, runtime=undefined, imdb_id=undefined, popularity=undefined) { | ||||
|   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; | ||||
| @@ -16,27 +31,66 @@ class Movie { | ||||
|     this.runtime = runtime; | ||||
|     this.imdb_id = imdb_id; | ||||
|     this.popularity = popularity; | ||||
|     this.type = 'movie'; | ||||
|     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 { | ||||
|       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 | ||||
|     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) | ||||
|     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 | ||||
|     const _ = undefined; | ||||
|  | ||||
|     return new Movie(null, title, year, summary, _, _, _, rating, _, _, tagline) | ||||
|     return new Movie( | ||||
|       null, | ||||
|       title, | ||||
|       year, | ||||
|       summary, | ||||
|       _, | ||||
|       _, | ||||
|       _, | ||||
|       rating, | ||||
|       _, | ||||
|       _, | ||||
|       tagline | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   createJsonResponse() { | ||||
| @@ -55,7 +109,7 @@ class Movie { | ||||
|       runtime: this.runtime, | ||||
|       imdb_id: this.imdb_id, | ||||
|       type: this.type | ||||
|     } | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| class Person { | ||||
|   constructor( | ||||
|     id, | ||||
|   | ||||
| @@ -1,30 +1,13 @@ | ||||
| class ReleaseDates {  | ||||
|   constructor(id, releases) { | ||||
|     this.id = id; | ||||
|     this.releases = releases; | ||||
|   } | ||||
| const releaseTypeEnum = { | ||||
|   1: "Premier", | ||||
|   2: "Limited theatrical", | ||||
|   3: "Theatrical", | ||||
|   4: "Digital", | ||||
|   5: "Physical", | ||||
|   6: "TV" | ||||
| }; | ||||
|  | ||||
|   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 {  | ||||
| class Release { | ||||
|   constructor(country, releaseDates) { | ||||
|     this.country = country; | ||||
|     this.releaseDates = releaseDates; | ||||
| @@ -33,8 +16,10 @@ class Release { | ||||
|   createJsonResponse() { | ||||
|     return { | ||||
|       country: this.country, | ||||
|       release_dates: this.releaseDates.map(releaseDate => releaseDate.createJsonResponse()) | ||||
|     } | ||||
|       release_dates: this.releaseDates.map(releaseDate => | ||||
|         releaseDate.createJsonResponse() | ||||
|       ) | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -47,21 +32,13 @@ class ReleaseDate { | ||||
|     this.note = note; | ||||
|   } | ||||
|  | ||||
|   releaseTypeLookup(releaseTypeKey) { | ||||
|     const releaseTypeEnum = { | ||||
|       1: 'Premier', | ||||
|       2: 'Limited theatrical', | ||||
|       3: 'Theatrical', | ||||
|       4: 'Digital', | ||||
|       5: 'Physical', | ||||
|       6: 'TV' | ||||
|     } | ||||
|   static releaseTypeLookup(releaseTypeKey) { | ||||
|     if (releaseTypeKey <= Object.keys(releaseTypeEnum).length) { | ||||
|       return releaseTypeEnum[releaseTypeKey] | ||||
|     } else { | ||||
|       // TODO log | Release type not defined, does this need updating? | ||||
|       return null | ||||
|       return releaseTypeEnum[releaseTypeKey]; | ||||
|     } | ||||
|  | ||||
|     // TODO log | Release type not defined, does this need updating? | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   createJsonResponse() { | ||||
| @@ -71,7 +48,44 @@ class ReleaseDate { | ||||
|       release_date: this.releaseDate, | ||||
|       type: this.type, | ||||
|       note: this.note | ||||
|     } | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
| 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()) | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,20 @@ | ||||
| /* eslint-disable camelcase */ | ||||
|  | ||||
| class Show { | ||||
|   constructor(id, title, year=undefined, overview=undefined, poster=undefined, backdrop=undefined, | ||||
|               seasons=undefined, episodes=undefined, rank=undefined, genres=undefined, status=undefined, | ||||
|               runtime=undefined) { | ||||
|   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; | ||||
| @@ -14,18 +27,44 @@ class Show { | ||||
|     this.genres = genres; | ||||
|     this.productionStatus = status; | ||||
|     this.runtime = runtime; | ||||
|     this.type = 'show'; | ||||
|     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 { | ||||
|       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 | ||||
|     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) | ||||
|     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() { | ||||
| @@ -43,7 +82,7 @@ class Show { | ||||
|       production_status: this.productionStatus, | ||||
|       runtime: this.runtime, | ||||
|       type: this.type | ||||
|      } | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| const User = require("./user"); | ||||
| const jwt = require("jsonwebtoken"); | ||||
| const User = require("./user"); | ||||
|  | ||||
| class Token { | ||||
|   constructor(user, admin = false, settings = null) { | ||||
| @@ -16,8 +16,8 @@ class Token { | ||||
|   toString(secret) { | ||||
|     const { user, admin, settings } = this; | ||||
|  | ||||
|     let data = { username: user.username, settings }; | ||||
|     if (admin) data["admin"] = admin; | ||||
|     const data = { username: user.username, settings }; | ||||
|     if (admin) data.admin = admin; | ||||
|  | ||||
|     return jwt.sign(data, secret, { expiresIn: "90d" }); | ||||
|   } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| class User { | ||||
|    constructor(username, email=undefined) { | ||||
|       this.username = username; | ||||
|       this.email = email; | ||||
|    } | ||||
|   constructor(username, email = undefined) { | ||||
|     this.username = username; | ||||
|     this.email = email; | ||||
|   } | ||||
| } | ||||
|  | ||||
| module.exports = User; | ||||
|   | ||||
| @@ -1,6 +1,69 @@ | ||||
| const assert = require("assert"); | ||||
| const establishedDatabase = require("../database/database"); | ||||
|  | ||||
| class LinkPlexUserError extends Error { | ||||
|   constructor(errorMessage = null) { | ||||
|     const message = | ||||
|       "An unexpected error occured while linking plex and seasoned accounts"; | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 500; | ||||
|     this.errorMessage = errorMessage; | ||||
|     this.source = "database"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class UnlinkPlexUserError extends Error { | ||||
|   constructor(errorMessage = null) { | ||||
|     const message = | ||||
|       "An unexpected error occured while unlinking plex and seasoned accounts"; | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 500; | ||||
|     this.errorMessage = errorMessage; | ||||
|     this.source = "database"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class UnexpectedUserSettingsError extends Error { | ||||
|   constructor(errorMessage = null) { | ||||
|     const message = | ||||
|       "An unexpected error occured while fetching settings for your account"; | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 500; | ||||
|     this.errorMessage = errorMessage; | ||||
|     this.source = "database"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| class NoSettingsUserNotFoundError extends Error { | ||||
|   constructor() { | ||||
|     const message = "User not found, no settings to get"; | ||||
|     super(message); | ||||
|  | ||||
|     this.statusCode = 404; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const rejectUnexpectedDatabaseError = ( | ||||
|   message, | ||||
|   status, | ||||
|   error, | ||||
|   reject = null | ||||
| ) => { | ||||
|   const body = { | ||||
|     status, | ||||
|     message, | ||||
|     source: "seasoned database" | ||||
|   }; | ||||
|  | ||||
|   if (reject == null) { | ||||
|     return new Promise((_, reject) => reject(body)); | ||||
|   } | ||||
|   return reject(body); | ||||
| }; | ||||
|  | ||||
| class UserRepository { | ||||
|   constructor(database) { | ||||
|     this.database = database || establishedDatabase; | ||||
| @@ -51,8 +114,7 @@ class UserRepository { | ||||
|         assert(row, "The user does not exist."); | ||||
|         return row.password; | ||||
|       }) | ||||
|       .catch(err => { | ||||
|         console.log(error); | ||||
|       .catch(() => { | ||||
|         throw new Error("Unable to find your user."); | ||||
|       }); | ||||
|   } | ||||
| @@ -78,17 +140,7 @@ class UserRepository { | ||||
|       this.database | ||||
|         .run(this.queries.link, [plexUserID, username]) | ||||
|         .then(row => resolve(row)) | ||||
|         .catch(error => { | ||||
|           // TODO log this unknown db error | ||||
|           console.error("db error", error); | ||||
|  | ||||
|           reject({ | ||||
|             status: 500, | ||||
|             message: | ||||
|               "An unexpected error occured while linking plex and seasoned accounts", | ||||
|             source: "seasoned database" | ||||
|           }); | ||||
|         }); | ||||
|         .catch(error => reject(new LinkPlexUserError(error))); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @@ -102,17 +154,7 @@ class UserRepository { | ||||
|       this.database | ||||
|         .run(this.queries.unlink, username) | ||||
|         .then(row => resolve(row)) | ||||
|         .catch(error => { | ||||
|           // TODO log this unknown db error | ||||
|           console.log("db error", error); | ||||
|  | ||||
|           reject({ | ||||
|             status: 500, | ||||
|             message: | ||||
|               "An unexpected error occured while unlinking plex and seasoned accounts", | ||||
|             source: "seasoned database" | ||||
|           }); | ||||
|         }); | ||||
|         .catch(error => reject(new UnlinkPlexUserError(error))); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @@ -138,6 +180,7 @@ class UserRepository { | ||||
|         .get(this.queries.getSettings, username) | ||||
|         .then(async row => { | ||||
|           if (row == null) { | ||||
|             // eslint-disable-next-line no-console | ||||
|             console.debug( | ||||
|               `settings do not exist for user: ${username}. Creating settings entry.` | ||||
|             ); | ||||
| @@ -153,27 +196,13 @@ class UserRepository { | ||||
|                 reject(error); | ||||
|               } | ||||
|             } else { | ||||
|               reject({ | ||||
|                 status: 404, | ||||
|                 message: "User not found, no settings to get" | ||||
|               }); | ||||
|               reject(new NoSettingsUserNotFoundError()); | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           resolve(row); | ||||
|         }) | ||||
|         .catch(error => { | ||||
|           console.error( | ||||
|             "Unexpected error occured while fetching settings for your account. Error:", | ||||
|             error | ||||
|           ); | ||||
|           reject({ | ||||
|             status: 500, | ||||
|             message: | ||||
|               "An unexpected error occured while fetching settings for your account", | ||||
|             source: "seasoned database" | ||||
|           }); | ||||
|         }); | ||||
|         .catch(error => reject(new UnexpectedUserSettingsError(error))); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @@ -184,12 +213,12 @@ class UserRepository { | ||||
|    * @param {String} emoji | ||||
|    * @returns {Promsie} | ||||
|    */ | ||||
|   updateSettings(username, dark_mode = undefined, emoji = undefined) { | ||||
|   updateSettings(username, _darkMode = null, _emoji = null) { | ||||
|     const settings = this.getSettings(username); | ||||
|     dark_mode = dark_mode !== undefined ? dark_mode : settings.dark_mode; | ||||
|     emoji = emoji !== undefined ? emoji : settings.emoji; | ||||
|     const darkMode = _darkMode || settings.darkMode; | ||||
|     const emoji = _emoji || settings.emoji; | ||||
|  | ||||
|     return this.dbUpdateSettings(username, dark_mode, emoji).catch(error => { | ||||
|     return this.dbUpdateSettings(username, darkMode, emoji).catch(error => { | ||||
|       if (error.status && error.message) { | ||||
|         return error; | ||||
|       } | ||||
| @@ -225,32 +254,13 @@ class UserRepository { | ||||
|    * @param {String} username | ||||
|    * @returns {Promsie} | ||||
|    */ | ||||
|   dbUpdateSettings(username, dark_mode, emoji) { | ||||
|     return new Promise((resolve, reject) => | ||||
|   dbUpdateSettings(username, darkMode, emoji) { | ||||
|     return new Promise(resolve => | ||||
|       this.database | ||||
|         .run(this.queries.updateSettings, [username, dark_mode, emoji]) | ||||
|         .run(this.queries.updateSettings, [username, darkMode, emoji]) | ||||
|         .then(row => resolve(row)) | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| const rejectUnexpectedDatabaseError = ( | ||||
|   message, | ||||
|   status, | ||||
|   error, | ||||
|   reject = null | ||||
| ) => { | ||||
|   console.error(error); | ||||
|   const body = { | ||||
|     status, | ||||
|     message, | ||||
|     source: "seasoned database" | ||||
|   }; | ||||
|  | ||||
|   if (reject == null) { | ||||
|     return new Promise((resolve, reject) => reject(body)); | ||||
|   } | ||||
|   reject(body); | ||||
| }; | ||||
|  | ||||
| module.exports = UserRepository; | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class UserSecurity { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       bcrypt.compare(clearPassword, hash, (error, match) => { | ||||
|         if (match) resolve(true); | ||||
|         reject(false); | ||||
|         reject(error); | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
| @@ -61,7 +61,7 @@ class UserSecurity { | ||||
|    * @returns {Promise} | ||||
|    */ | ||||
|   static hashPassword(clearPassword) { | ||||
|     return new Promise(resolve => { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       const saltRounds = 10; | ||||
|       bcrypt.hash(clearPassword, saltRounds, (error, hash) => { | ||||
|         if (error) reject(error); | ||||
|   | ||||
| @@ -11,7 +11,7 @@ const mustBeAdmin = require("./middleware/mustBeAdmin"); | ||||
| const mustHaveAccountLinkedToPlex = require("./middleware/mustHaveAccountLinkedToPlex"); | ||||
|  | ||||
| const listController = require("./controllers/list/listController"); | ||||
| const tautulli = require("./controllers/user/viewHistory.js"); | ||||
| const tautulli = require("./controllers/user/viewHistory"); | ||||
| const SettingsController = require("./controllers/user/settings"); | ||||
| const AuthenticatePlexAccountController = require("./controllers/user/authenticatePlexAccount"); | ||||
|  | ||||
| @@ -24,7 +24,7 @@ app.use(bodyParser.json()); | ||||
| app.use(cookieParser()); | ||||
|  | ||||
| const router = express.Router(); | ||||
| const allowedOrigins = configuration.get("webserver", "origins"); | ||||
| // const allowedOrigins = configuration.get("webserver", "origins"); | ||||
|  | ||||
| // TODO: All JSON handling in a single router | ||||
| // router.use(bodyParser.json()); | ||||
| @@ -56,18 +56,19 @@ router.get("/", (req, res) => { | ||||
|   res.send("welcome to seasoned api"); | ||||
| }); | ||||
|  | ||||
| app.use(Raven.errorHandler()); | ||||
| app.use((err, req, res, next) => { | ||||
|   res.statusCode = 500; | ||||
|   res.end(res.sentry + "\n"); | ||||
| }); | ||||
| // app.use(Raven.errorHandler()); | ||||
| // app.use((err, req, res) => { | ||||
| //   res.statusCode = 500; | ||||
| //   res.end(`${res.sentry}\n`); | ||||
| // }); | ||||
|  | ||||
| /** | ||||
|  * User | ||||
|  */ | ||||
| router.post("/v1/user", require("./controllers/user/register.js")); | ||||
| router.post("/v1/user/login", require("./controllers/user/login.js")); | ||||
| router.post("/v1/user/logout", require("./controllers/user/logout.js")); | ||||
| router.post("/v1/user", require("./controllers/user/register")); | ||||
| router.post("/v1/user/login", require("./controllers/user/login")); | ||||
| router.post("/v1/user/logout", require("./controllers/user/logout")); | ||||
|  | ||||
| router.get( | ||||
|   "/v1/user/settings", | ||||
|   mustBeAuthenticated, | ||||
| @@ -81,13 +82,14 @@ router.put( | ||||
| router.get( | ||||
|   "/v1/user/search_history", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/user/searchHistory.js") | ||||
|   require("./controllers/user/searchHistory") | ||||
| ); | ||||
| router.get( | ||||
|   "/v1/user/requests", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/user/requests.js") | ||||
|   require("./controllers/user/requests") | ||||
| ); | ||||
|  | ||||
| router.post( | ||||
|   "/v1/user/link_plex", | ||||
|   mustBeAuthenticated, | ||||
| @@ -123,46 +125,40 @@ router.get( | ||||
| /** | ||||
|  * Seasoned | ||||
|  */ | ||||
| router.get("/v1/seasoned/all", require("./controllers/seasoned/readStrays.js")); | ||||
| router.get("/v1/seasoned/all", require("./controllers/seasoned/readStrays")); | ||||
| router.get( | ||||
|   "/v1/seasoned/:strayId", | ||||
|   require("./controllers/seasoned/strayById.js") | ||||
|   require("./controllers/seasoned/strayById") | ||||
| ); | ||||
| router.post( | ||||
|   "/v1/seasoned/verify/:strayId", | ||||
|   require("./controllers/seasoned/verifyStray.js") | ||||
|   require("./controllers/seasoned/verifyStray") | ||||
| ); | ||||
|  | ||||
| router.get("/v2/search/", require("./controllers/search/multiSearch.js")); | ||||
| router.get("/v2/search/movie", require("./controllers/search/movieSearch.js")); | ||||
| router.get("/v2/search/show", require("./controllers/search/showSearch.js")); | ||||
| router.get( | ||||
|   "/v2/search/person", | ||||
|   require("./controllers/search/personSearch.js") | ||||
| ); | ||||
| router.get("/v2/search/", require("./controllers/search/multiSearch")); | ||||
| router.get("/v2/search/movie", require("./controllers/search/movieSearch")); | ||||
| router.get("/v2/search/show", require("./controllers/search/showSearch")); | ||||
| router.get("/v2/search/person", require("./controllers/search/personSearch")); | ||||
|  | ||||
| router.get("/v2/movie/now_playing", listController.nowPlayingMovies); | ||||
| router.get("/v2/movie/popular", listController.popularMovies); | ||||
| router.get("/v2/movie/top_rated", listController.topRatedMovies); | ||||
| router.get("/v2/movie/upcoming", listController.upcomingMovies); | ||||
| router.get("/v2/movie/:id/credits", require("./controllers/movie/credits.js")); | ||||
| router.get("/v2/movie/:id/credits", require("./controllers/movie/credits")); | ||||
| router.get( | ||||
|   "/v2/movie/:id/release_dates", | ||||
|   require("./controllers/movie/releaseDates.js") | ||||
|   require("./controllers/movie/releaseDates") | ||||
| ); | ||||
| router.get("/v2/movie/:id", require("./controllers/movie/info.js")); | ||||
| router.get("/v2/movie/:id", require("./controllers/movie/info")); | ||||
|  | ||||
| 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/show/:id/credits", require("./controllers/show/credits.js")); | ||||
| router.get("/v2/show/:id", require("./controllers/show/info.js")); | ||||
| router.get("/v2/show/:id/credits", require("./controllers/show/credits")); | ||||
| router.get("/v2/show/:id", require("./controllers/show/info")); | ||||
|  | ||||
| router.get( | ||||
|   "/v2/person/:id/credits", | ||||
|   require("./controllers/person/credits.js") | ||||
| ); | ||||
| router.get("/v2/person/:id", require("./controllers/person/info.js")); | ||||
| router.get("/v2/person/:id/credits", require("./controllers/person/credits")); | ||||
| router.get("/v2/person/:id", require("./controllers/person/info")); | ||||
|  | ||||
| /** | ||||
|  * Plex | ||||
| @@ -172,40 +168,40 @@ router.get("/v2/plex/search", require("./controllers/plex/search")); | ||||
| /** | ||||
|  * List | ||||
|  */ | ||||
| router.get("/v1/plex/search", require("./controllers/plex/searchMedia.js")); | ||||
| router.get("/v1/plex/playing", require("./controllers/plex/plexPlaying.js")); | ||||
| router.get("/v1/plex/request", require("./controllers/plex/searchRequest.js")); | ||||
| router.get("/v1/plex/search", require("./controllers/plex/searchMedia")); | ||||
| router.get("/v1/plex/playing", require("./controllers/plex/plexPlaying")); | ||||
| router.get("/v1/plex/request", require("./controllers/plex/searchRequest")); | ||||
| router.get( | ||||
|   "/v1/plex/request/:mediaId", | ||||
|   require("./controllers/plex/readRequest.js") | ||||
|   require("./controllers/plex/readRequest") | ||||
| ); | ||||
| router.post( | ||||
|   "/v1/plex/request/:mediaId", | ||||
|   require("./controllers/plex/submitRequest.js") | ||||
|   require("./controllers/plex/submitRequest") | ||||
| ); | ||||
| router.post("/v1/plex/hook", require("./controllers/plex/hookDump.js")); | ||||
| router.post("/v1/plex/hook", require("./controllers/plex/hookDump")); | ||||
|  | ||||
| router.get( | ||||
|   "/v1/plex/watch-link", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/plex/watchDirectLink.js") | ||||
|   require("./controllers/plex/watchDirectLink") | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Requests | ||||
|  */ | ||||
|  | ||||
| router.get("/v2/request", require("./controllers/request/fetchAllRequests.js")); | ||||
| router.get("/v2/request/:id", require("./controllers/request/getRequest.js")); | ||||
| router.post("/v2/request", require("./controllers/request/requestTmdbId.js")); | ||||
| router.get("/v2/request", require("./controllers/request/fetchAllRequests")); | ||||
| router.get("/v2/request/:id", require("./controllers/request/getRequest")); | ||||
| router.post("/v2/request", require("./controllers/request/requestTmdbId")); | ||||
| router.get( | ||||
|   "/v1/plex/requests/all", | ||||
|   require("./controllers/plex/fetchRequested.js") | ||||
|   require("./controllers/plex/fetchRequested") | ||||
| ); | ||||
| router.put( | ||||
|   "/v1/plex/request/:requestId", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/plex/updateRequested.js") | ||||
|   require("./controllers/plex/updateRequested") | ||||
| ); | ||||
|  | ||||
| /** | ||||
| @@ -213,24 +209,24 @@ router.put( | ||||
|  */ | ||||
| router.get( | ||||
|   "/v1/pirate/search", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/pirate/searchTheBay.js") | ||||
|   mustBeAdmin, | ||||
|   require("./controllers/pirate/searchTheBay") | ||||
| ); | ||||
| router.post( | ||||
|   "/v1/pirate/add", | ||||
|   mustBeAuthenticated, | ||||
|   require("./controllers/pirate/addMagnet.js") | ||||
|   mustBeAdmin, | ||||
|   require("./controllers/pirate/addMagnet") | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * git | ||||
|  */ | ||||
| router.post("/v1/git/dump", require("./controllers/git/dumpHook.js")); | ||||
| router.post("/v1/git/dump", require("./controllers/git/dumpHook")); | ||||
|  | ||||
| /** | ||||
|  * misc | ||||
|  */ | ||||
| router.get("/v1/emoji", require("./controllers/misc/emoji.js")); | ||||
| router.get("/v1/emoji", require("./controllers/misc/emoji")); | ||||
|  | ||||
| // REGISTER OUR ROUTES ------------------------------- | ||||
| // all of our routes will be prefixed with /api | ||||
|   | ||||
| @@ -5,12 +5,8 @@ const gitRepository = new GitRepository(); | ||||
| function dumpHookController(req, res) { | ||||
|   gitRepository | ||||
|     .dumpHook(req.body) | ||||
|     .then(() => { | ||||
|       res.status(200); | ||||
|     }) | ||||
|     .catch(error => { | ||||
|       res.status(500); | ||||
|     }); | ||||
|     .then(() => res.status(200)) | ||||
|     .catch(() => res.status(500)); | ||||
| } | ||||
|  | ||||
| module.exports = dumpHookController; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
|  | ||||
| // there should be a translate function from query params to | ||||
| @@ -14,21 +15,13 @@ const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| // + newly created (tv/latest). | ||||
| // + movie/latest | ||||
| // | ||||
| function handleError(error, res) { | ||||
|   const { status, message } = error; | ||||
|  | ||||
|   if (status && message) { | ||||
|     res.status(status).send({ success: false, message }); | ||||
|   } else { | ||||
|     console.log("caught list controller error", error); | ||||
|     res | ||||
|       .status(500) | ||||
|       .send({ message: "An unexpected error occured while requesting list" }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleListResponse(response, res) { | ||||
|   return res.send(response).catch(error => handleError(error, res)); | ||||
| function handleError(listname, error, res) { | ||||
|   return res.status(error?.statusCode || 500).send({ | ||||
|     success: false, | ||||
|     message: | ||||
|       error?.message || | ||||
|       `An unexpected error occured while requesting list with id: ${listname}` | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function fetchTmdbList(req, res, listname, type) { | ||||
| @@ -38,15 +31,17 @@ function fetchTmdbList(req, res, listname, type) { | ||||
|     return tmdb | ||||
|       .movieList(listname, page) | ||||
|       .then(listResponse => res.send(listResponse)) | ||||
|       .catch(error => handleError(error, res)); | ||||
|   } else if (type === "show") { | ||||
|       .catch(error => handleError(listname, error, res)); | ||||
|   } | ||||
|   if (type === "show") { | ||||
|     return tmdb | ||||
|       .showList(listname, page) | ||||
|       .then(listResponse => res.send(listResponse)) | ||||
|       .catch(error => handleError(error, res)); | ||||
|       .catch(error => handleError(listname, error, res)); | ||||
|   } | ||||
|  | ||||
|   handleError( | ||||
|   return handleError( | ||||
|     listname, | ||||
|     { | ||||
|       status: 400, | ||||
|       message: `'${type}' is not a valid list type.` | ||||
|   | ||||
| @@ -10,20 +10,12 @@ const movieCreditsController = (req, res) => { | ||||
|     .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" | ||||
|           }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting credits for movie with id: ${movieId}` | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,22 +1,10 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const Plex = require("../../../plex/plex"); | ||||
|  | ||||
| const tmdb = new TMDB(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 | ||||
| @@ -25,21 +13,18 @@ function handleError(error, res) { | ||||
|  */ | ||||
| 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 credits = req.query?.credits; | ||||
|   let releaseDates = req.query?.release_dates; | ||||
|   let checkExistance = req.query?.check_existance; | ||||
|  | ||||
|   let tmdbQueue = [tmdb.movieInfo(movieId)]; | ||||
|   credits = credits?.toLowerCase() === "true"; | ||||
|   releaseDates = releaseDates?.toLowerCase() === "true"; | ||||
|   checkExistance = checkExistance?.toLowerCase() === "true"; | ||||
|  | ||||
|   const tmdbQueue = [tmdb.movieInfo(movieId)]; | ||||
|   if (credits) tmdbQueue.push(tmdb.movieCredits(movieId)); | ||||
|   if (release_dates) tmdbQueue.push(tmdb.movieReleaseDates(movieId)); | ||||
|   if (releaseDates) tmdbQueue.push(tmdb.movieReleaseDates(movieId)); | ||||
|  | ||||
|   try { | ||||
|     const [Movie, Credits, ReleaseDates] = await Promise.all(tmdbQueue); | ||||
| @@ -47,24 +32,22 @@ async function movieInfoController(req, res) { | ||||
|     const movie = Movie.createJsonResponse(); | ||||
|     if (Credits) movie.credits = Credits.createJsonResponse(); | ||||
|     if (ReleaseDates) | ||||
|       movie.release_dates = ReleaseDates.createJsonResponse().results; | ||||
|       movie.releaseDates = ReleaseDates.createJsonResponse().results; | ||||
|  | ||||
|     if (check_existance) { | ||||
|     if (checkExistance) { | ||||
|       try { | ||||
|         movie.exists_in_plex = await plex.existsInPlex(movie); | ||||
|       } catch (error) { | ||||
|         if (error.status === 401) { | ||||
|           console.log("Unathorized request, check plex server LAN settings"); | ||||
|         } else { | ||||
|           console.log("Unkown error from plex!"); | ||||
|         } | ||||
|         console.log(error?.message); | ||||
|       } | ||||
|       } catch {} | ||||
|     } | ||||
|  | ||||
|     res.send(movie); | ||||
|     return res.send(movie); | ||||
|   } catch (error) { | ||||
|     handleError(error, res); | ||||
|     return res.status(error?.statusCode || 500).send({ | ||||
|       success: false, | ||||
|       message: | ||||
|         error?.message || | ||||
|         `An unexpected error occured while requesting info for with id: ${movieId}` | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,20 +10,12 @@ const movieReleaseDatesController = (req, res) => { | ||||
|     .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" | ||||
|           }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting release dates for movie with id: ${movieId}` | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
|  | ||||
| const personCreditsController = (req, res) => { | ||||
| @@ -9,17 +10,12 @@ const personCreditsController = (req, res) => { | ||||
|     .personCredits(personId) | ||||
|     .then(credits => res.send(credits)) | ||||
|     .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 person credits" | ||||
|         }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting info for person with id ${personId}.` | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,8 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
|  | ||||
| function handleError(error, res) { | ||||
|   const { status, message } = error; | ||||
|  | ||||
|   if (status && message) { | ||||
|     res.status(status).send({ success: false, message }); | ||||
|   } else { | ||||
|     console.log("caught personinfo controller error", error); | ||||
|     res.status(500).send({ | ||||
|       message: "An unexpected error occured while requesting person info." | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Controller: Retrieve information for a person | ||||
|  * @param {Request} req http request variable | ||||
| @@ -25,13 +13,10 @@ function handleError(error, res) { | ||||
| async function personInfoController(req, res) { | ||||
|   const personId = req.params.id; | ||||
|   let { credits } = req.query; | ||||
|   arguments; | ||||
|  | ||||
|   credits && credits.toLowerCase() === "true" | ||||
|     ? (credits = true) | ||||
|     : (credits = false); | ||||
|   credits = credits?.toLowerCase() === "true"; | ||||
|  | ||||
|   let tmdbQueue = [tmdb.personInfo(personId)]; | ||||
|   const tmdbQueue = [tmdb.personInfo(personId)]; | ||||
|   if (credits) tmdbQueue.push(tmdb.personCredits(personId)); | ||||
|  | ||||
|   try { | ||||
| @@ -42,7 +27,12 @@ async function personInfoController(req, res) { | ||||
|  | ||||
|     return res.send(person); | ||||
|   } catch (error) { | ||||
|     handleError(error, res); | ||||
|     return res.status(error?.statusCode || 500).send({ | ||||
|       success: false, | ||||
|       message: | ||||
|         error?.message || | ||||
|         `An unexpected error occured while requesting info for person with id: ${personId}` | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -8,14 +8,11 @@ | ||||
| const PirateRepository = require("../../../pirate/pirateRepository"); | ||||
|  | ||||
| function addMagnet(req, res) { | ||||
|   const magnet = req.body.magnet; | ||||
|   const name = req.body.name; | ||||
|   const tmdb_id = req.body.tmdb_id; | ||||
|   const { magnet, name } = req.body; | ||||
|   const tmdbId = req.body?.tmdb_id; | ||||
|  | ||||
|   PirateRepository.AddMagnet(magnet, name, tmdb_id) | ||||
|     .then(result => { | ||||
|       res.send(result); | ||||
|     }) | ||||
|   PirateRepository.AddMagnet(magnet, name, tmdbId) | ||||
|     .then(result => res.send(result)) | ||||
|     .catch(error => { | ||||
|       res.status(500).send({ success: false, message: error.message }); | ||||
|     }); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| const RequestRepository = require("../../../plex/requestRepository.js"); | ||||
| const RequestRepository = require("../../../plex/requestRepository"); | ||||
|  | ||||
| const requestRepository = new RequestRepository(); | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,8 @@ | ||||
| /* | ||||
| * @Author: KevinMidboe | ||||
| * @Date:   2017-05-03 23:26:46 | ||||
| * @Last Modified by:   KevinMidboe | ||||
| * @Last Modified time: 2018-02-06 20:54:22 | ||||
| */ | ||||
|  | ||||
| function hookDumpController(req, res) { | ||||
|    console.log(req); | ||||
|   // eslint-disable-next-line no-console | ||||
|   console.log("plex hook dump:", req); | ||||
|  | ||||
|   res.status(200); | ||||
| } | ||||
|  | ||||
| module.exports = hookDumpController; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ const requestRepository = new RequestRepository(); | ||||
|  * @returns {Callback} | ||||
|  */ | ||||
| function readRequestController(req, res) { | ||||
|   const mediaId = req.params.mediaId; | ||||
|   const { mediaId } = req.params; | ||||
|   const { type } = req.query; | ||||
|   requestRepository | ||||
|     .lookup(mediaId, type) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const Plex = require("../../../plex/plex"); | ||||
|  | ||||
| const plex = new Plex(configuration.get("plex", "ip")); | ||||
|  | ||||
| /** | ||||
| @@ -16,12 +17,10 @@ function searchPlexController(req, res) { | ||||
|       if (movies.length > 0) { | ||||
|         res.send(movies); | ||||
|       } else { | ||||
|         res | ||||
|           .status(404) | ||||
|           .send({ | ||||
|             success: false, | ||||
|             message: "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 => { | ||||
|   | ||||
| @@ -19,12 +19,10 @@ function searchMediaController(req, res) { | ||||
|       if (media !== undefined || media.length > 0) { | ||||
|         res.send(media); | ||||
|       } else { | ||||
|         res | ||||
|           .status(404) | ||||
|           .send({ | ||||
|             success: false, | ||||
|             message: "Search query did not return any results." | ||||
|           }); | ||||
|         res.status(404).send({ | ||||
|           success: false, | ||||
|           message: "Search query did not return any results." | ||||
|         }); | ||||
|       } | ||||
|     }) | ||||
|     .catch(error => { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| const SearchHistory = require("../../../searchHistory/searchHistory"); | ||||
| const Cache = require("../../../tmdb/cache"); | ||||
| const RequestRepository = require("../../../plex/requestRepository.js"); | ||||
| const RequestRepository = require("../../../plex/requestRepository"); | ||||
|  | ||||
| const cache = new Cache(); | ||||
| const requestRepository = new RequestRepository(cache); | ||||
| @@ -10,8 +10,8 @@ function searchRequestController(req, res) { | ||||
|   const { query, page, type } = req.query; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|  | ||||
|   Promise.resolve() | ||||
|     .then(() => searchHistory.create(username, query)) | ||||
|   searchHistory | ||||
|     .create(username, query) | ||||
|     .then(() => requestRepository.search(query, page, type)) | ||||
|     .then(searchResult => { | ||||
|       res.send(searchResult); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const RequestRepository = require("../../../request/request"); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| const request = new RequestRepository(); | ||||
|  | ||||
| @@ -23,16 +24,14 @@ function submitRequestController(req, res) { | ||||
|   const id = req.params.mediaId; | ||||
|   const type = req.query.type ? req.query.type.toLowerCase() : undefined; | ||||
|   const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; | ||||
|   const user_agent = req.headers["user-agent"]; | ||||
|   const userAgent = req.headers["user-agent"]; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|  | ||||
|   let mediaFunction = undefined; | ||||
|   let mediaFunction; | ||||
|  | ||||
|   if (type === "movie") { | ||||
|     console.log("movie"); | ||||
|     mediaFunction = tmdbMovieInfo; | ||||
|   } else if (type === "show") { | ||||
|     console.log("show"); | ||||
|     mediaFunction = tmdbShowInfo; | ||||
|   } else { | ||||
|     res.status(422).send({ | ||||
| @@ -48,7 +47,7 @@ function submitRequestController(req, res) { | ||||
|  | ||||
|   mediaFunction(id) | ||||
|     .then(tmdbMedia => | ||||
|       request.requestFromTmdb(tmdbMedia, ip, user_agent, username) | ||||
|       request.requestFromTmdb(tmdbMedia, ip, userAgent, username) | ||||
|     ) | ||||
|     .then(() => | ||||
|       res.send({ success: true, message: "Media item successfully requested" }) | ||||
|   | ||||
| @@ -10,8 +10,8 @@ const requestRepository = new RequestRepository(); | ||||
|  */ | ||||
| function updateRequested(req, res) { | ||||
|   const id = req.params.requestId; | ||||
|   const type = req.body.type; | ||||
|   const status = req.body.status; | ||||
|   const { type } = req.body; | ||||
|   const { status } = req.body; | ||||
|  | ||||
|   requestRepository | ||||
|     .updateRequestedById(id, type, status) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const Plex = require("../../../plex/plex"); | ||||
|  | ||||
| const plex = new Plex(configuration.get("plex", "ip")); | ||||
|  | ||||
| /** | ||||
| @@ -15,7 +16,7 @@ function watchDirectLink(req, res) { | ||||
|   plex | ||||
|     .getDirectLinkByTitleAndYear(title, year) | ||||
|     .then(plexDirectLink => { | ||||
|       if (plexDirectLink == false) | ||||
|       if (plexDirectLink === false) | ||||
|         res.status(404).send({ success: true, link: null }); | ||||
|       else res.status(200).send({ success: true, link: plexDirectLink }); | ||||
|     }) | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const RequestRepository = require("../../../request/request"); | ||||
|  | ||||
| const request = new RequestRepository(); | ||||
|  | ||||
| /** | ||||
| @@ -8,19 +9,18 @@ const request = new RequestRepository(); | ||||
|  * @returns {Callback} | ||||
|  */ | ||||
| function fetchAllRequests(req, res) { | ||||
|   let { page, filter, sort, query } = req.query; | ||||
|   let sort_by = sort; | ||||
|   let sort_direction = undefined; | ||||
|   const { page, filter } = req.query; | ||||
|  | ||||
|   if (sort !== undefined && sort.includes(":")) { | ||||
|     [sort_by, sort_direction] = sort.split(":"); | ||||
|   } | ||||
|  | ||||
|   Promise.resolve() | ||||
|     .then(() => request.fetchAll(page, sort_by, sort_direction, filter, query)) | ||||
|   request | ||||
|     .fetchAll(page, filter) | ||||
|     .then(result => res.send(result)) | ||||
|     .catch(error => { | ||||
|       res.status(404).send({ success: false, message: error.message }); | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting all requests` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const RequestRepository = require("../../../request/request"); | ||||
|  | ||||
| const request = new RequestRepository(); | ||||
|  | ||||
| /** | ||||
| @@ -8,14 +9,19 @@ const request = new RequestRepository(); | ||||
|  * @returns {Callback} | ||||
|  */ | ||||
| function fetchAllRequests(req, res) { | ||||
|   const id = req.params.id; | ||||
|   const { id } = req.params; | ||||
|   const { type } = req.query; | ||||
|  | ||||
|   request | ||||
|     .getRequestByIdAndType(id, type) | ||||
|     .then(result => res.send(result)) | ||||
|     .catch(error => { | ||||
|       res.status(404).send({ success: false, message: error.message }); | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting request with id: ${id}` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const RequestRepository = require("../../../request/request"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| const request = new RequestRepository(); | ||||
| // const { sendSMS } = require("src/notifications/sms"); | ||||
| @@ -23,10 +24,10 @@ function requestTmdbIdController(req, res) { | ||||
|   const { id, type } = req.body; | ||||
|  | ||||
|   const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; | ||||
|   const user_agent = req.headers["user-agent"]; | ||||
|   const userAgent = req.headers["user-agent"]; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|  | ||||
|   let mediaFunction = undefined; | ||||
|   let mediaFunction; | ||||
|  | ||||
|   if (id === undefined || type === undefined) { | ||||
|     res.status(422).send({ | ||||
| @@ -49,7 +50,7 @@ function requestTmdbIdController(req, res) { | ||||
|   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, username); | ||||
|       request.requestFromTmdb(tmdbMedia, ip, userAgent, username); | ||||
|  | ||||
|       // TODO enable SMS | ||||
|       // const url = `https://request.movie?${tmdbMedia.type}=${tmdbMedia.id}`; | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const SearchHistory = require("../../../searchHistory/searchHistory"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| const searchHistory = new SearchHistory(); | ||||
|  | ||||
| @@ -13,7 +14,7 @@ const searchHistory = new SearchHistory(); | ||||
| function movieSearchController(req, res) { | ||||
|   const { query, page, adult } = req.query; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|   const includeAdult = adult == "true" ? true : false; | ||||
|   const includeAdult = adult === "true"; | ||||
|  | ||||
|   if (username) { | ||||
|     searchHistory.create(username, query); | ||||
| @@ -23,17 +24,12 @@ function movieSearchController(req, res) { | ||||
|     .movieSearch(query, page, includeAdult) | ||||
|     .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}` | ||||
|         }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while searching movies with query: ${query}` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,10 @@ | ||||
| const configuration = require("../../..//config/configuration").getInstance(); | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const SearchHistory = require("../../../searchHistory/searchHistory"); | ||||
|  | ||||
| const tmdb = new TMDB(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 | ||||
| @@ -20,26 +14,22 @@ function checkAndCreateJsonResponse(result) { | ||||
| function multiSearchController(req, res) { | ||||
|   const { query, page, adult } = req.query; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|   const includeAdult = adult === "true"; | ||||
|  | ||||
|   if (username) { | ||||
|     searchHistory.create(username, query); | ||||
|   } | ||||
|  | ||||
|   return tmdb | ||||
|     .multiSearch(query, page, adult) | ||||
|     .multiSearch(query, page, includeAdult) | ||||
|     .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 res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while searching with query: ${query}` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const SearchHistory = require("../../../searchHistory/searchHistory"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| const searchHistory = new SearchHistory(); | ||||
|  | ||||
| @@ -13,7 +14,7 @@ const searchHistory = new SearchHistory(); | ||||
| function personSearchController(req, res) { | ||||
|   const { query, page, adult } = req.query; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|   const includeAdult = adult == "true" ? true : false; | ||||
|   const includeAdult = adult === "true"; | ||||
|  | ||||
|   if (username) { | ||||
|     searchHistory.create(username, query); | ||||
| @@ -23,17 +24,12 @@ function personSearchController(req, res) { | ||||
|     .personSearch(query, page, includeAdult) | ||||
|     .then(persons => res.send(persons)) | ||||
|     .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}` | ||||
|         }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while searching person with query: ${query}` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| const SearchHistory = require("../../../searchHistory/searchHistory"); | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
| const searchHistory = new SearchHistory(); | ||||
|  | ||||
| @@ -13,7 +14,7 @@ const searchHistory = new SearchHistory(); | ||||
| function showSearchController(req, res) { | ||||
|   const { query, page, adult } = req.query; | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|   const includeAdult = adult == "true" ? true : false; | ||||
|   const includeAdult = adult === "true"; | ||||
|  | ||||
|   if (username) { | ||||
|     searchHistory.create(username, query); | ||||
| @@ -25,7 +26,12 @@ function showSearchController(req, res) { | ||||
|       res.send(shows); | ||||
|     }) | ||||
|     .catch(error => { | ||||
|       res.status(500).send({ success: false, message: error.message }); | ||||
|       res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while searching person with query: ${query}` | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
|  | ||||
| const tmdb = new TMDB(configuration.get("tmdb", "apiKey")); | ||||
|  | ||||
| const showCreditsController = (req, res) => { | ||||
| @@ -9,19 +10,12 @@ const showCreditsController = (req, res) => { | ||||
|     .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" | ||||
|           }); | ||||
|       } | ||||
|       return res.status(error?.statusCode || 500).send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `An unexpected error occured while requesting credits for show with id: ${showId}.` | ||||
|       }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,22 +1,10 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const TMDB = require("../../../tmdb/tmdb"); | ||||
| const Plex = require("../../../plex/plex"); | ||||
|  | ||||
| const tmdb = new TMDB(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 | ||||
| @@ -26,16 +14,13 @@ function handleError(error, res) { | ||||
|  | ||||
| async function showInfoController(req, res) { | ||||
|   const showId = req.params.id; | ||||
|   let { credits, check_existance } = req.query; | ||||
|   let credits = req.query?.credits; | ||||
|   let checkExistance = req.query?.check_existance; | ||||
|  | ||||
|   credits && credits.toLowerCase() === "true" | ||||
|     ? (credits = true) | ||||
|     : (credits = false); | ||||
|   check_existance && check_existance.toLowerCase() === "true" | ||||
|     ? (check_existance = true) | ||||
|     : (check_existance = false); | ||||
|   credits = credits?.toLowerCase() === "true"; | ||||
|   checkExistance = checkExistance?.toLowerCase() === "true"; | ||||
|  | ||||
|   let tmdbQueue = [tmdb.showInfo(showId)]; | ||||
|   const tmdbQueue = [tmdb.showInfo(showId)]; | ||||
|   if (credits) tmdbQueue.push(tmdb.showCredits(showId)); | ||||
|  | ||||
|   try { | ||||
| @@ -44,11 +29,21 @@ async function showInfoController(req, res) { | ||||
|     const show = Show.createJsonResponse(); | ||||
|     if (credits) show.credits = Credits.createJsonResponse(); | ||||
|  | ||||
|     if (check_existance) show.exists_in_plex = await plex.existsInPlex(show); | ||||
|     if (checkExistance) { | ||||
|       /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */ | ||||
|       try { | ||||
|         show.exists_in_plex = await plex.existsInPlex(show); | ||||
|       } catch {} | ||||
|     } | ||||
|  | ||||
|     res.send(show); | ||||
|     return res.send(show); | ||||
|   } catch (error) { | ||||
|     handleError(error, res); | ||||
|     return res.status(error?.statusCode || 500).send({ | ||||
|       success: false, | ||||
|       message: | ||||
|         error?.message || | ||||
|         `An unexpected error occured while requesting info for show with id: ${showId}` | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,33 @@ | ||||
| const UserRepository = require("../../../user/userRepository"); | ||||
| const userRepository = new UserRepository(); | ||||
| const fetch = require("node-fetch"); | ||||
| const FormData = require("form-data"); | ||||
| const UserRepository = require("../../../user/userRepository"); | ||||
|  | ||||
| const userRepository = new UserRepository(); | ||||
|  | ||||
| class PlexAuthenticationError extends Error { | ||||
|   constructor(errorResponse, statusCode) { | ||||
|     const message = | ||||
|       "Unexptected error while authenticating to plex signin api. View error response."; | ||||
|     super(message); | ||||
|  | ||||
|     this.errorResponse = errorResponse; | ||||
|     this.statusCode = statusCode; | ||||
|     this.success = false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleError(error, res) { | ||||
|   let { status, message, source } = error; | ||||
|   const status = error?.status; | ||||
|   let { message, source } = error; | ||||
|  | ||||
|   if (status && message) { | ||||
|     if (status === 401) { | ||||
|       (message = "Unauthorized. Please check plex credentials."), | ||||
|         (source = "plex"); | ||||
|       message = "Unauthorized. Please check plex credentials."; | ||||
|       source = "plex"; | ||||
|     } | ||||
|  | ||||
|     res.status(status).send({ success: false, message, source }); | ||||
|   } else { | ||||
|     // eslint-disable-next-line no-console | ||||
|     console.log("caught authenticate plex account controller error", error); | ||||
|     res.status(500).send({ | ||||
|       message: | ||||
| @@ -25,11 +39,7 @@ function handleError(error, res) { | ||||
|  | ||||
| function handleResponse(response) { | ||||
|   if (!response.ok) { | ||||
|     throw { | ||||
|       success: false, | ||||
|       status: response.status, | ||||
|       message: response.statusText | ||||
|     }; | ||||
|     throw new PlexAuthenticationError(response.statusText, response.status); | ||||
|   } | ||||
|  | ||||
|   return response.json(); | ||||
| @@ -63,7 +73,7 @@ function link(req, res) { | ||||
|  | ||||
|   return plexAuthenticate(username, password) | ||||
|     .then(plexUser => userRepository.linkPlexUserId(user.username, plexUser.id)) | ||||
|     .then(response => | ||||
|     .then(() => | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         message: | ||||
| @@ -78,7 +88,7 @@ function unlink(req, res) { | ||||
|  | ||||
|   return userRepository | ||||
|     .unlinkPlexUserId(username) | ||||
|     .then(response => | ||||
|     .then(() => | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         message: "Successfully unlinked plex account from seasoned request." | ||||
|   | ||||
| @@ -27,7 +27,7 @@ const cookieOptions = { | ||||
|  */ | ||||
| async function loginController(req, res) { | ||||
|   const user = new User(req.body.username); | ||||
|   const password = req.body.password; | ||||
|   const { password } = req.body; | ||||
|  | ||||
|   try { | ||||
|     const [loggedIn, isAdmin, settings] = await Promise.all([ | ||||
| @@ -43,11 +43,7 @@ async function loginController(req, res) { | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     const token = new Token( | ||||
|       user, | ||||
|       isAdmin === 1 ? true : false, | ||||
|       settings | ||||
|     ).toString(secret); | ||||
|     const token = new Token(user, isAdmin === 1, settings).toString(secret); | ||||
|  | ||||
|     return res.cookie("authorization", token, cookieOptions).status(200).send({ | ||||
|       success: true, | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| const User = require("../../../user/user"); | ||||
| const Token = require("../../../user/token"); | ||||
| const UserSecurity = require("../../../user/userSecurity"); | ||||
| const UserRepository = require("../../../user/userRepository"); | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
|  | ||||
| const secret = configuration.get("authentication", "secret"); | ||||
| const userSecurity = new UserSecurity(); | ||||
| const userRepository = new UserRepository(); | ||||
|  | ||||
| const isProduction = process.env.NODE_ENV === "production"; | ||||
| const cookieOptions = { | ||||
| @@ -24,7 +22,7 @@ const cookieOptions = { | ||||
|  */ | ||||
| function registerController(req, res) { | ||||
|   const user = new User(req.body.username, req.body.email); | ||||
|   const password = req.body.password; | ||||
|   const { password } = req.body; | ||||
|  | ||||
|   userSecurity | ||||
|     .createNewUser(user, password) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| const RequestRepository = require("../../../plex/requestRepository.js"); | ||||
| const RequestRepository = require("../../../plex/requestRepository"); | ||||
|  | ||||
| const requestRepository = new RequestRepository(); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const UserRepository = require("../../../user/userRepository"); | ||||
|  | ||||
| const userRepository = new UserRepository(); | ||||
| /** | ||||
|  * Controller: Retrieves settings of a logged in user | ||||
| @@ -22,11 +23,12 @@ const getSettingsController = (req, res) => { | ||||
| const updateSettingsController = (req, res) => { | ||||
|   const username = req.loggedInUser ? req.loggedInUser.username : null; | ||||
|  | ||||
|   const idempotencyKey = req.headers("Idempotency-Key"); // TODO implement better transactions | ||||
|   const { dark_mode, emoji } = req.body; | ||||
|   // const idempotencyKey = req.headers("Idempotency-Key"); // TODO implement better transactions | ||||
|   const emoji = req.body?.emoji; | ||||
|   const darkMode = req.body?.dark_mode; | ||||
|  | ||||
|   userRepository | ||||
|     .updateSettings(username, dark_mode, emoji) | ||||
|     .updateSettings(username, darkMode, emoji) | ||||
|     .then(settings => { | ||||
|       res.send({ success: true, settings }); | ||||
|     }) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const configuration = require("../../../config/configuration").getInstance(); | ||||
| const Tautulli = require("../../../tautulli/tautulli"); | ||||
|  | ||||
| const apiKey = configuration.get("tautulli", "apiKey"); | ||||
| const ip = configuration.get("tautulli", "ip"); | ||||
| const port = configuration.get("tautulli", "port"); | ||||
| @@ -10,19 +11,18 @@ function handleError(error, res) { | ||||
|  | ||||
|   if (status && message) { | ||||
|     return res.status(status).send({ success: false, message }); | ||||
|   } else { | ||||
|     console.log("caught view history controller error", error); | ||||
|     return res.status(500).send({ | ||||
|       message: "An unexpected error occured while fetching view history" | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   return res.status(500).send({ | ||||
|     message: "An unexpected error occured while fetching view history" | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function watchTimeStatsController(req, res) { | ||||
|   const user = req.loggedInUser; | ||||
|  | ||||
|   return tautulli | ||||
|     .watchTimeStats(user.plex_userid) | ||||
|     .watchTimeStats(user.plexUserId) | ||||
|     .then(data => { | ||||
|       return res.send({ | ||||
|         success: true, | ||||
| @@ -35,10 +35,11 @@ function watchTimeStatsController(req, res) { | ||||
|  | ||||
| function getPlaysByDayOfWeekController(req, res) { | ||||
|   const user = req.loggedInUser; | ||||
|   const { days, y_axis } = req.query; | ||||
|   const days = req.query?.days; | ||||
|   const yAxis = req.query?.y_axis; | ||||
|  | ||||
|   return tautulli | ||||
|     .getPlaysByDayOfWeek(user.plex_userid, days, y_axis) | ||||
|     .getPlaysByDayOfWeek(user.plexUserId, days, yAxis) | ||||
|     .then(data => | ||||
|       res.send({ | ||||
|         success: true, | ||||
| @@ -51,7 +52,8 @@ function getPlaysByDayOfWeekController(req, res) { | ||||
|  | ||||
| function getPlaysByDaysController(req, res) { | ||||
|   const user = req.loggedInUser; | ||||
|   const { days, y_axis } = req.query; | ||||
|   const days = req.query?.days; | ||||
|   const yAxis = req.query?.y_axis; | ||||
|  | ||||
|   if (days === undefined) { | ||||
|     return res.status(422).send({ | ||||
| @@ -61,7 +63,7 @@ function getPlaysByDaysController(req, res) { | ||||
|   } | ||||
|  | ||||
|   const allowedYAxisDataType = ["plays", "duration"]; | ||||
|   if (!allowedYAxisDataType.includes(y_axis)) { | ||||
|   if (!allowedYAxisDataType.includes(yAxis)) { | ||||
|     return res.status(422).send({ | ||||
|       success: false, | ||||
|       message: `Y axis parameter must be one of values: [${allowedYAxisDataType}]` | ||||
| @@ -69,7 +71,7 @@ function getPlaysByDaysController(req, res) { | ||||
|   } | ||||
|  | ||||
|   return tautulli | ||||
|     .getPlaysByDays(user.plex_userid, days, y_axis) | ||||
|     .getPlaysByDays(user.plexUserId, days, yAxis) | ||||
|     .then(data => | ||||
|       res.send({ | ||||
|         success: true, | ||||
| @@ -86,7 +88,7 @@ function userViewHistoryController(req, res) { | ||||
|   // and then return 501 Not implemented | ||||
|  | ||||
|   return tautulli | ||||
|     .viewHistory(user.plex_userid) | ||||
|     .viewHistory(user.plexUserId) | ||||
|     .then(data => { | ||||
|       return res.send({ | ||||
|         success: true, | ||||
|   | ||||
| @@ -1,31 +1,31 @@ | ||||
| const establishedDatabase = require("../../database/database"); | ||||
|  | ||||
| // eslint-disable-next-line consistent-return | ||||
| const mustBeAdmin = (req, res, next) => { | ||||
|   let database = establishedDatabase; | ||||
|   const database = establishedDatabase; | ||||
|  | ||||
|   if (req.loggedInUser === undefined) { | ||||
|     return res.status(401).send({ | ||||
|     res.status(401).send({ | ||||
|       success: false, | ||||
|       message: "You must be logged in." | ||||
|     }); | ||||
|   } else { | ||||
|     database | ||||
|       .get( | ||||
|         `SELECT admin FROM user WHERE user_name IS ?`, | ||||
|         req.loggedInUser.username | ||||
|       ) | ||||
|       .then(isAdmin => { | ||||
|         console.log(isAdmin, req.loggedInUser); | ||||
|         if (isAdmin.admin == 0) { | ||||
|           return res.status(401).send({ | ||||
|             success: false, | ||||
|             message: "You must be logged in as a admin." | ||||
|           }); | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   return next(); | ||||
|   database | ||||
|     .get( | ||||
|       `SELECT admin FROM user WHERE user_name IS ?`, | ||||
|       req.loggedInUser.username | ||||
|     ) | ||||
|     .then(isAdmin => { | ||||
|       if (isAdmin.admin === 0) { | ||||
|         return res.status(401).send({ | ||||
|           success: false, | ||||
|           message: "You must be logged in as a admin." | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       return next(); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| module.exports = mustBeAdmin; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| // eslint-disable-next-line consistent-return | ||||
| const mustBeAuthenticated = (req, res, next) => { | ||||
|   if (req.loggedInUser === undefined) { | ||||
|     return res.status(401).send({ | ||||
| @@ -5,7 +6,8 @@ const mustBeAuthenticated = (req, res, next) => { | ||||
|       message: "You must be logged in." | ||||
|     }); | ||||
|   } | ||||
|   return next(); | ||||
|  | ||||
|   next(); | ||||
| }; | ||||
|  | ||||
| module.exports = mustBeAuthenticated; | ||||
|   | ||||
| @@ -1,35 +1,36 @@ | ||||
| const establishedDatabase = require("../../database/database"); | ||||
|  | ||||
| /* eslint-disable consistent-return */ | ||||
| const mustHaveAccountLinkedToPlex = (req, res, next) => { | ||||
|   let database = establishedDatabase; | ||||
|   const loggedInUser = req.loggedInUser; | ||||
|   const database = establishedDatabase; | ||||
|   const { loggedInUser } = req; | ||||
|  | ||||
|   if (loggedInUser === undefined) { | ||||
|   if (loggedInUser === null) { | ||||
|     return res.status(401).send({ | ||||
|       success: false, | ||||
|       message: "You must have your account linked to a plex account." | ||||
|     }); | ||||
|   } else { | ||||
|     database | ||||
|       .get( | ||||
|         `SELECT plex_userid FROM settings WHERE user_name IS ?`, | ||||
|         loggedInUser.username | ||||
|       ) | ||||
|       .then(row => { | ||||
|         const plex_userid = row.plex_userid; | ||||
|  | ||||
|         if (plex_userid === null || plex_userid === undefined) { | ||||
|           return res.status(403).send({ | ||||
|             success: false, | ||||
|             message: | ||||
|               "No plex account user id found for your user. Please authenticate your plex account at /user/authenticate." | ||||
|           }); | ||||
|         } else { | ||||
|           req.loggedInUser.plex_userid = plex_userid; | ||||
|           return next(); | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   database | ||||
|     .get( | ||||
|       `SELECT plex_userid FROM settings WHERE user_name IS ?`, | ||||
|       loggedInUser.username | ||||
|     ) | ||||
|     .then(row => { | ||||
|       const plexUserId = row.plex_userid; | ||||
|       if (plexUserId === null) { | ||||
|         return res.status(403).send({ | ||||
|           success: false, | ||||
|           message: | ||||
|             "No plex account user id found for your user. Please authenticate your plex account at /user/authenticate." | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       req.loggedInUser.plexUserId = plexUserId; | ||||
|       next(); | ||||
|     }); | ||||
| }; | ||||
| /* eslint-enable consistent-return */ | ||||
|  | ||||
| module.exports = mustHaveAccountLinkedToPlex; | ||||
|   | ||||
| @@ -11,22 +11,18 @@ const reqTokenToUser = (req, res, next) => { | ||||
|   const cookieAuthToken = req.cookies.authorization; | ||||
|   const headerAuthToken = req.headers.authorization; | ||||
|  | ||||
|   if (cookieAuthToken || headerAuthToken) { | ||||
|     try { | ||||
|       const token = Token.fromString( | ||||
|         cookieAuthToken || headerAuthToken, | ||||
|         secret | ||||
|       ); | ||||
|       req.loggedInUser = token.user; | ||||
|     } catch (error) { | ||||
|       req.loggedInUser = undefined; | ||||
|     } | ||||
|   } else { | ||||
|     // guest session | ||||
|     console.debug("No auth token in header or cookie."); | ||||
|   if (!(cookieAuthToken || headerAuthToken)) { | ||||
|     return next(); | ||||
|   } | ||||
|  | ||||
|   next(); | ||||
|   try { | ||||
|     const token = Token.fromString(cookieAuthToken || headerAuthToken, secret); | ||||
|     req.loggedInUser = token.user; | ||||
|   } catch (error) { | ||||
|     req.loggedInUser = null; | ||||
|   } | ||||
|  | ||||
|   return next(); | ||||
| }; | ||||
|  | ||||
| module.exports = reqTokenToUser; | ||||
|   | ||||
| @@ -1 +1,89 @@ | ||||
| [{"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 | ||||
|   } | ||||
| ] | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user