diff --git a/.eslintrc.json b/.eslintrc.json
index 9bbe884..157265e 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -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"
}
}
diff --git a/src/cache/redis.js b/src/cache/redis.js
index 75e098c..fdee990 100644
--- a/src/cache/redis.js
+++ b/src/cache/redis.js
@@ -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));
});
});
}
diff --git a/src/config/configuration.js b/src/config/configuration.js
index 0414dc4..175a0a7 100644
--- a/src/config/configuration.js
+++ b/src/config/configuration.js
@@ -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}`);
}
diff --git a/src/config/environmentVariables.js b/src/config/environmentVariables.js
index b7fc3f2..22d467f 100644
--- a/src/config/environmentVariables.js
+++ b/src/config/environmentVariables.js
@@ -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;
diff --git a/src/config/field.js b/src/config/field.js
index 42eb8cd..ef4b3d1 100644
--- a/src/config/field.js
+++ b/src/config/field.js
@@ -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;
diff --git a/src/config/filters.js b/src/config/filters.js
index b4ec359..8d5fad1 100644
--- a/src/config/filters.js
+++ b/src/config/filters.js
@@ -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;
diff --git a/src/database/sqliteDatabase.js b/src/database/sqliteDatabase.js
index a330487..a6f8e53 100644
--- a/src/database/sqliteDatabase.js
+++ b/src/database/sqliteDatabase.js
@@ -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);
diff --git a/src/git/gitRepository.js b/src/git/gitRepository.js
index 703ce2c..7659561 100644
--- a/src/git/gitRepository.js
+++ b/src/git/gitRepository.js
@@ -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;
diff --git a/src/media_classes/media.js b/src/media_classes/media.js
index 4ec1627..e68f670 100644
--- a/src/media_classes/media.js
+++ b/src/media_classes/media.js
@@ -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;
diff --git a/src/media_classes/mediaInfo.js b/src/media_classes/mediaInfo.js
index 3cbe69d..663bcb8 100644
--- a/src/media_classes/mediaInfo.js
+++ b/src/media_classes/mediaInfo.js
@@ -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;
diff --git a/src/media_classes/player.js b/src/media_classes/player.js
index e675f85..6f9a22f 100644
--- a/src/media_classes/player.js
+++ b/src/media_classes/player.js
@@ -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;
diff --git a/src/media_classes/plex.js b/src/media_classes/plex.js
index 2df0f77..2626e39 100644
--- a/src/media_classes/plex.js
+++ b/src/media_classes/plex.js
@@ -1,3 +1,5 @@
+/* eslint-disable camelcase */
+
const Media = require("./media");
class Plex extends Media {
diff --git a/src/media_classes/tmdb.js b/src/media_classes/tmdb.js
index 724ac86..63a0c40 100644
--- a/src/media_classes/tmdb.js
+++ b/src/media_classes/tmdb.js
@@ -1,3 +1,5 @@
+/* eslint-disable camelcase */
+
const Media = require("./media");
class TMDB extends Media {
diff --git a/src/media_classes/user.js b/src/media_classes/user.js
index fc09d7d..62a5f3e 100644
--- a/src/media_classes/user.js
+++ b/src/media_classes/user.js
@@ -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;
diff --git a/src/notifications/sms.js b/src/notifications/sms.js
index 7e78b05..b9e6f41 100644
--- a/src/notifications/sms.js
+++ b/src/notifications/sms.js
@@ -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);
}
);
});
diff --git a/src/pirate/pirateRepository.js b/src/pirate/pirateRepository.js
index ba01bfa..36849b2 100644
--- a/src/pirate/pirateRepository.js
+++ b/src/pirate/pirateRepository.js
@@ -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 });
})
diff --git a/src/plex/convertPlexToSeasoned.js b/src/plex/convertPlexToSeasoned.js
index 148fe7c..d216c23 100644
--- a/src/plex/convertPlexToSeasoned.js
+++ b/src/plex/convertPlexToSeasoned.js
@@ -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;
}
diff --git a/src/plex/hookDump.js b/src/plex/hookDump.js
deleted file mode 100644
index 5a34b2e..0000000
--- a/src/plex/hookDump.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const configuration = require("../config/configuration").getInstance();
-
-function hookDumpController(req, res) {
- console.log(req);
-}
-
-module.exports = hookDumpController;
diff --git a/src/plex/mailTemplate.js b/src/plex/mailTemplate.js
index 64d38a9..fa0a355 100644
--- a/src/plex/mailTemplate.js
+++ b/src/plex/mailTemplate.js
@@ -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 `
${info.name} ${info.year}
`;
- }
+ }
}
module.exports = mailTemplate;
diff --git a/src/plex/plex.js b/src/plex/plex.js
index 159fd00..01b9cee 100644
--- a/src/plex/plex.js
+++ b/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;
+ });
}
}
diff --git a/src/plex/plexRepository.js b/src/plex/plexRepository.js
index 4433fa2..472111f 100644
--- a/src/plex/plexRepository.js
+++ b/src/plex/plexRepository.js
@@ -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`,
diff --git a/src/plex/requestRepository.js b/src/plex/requestRepository.js
index 357ad40..85caaf1 100644
--- a/src/plex/requestRepository.js
+++ b/src/plex/requestRepository.js
@@ -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;
+ });
});
}
diff --git a/src/plex/stream/convertStreamToPlayback.js b/src/plex/stream/convertStreamToPlayback.js
index cdabee4..c07d439 100644
--- a/src/plex/stream/convertStreamToPlayback.js
+++ b/src/plex/stream/convertStreamToPlayback.js
@@ -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;
diff --git a/src/plex/types/episode.js b/src/plex/types/episode.js
index a99fa8f..69ce4fe 100644
--- a/src/plex/types/episode.js
+++ b/src/plex/types/episode.js
@@ -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;
\ No newline at end of file
+module.exports = Episode;
diff --git a/src/plex/types/movie.js b/src/plex/types/movie.js
index 8a2b6fa..aa62ca6 100644
--- a/src/plex/types/movie.js
+++ b/src/plex/types/movie.js
@@ -5,8 +5,8 @@ class Movie {
this.summary = null;
this.rating = null;
this.tagline = null;
- this.type = 'movie';
+ this.type = "movie";
}
}
-module.exports = Movie;
\ No newline at end of file
+module.exports = Movie;
diff --git a/src/plex/types/show.js b/src/plex/types/show.js
index 86c1ef4..426fd7c 100644
--- a/src/plex/types/show.js
+++ b/src/plex/types/show.js
@@ -9,4 +9,4 @@ class Show {
}
}
-module.exports = Show;
\ No newline at end of file
+module.exports = Show;
diff --git a/src/request/request.js b/src/request/request.js
index 97afb05..3f3d91b 100644
--- a/src/request/request.js
+++ b/src/request/request.js
@@ -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;
});
}
diff --git a/src/request/utils.js b/src/request/utils.js
index 0f92156..67756c0 100644
--- a/src/request/utils.js
+++ b/src/request/utils.js
@@ -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 }
\ No newline at end of file
+module.exports = { validSort, validFilter };
diff --git a/src/searchHistory/searchHistory.js b/src/searchHistory/searchHistory.js
index 15fdcb1..9c25380 100644
--- a/src/searchHistory/searchHistory.js
+++ b/src/searchHistory/searchHistory.js
@@ -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();
});
}
}
diff --git a/src/seasoned/stray.js b/src/seasoned/stray.js
index f33e2ac..c2620fa 100644
--- a/src/seasoned/stray.js
+++ b/src/seasoned/stray.js
@@ -1,7 +1,7 @@
class Stray {
- constructor(id) {
- this.id = id;
- }
+ constructor(id) {
+ this.id = id;
+ }
}
module.exports = Stray;
diff --git a/src/seasoned/strayRepository.js b/src/seasoned/strayRepository.js
index 8474295..9239736 100644
--- a/src/seasoned/strayRepository.js
+++ b/src/seasoned/strayRepository.js
@@ -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) {
diff --git a/src/tautulli/tautulli.js b/src/tautulli/tautulli.js
index 3ac47bc..f5fdf3d 100644
--- a/src/tautulli/tautulli.js
+++ b/src/tautulli/tautulli.js
@@ -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));
}
}
diff --git a/src/tmdb/tmdb.js b/src/tmdb/tmdb.js
index d549d0e..9adb346 100644
--- a/src/tmdb/tmdb.js
+++ b/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) {
diff --git a/src/tmdb/types.js b/src/tmdb/types.js
index 0c7ae23..cbf5cbc 100644
--- a/src/tmdb/types.js
+++ b/src/tmdb/types.js
@@ -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 };
diff --git a/src/tmdb/types.ts b/src/tmdb/types.ts
index 89e2f8a..e079157 100644
--- a/src/tmdb/types.ts
+++ b/src/tmdb/types.ts
@@ -61,4 +61,4 @@ interface Genre {
name: string;
}
-export { Movie, Show, Person, Genre }
+export { Movie, Show, Person, Genre };
diff --git a/src/tmdb/types/credits.js b/src/tmdb/types/credits.js
index 0c47e51..8740b6d 100644
--- a/src/tmdb/types/credits.js
+++ b/src/tmdb/types/credits.js
@@ -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;
diff --git a/src/tmdb/types/movie.js b/src/tmdb/types/movie.js
index 4663245..db78856 100644
--- a/src/tmdb/types/movie.js
+++ b/src/tmdb/types/movie.js
@@ -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
- }
+ };
}
}
diff --git a/src/tmdb/types/person.js b/src/tmdb/types/person.js
index 67ca569..2cd24d9 100644
--- a/src/tmdb/types/person.js
+++ b/src/tmdb/types/person.js
@@ -1,3 +1,5 @@
+/* eslint-disable camelcase */
+
class Person {
constructor(
id,
diff --git a/src/tmdb/types/releaseDates.js b/src/tmdb/types/releaseDates.js
index 340479e..94ffa31 100644
--- a/src/tmdb/types/releaseDates.js
+++ b/src/tmdb/types/releaseDates.js
@@ -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())
+ };
}
}
diff --git a/src/tmdb/types/show.js b/src/tmdb/types/show.js
index 176c59a..cbbf434 100644
--- a/src/tmdb/types/show.js
+++ b/src/tmdb/types/show.js
@@ -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
- }
+ };
}
}
diff --git a/src/user/token.js b/src/user/token.js
index d99baac..3f80aad 100644
--- a/src/user/token.js
+++ b/src/user/token.js
@@ -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" });
}
diff --git a/src/user/user.js b/src/user/user.js
index bdf326a..0c24602 100644
--- a/src/user/user.js
+++ b/src/user/user.js
@@ -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;
diff --git a/src/user/userRepository.js b/src/user/userRepository.js
index 4f9683c..d23f0d4 100644
--- a/src/user/userRepository.js
+++ b/src/user/userRepository.js
@@ -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;
diff --git a/src/user/userSecurity.js b/src/user/userSecurity.js
index ea07bb8..5caef59 100644
--- a/src/user/userSecurity.js
+++ b/src/user/userSecurity.js
@@ -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);
diff --git a/src/webserver/app.js b/src/webserver/app.js
index 270c9ff..dcbe080 100644
--- a/src/webserver/app.js
+++ b/src/webserver/app.js
@@ -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
diff --git a/src/webserver/controllers/git/dumpHook.js b/src/webserver/controllers/git/dumpHook.js
index bed04f5..5016893 100644
--- a/src/webserver/controllers/git/dumpHook.js
+++ b/src/webserver/controllers/git/dumpHook.js
@@ -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;
diff --git a/src/webserver/controllers/list/listController.js b/src/webserver/controllers/list/listController.js
index 9ec287e..298dccf 100644
--- a/src/webserver/controllers/list/listController.js
+++ b/src/webserver/controllers/list/listController.js
@@ -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.`
diff --git a/src/webserver/controllers/movie/credits.js b/src/webserver/controllers/movie/credits.js
index a33b2fa..ad467dd 100644
--- a/src/webserver/controllers/movie/credits.js
+++ b/src/webserver/controllers/movie/credits.js
@@ -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}`
+ });
});
};
diff --git a/src/webserver/controllers/movie/info.js b/src/webserver/controllers/movie/info.js
index 5d31b7e..6311b88 100644
--- a/src/webserver/controllers/movie/info.js
+++ b/src/webserver/controllers/movie/info.js
@@ -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}`
+ });
}
}
diff --git a/src/webserver/controllers/movie/releaseDates.js b/src/webserver/controllers/movie/releaseDates.js
index d8eb2e0..74ad3d8 100644
--- a/src/webserver/controllers/movie/releaseDates.js
+++ b/src/webserver/controllers/movie/releaseDates.js
@@ -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}`
+ });
});
};
diff --git a/src/webserver/controllers/person/credits.js b/src/webserver/controllers/person/credits.js
index 91a79e5..72d9c5a 100644
--- a/src/webserver/controllers/person/credits.js
+++ b/src/webserver/controllers/person/credits.js
@@ -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}.`
+ });
});
};
diff --git a/src/webserver/controllers/person/info.js b/src/webserver/controllers/person/info.js
index be1dad0..5a00102 100644
--- a/src/webserver/controllers/person/info.js
+++ b/src/webserver/controllers/person/info.js
@@ -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}`
+ });
}
}
diff --git a/src/webserver/controllers/pirate/addMagnet.js b/src/webserver/controllers/pirate/addMagnet.js
index 0ce38d0..2ff7599 100644
--- a/src/webserver/controllers/pirate/addMagnet.js
+++ b/src/webserver/controllers/pirate/addMagnet.js
@@ -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 });
});
diff --git a/src/webserver/controllers/plex/fetchRequested.js b/src/webserver/controllers/plex/fetchRequested.js
index 56eeaaa..14ba536 100644
--- a/src/webserver/controllers/plex/fetchRequested.js
+++ b/src/webserver/controllers/plex/fetchRequested.js
@@ -1,4 +1,4 @@
-const RequestRepository = require("../../../plex/requestRepository.js");
+const RequestRepository = require("../../../plex/requestRepository");
const requestRepository = new RequestRepository();
diff --git a/src/webserver/controllers/plex/hookDump.js b/src/webserver/controllers/plex/hookDump.js
index 82d4e69..bd39fcb 100644
--- a/src/webserver/controllers/plex/hookDump.js
+++ b/src/webserver/controllers/plex/hookDump.js
@@ -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;
diff --git a/src/webserver/controllers/plex/readRequest.js b/src/webserver/controllers/plex/readRequest.js
index db29647..04d8c98 100644
--- a/src/webserver/controllers/plex/readRequest.js
+++ b/src/webserver/controllers/plex/readRequest.js
@@ -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)
diff --git a/src/webserver/controllers/plex/search.js b/src/webserver/controllers/plex/search.js
index 0d7b577..50162e9 100644
--- a/src/webserver/controllers/plex/search.js
+++ b/src/webserver/controllers/plex/search.js
@@ -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 => {
diff --git a/src/webserver/controllers/plex/searchMedia.js b/src/webserver/controllers/plex/searchMedia.js
index c4d9698..d64cefe 100644
--- a/src/webserver/controllers/plex/searchMedia.js
+++ b/src/webserver/controllers/plex/searchMedia.js
@@ -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 => {
diff --git a/src/webserver/controllers/plex/searchRequest.js b/src/webserver/controllers/plex/searchRequest.js
index c040f48..b7ce101 100644
--- a/src/webserver/controllers/plex/searchRequest.js
+++ b/src/webserver/controllers/plex/searchRequest.js
@@ -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);
diff --git a/src/webserver/controllers/plex/submitRequest.js b/src/webserver/controllers/plex/submitRequest.js
index 2d78018..6039c47 100644
--- a/src/webserver/controllers/plex/submitRequest.js
+++ b/src/webserver/controllers/plex/submitRequest.js
@@ -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" })
diff --git a/src/webserver/controllers/plex/updateRequested.js b/src/webserver/controllers/plex/updateRequested.js
index 3194a03..0fd4b18 100644
--- a/src/webserver/controllers/plex/updateRequested.js
+++ b/src/webserver/controllers/plex/updateRequested.js
@@ -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)
diff --git a/src/webserver/controllers/plex/watchDirectLink.js b/src/webserver/controllers/plex/watchDirectLink.js
index a56f85d..dabee21 100644
--- a/src/webserver/controllers/plex/watchDirectLink.js
+++ b/src/webserver/controllers/plex/watchDirectLink.js
@@ -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 });
})
diff --git a/src/webserver/controllers/request/fetchAllRequests.js b/src/webserver/controllers/request/fetchAllRequests.js
index 7679169..252d76f 100644
--- a/src/webserver/controllers/request/fetchAllRequests.js
+++ b/src/webserver/controllers/request/fetchAllRequests.js
@@ -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`
+ });
});
}
diff --git a/src/webserver/controllers/request/getRequest.js b/src/webserver/controllers/request/getRequest.js
index 9167e95..b4a491f 100644
--- a/src/webserver/controllers/request/getRequest.js
+++ b/src/webserver/controllers/request/getRequest.js
@@ -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}`
+ });
});
}
diff --git a/src/webserver/controllers/request/requestTmdbId.js b/src/webserver/controllers/request/requestTmdbId.js
index a4e6185..01d5c1a 100644
--- a/src/webserver/controllers/request/requestTmdbId.js
+++ b/src/webserver/controllers/request/requestTmdbId.js
@@ -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}`;
diff --git a/src/webserver/controllers/search/movieSearch.js b/src/webserver/controllers/search/movieSearch.js
index bfee6d1..d863cc4 100644
--- a/src/webserver/controllers/search/movieSearch.js
+++ b/src/webserver/controllers/search/movieSearch.js
@@ -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}`
+ });
});
}
diff --git a/src/webserver/controllers/search/multiSearch.js b/src/webserver/controllers/search/multiSearch.js
index db977c7..f16ce30 100644
--- a/src/webserver/controllers/search/multiSearch.js
+++ b/src/webserver/controllers/search/multiSearch.js
@@ -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}`
+ });
});
}
diff --git a/src/webserver/controllers/search/personSearch.js b/src/webserver/controllers/search/personSearch.js
index dbd11c1..838bed0 100644
--- a/src/webserver/controllers/search/personSearch.js
+++ b/src/webserver/controllers/search/personSearch.js
@@ -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}`
+ });
});
}
diff --git a/src/webserver/controllers/search/showSearch.js b/src/webserver/controllers/search/showSearch.js
index cd420c4..534d1d0 100644
--- a/src/webserver/controllers/search/showSearch.js
+++ b/src/webserver/controllers/search/showSearch.js
@@ -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}`
+ });
});
}
diff --git a/src/webserver/controllers/show/credits.js b/src/webserver/controllers/show/credits.js
index b7014a5..672f3e8 100644
--- a/src/webserver/controllers/show/credits.js
+++ b/src/webserver/controllers/show/credits.js
@@ -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}.`
+ });
});
};
diff --git a/src/webserver/controllers/show/info.js b/src/webserver/controllers/show/info.js
index 322c381..68f7301 100644
--- a/src/webserver/controllers/show/info.js
+++ b/src/webserver/controllers/show/info.js
@@ -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}`
+ });
}
}
diff --git a/src/webserver/controllers/user/authenticatePlexAccount.js b/src/webserver/controllers/user/authenticatePlexAccount.js
index 19a2749..8ac5896 100644
--- a/src/webserver/controllers/user/authenticatePlexAccount.js
+++ b/src/webserver/controllers/user/authenticatePlexAccount.js
@@ -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."
diff --git a/src/webserver/controllers/user/login.js b/src/webserver/controllers/user/login.js
index c0098ca..0438a89 100644
--- a/src/webserver/controllers/user/login.js
+++ b/src/webserver/controllers/user/login.js
@@ -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,
diff --git a/src/webserver/controllers/user/register.js b/src/webserver/controllers/user/register.js
index e3a5d71..655f920 100644
--- a/src/webserver/controllers/user/register.js
+++ b/src/webserver/controllers/user/register.js
@@ -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)
diff --git a/src/webserver/controllers/user/requests.js b/src/webserver/controllers/user/requests.js
index e412c64..5fe0f22 100644
--- a/src/webserver/controllers/user/requests.js
+++ b/src/webserver/controllers/user/requests.js
@@ -1,4 +1,4 @@
-const RequestRepository = require("../../../plex/requestRepository.js");
+const RequestRepository = require("../../../plex/requestRepository");
const requestRepository = new RequestRepository();
diff --git a/src/webserver/controllers/user/settings.js b/src/webserver/controllers/user/settings.js
index b69b322..adf0d8d 100644
--- a/src/webserver/controllers/user/settings.js
+++ b/src/webserver/controllers/user/settings.js
@@ -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 });
})
diff --git a/src/webserver/controllers/user/viewHistory.js b/src/webserver/controllers/user/viewHistory.js
index 8a5334e..865a3d7 100644
--- a/src/webserver/controllers/user/viewHistory.js
+++ b/src/webserver/controllers/user/viewHistory.js
@@ -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,
diff --git a/src/webserver/middleware/mustBeAdmin.js b/src/webserver/middleware/mustBeAdmin.js
index be516c6..50e9c71 100644
--- a/src/webserver/middleware/mustBeAdmin.js
+++ b/src/webserver/middleware/mustBeAdmin.js
@@ -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;
diff --git a/src/webserver/middleware/mustBeAuthenticated.js b/src/webserver/middleware/mustBeAuthenticated.js
index 6af925b..9a470db 100644
--- a/src/webserver/middleware/mustBeAuthenticated.js
+++ b/src/webserver/middleware/mustBeAuthenticated.js
@@ -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;
diff --git a/src/webserver/middleware/mustHaveAccountLinkedToPlex.js b/src/webserver/middleware/mustHaveAccountLinkedToPlex.js
index d3413d0..49e4849 100644
--- a/src/webserver/middleware/mustHaveAccountLinkedToPlex.js
+++ b/src/webserver/middleware/mustHaveAccountLinkedToPlex.js
@@ -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;
diff --git a/src/webserver/middleware/reqTokenToUser.js b/src/webserver/middleware/reqTokenToUser.js
index 0b92cef..bde99f6 100644
--- a/src/webserver/middleware/reqTokenToUser.js
+++ b/src/webserver/middleware/reqTokenToUser.js
@@ -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;
diff --git a/tests/fixtures/blade_runner_2049-info-success-response.json b/tests/fixtures/blade_runner_2049-info-success-response.json
index 62eb96f..4c412e0 100644
--- a/tests/fixtures/blade_runner_2049-info-success-response.json
+++ b/tests/fixtures/blade_runner_2049-info-success-response.json
@@ -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
+ }
+]
diff --git a/tests/fixtures/interstellar-query-movie-success-response.json b/tests/fixtures/interstellar-query-movie-success-response.json
index 19162ac..d511be2 100644
--- a/tests/fixtures/interstellar-query-movie-success-response.json
+++ b/tests/fixtures/interstellar-query-movie-success-response.json
@@ -1 +1,193 @@
-{"results":[{"id":157336,"title":"Interstellar","year":2014,"overview":"Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.","poster":"/nBNZadXqJSdt05SHLqgT0HuC5Gm.jpg","backdrop":"/xu9zaAevzQ5nnrsXN6JcahLnG4i.jpg","rank":8.2,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","release_date":"2014-11-05T00:00:00.000Z"},{"id":301959,"title":"Interstellar: Nolan's Odyssey","year":2014,"overview":"Behind the scenes of Christopher Nolan's sci-fi drama, which stars Matthew McConaughey and Anne Hathaway","poster":"/xZwUIPqBHyJ2QIfMPANOZ1mAld6.jpg","backdrop":"/bT5jpIZE50MI0COE8pOeq0kMpQo.jpg","rank":7.9,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":1,"release_date":"2014-11-05T00:00:00.000Z"},{"id":398188,"title":"Interstellar Wars","year":2016,"overview":"For Millennia the Aliien force has watched and waited, a brooding menace that has now at last decided to take over the Earth. Communications systems worldwide are sent into chaos by a strange atmospheric interference and this has turned into a global phenomenon. A massive spaceship headed towards Earth and smaller spaceships began to cover entire cities around the world. Suddenly, the wonder turns into horror as the spaceships destroy the cities with energy weapons. When the world counterattacks, the alien ships are invincible to military weapons. The survivors have to use their wits to kill the aliens, or die.","poster":"/cjvTebuqD8wmhchHE286ltVcbX6.jpg","backdrop":"/yTnHa6lgIv8rNneSNBDkBe8MnZe.jpg","rank":3.8,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":2,"release_date":"2016-05-23T00:00:00.000Z"},{"id":287954,"title":"Lolita from Interstellar Space","year":2014,"overview":"An undeniably beautiful alien is sent to Earth to study the complex mating rituals of human beings, which leads to the young interstellar traveler experiencing the passion that surrounds the centuries-old ritual of the species.","poster":"/buoq7zYO4J3ttkEAqEMWelPDC0G.jpg","backdrop":"/mgb6tVEieDYLpQt666ACzGz5cyE.jpg","rank":7,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":3,"release_date":"2014-03-08T00:00:00.000Z"},{"id":336592,"title":"The Science of Interstellar","year":2014,"overview":"The science of Christopher Nolan's sci-fi, Interstellar.","poster":"/6KBD7YSBjCfgBgHwpsQo3G3GGdx.jpg","backdrop":null,"rank":7.8,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":4,"release_date":"2014-11-25T00:00:00.000Z"},{"id":529107,"title":"Inside Interstellar","year":2015,"overview":"Cast and crew of Christopher Nolan's 'Interstellar' discuss project origins, the film's imagery, ambitions, incorporating IMAX footage, the human element within the film, arm shooting locations outside of Calgary, the set construction and design, working with real corn, mechanical characters, including backstory, design, the blend of practical and digital effects in bringing them to life, the differences in the characters, the human performances behind the characters, the creative process behind the film's music, Icelandic locations, vehicle interiors, the processes of simulating the absence of gravity, the crucial end-film visuals and influence and inspiration for future generations","poster":"/vemBplPKQhVe5cRWL7kxtgp15Vq.jpg","backdrop":null,"rank":9,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":5,"release_date":"2015-03-31T00:00:00.000Z"},{"id":552531,"title":"The Prom Goer's Interstellar Excursion","year":null,"overview":"High schooler Bennett lands the prom date of his dreams, Sophie, just days before the dance. Not long after, he witnesses Sophie being abducted by aliens in the middle of the New Mexico desert.","poster":null,"backdrop":null,"rank":0,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":6,"release_date":null},{"id":460616,"title":"Interstellar Civil War: Shadows of the Empire","year":2018,"overview":"The Imperial Empire is attacked by an Alliance of rebels led by fanatical mystics. The ruler, Empress Nobu, the 8th generation of her family, wants to execute a bold plan to rescue a cyborg, Leah C6, trapped on the battle ravaged planet Endor. The Empress believes Leah C6 holds the secret to destroying the Alliance of Rebels before their insurgency can kill millions of citizens of the Empire. She recruits her heroic fleet commander, Lord General Luka Raan and asks him to gather a team from the Empire's elite soldiers, the Galactic Rangers. Raan assembles the team in the ruins of Endor which was attacked by depraved Rebels and outlaws led by, Kindo-Ker, a fanatical mystic in Dark Energy. The Galactic Rangers begin a desperate search to find and rescue Leah C6 before the Alliance Rebels can.","poster":"/1lDY7ZpEKOl3OaIQURjRbmFPfT8.jpg","backdrop":null,"rank":4,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":7,"release_date":"2018-04-15T00:00:00.000Z"},{"id":47662,"title":"Trancers 4: Jack of Swords","year":1994,"overview":"Jack is now back in the future. He had since lost Lena, and finds out that he's lost his other wife Alice to none other than Harris. While heading out for another assignment, something goes awry with the TCL chamber. Jack finds himself in a whole new dimension. He also runs across a different version of trancers. These guys seem to be in control of this planet. Jack manages to assist a rebel group known as the \"Tunnel Rats\" crush the rule of the evil Lord Calaban.","poster":"/69yr3oxBpSgua26RJkFmsm7plTG.jpg","backdrop":"/5ism2HNUGuQi5a3ajYaN9ypMQMf.jpg","rank":5.2,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":8,"release_date":"1994-02-02T00:00:00.000Z"},{"id":47663,"title":"Trancers 5: Sudden Deth","year":1994,"overview":"Jack Deth is back for one more round with the trancers. Jack must attempt to find his way home from the other-dimensional world of Orpheus, where magic works and the trancers were the ruling class (before Trancers IV, that is). Unfortunately, Jack's quest to find the mystical Tiamond in the Castle of Unrelenting Terror may be thwarted by the return of Caliban, king of the trancers who was thought dead.","poster":"/epMaTjPDMbgC8TbW1ZToh4RNv0i.jpg","backdrop":"/an0xpUEX1P1BI80sCpkU1pSoREx.jpg","rank":5,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":9,"release_date":"1994-11-04T00:00:00.000Z"},{"id":261443,"title":"Angry Planet","year":2008,"overview":"A criminal sentenced to life on a prison planet reveals his true purpose: to extract revenge on the killers who murdered his family.","poster":"/ie5luS87ess1c5VgFhbGECJTQVK.jpg","backdrop":"/u4JBwlGZN8hGeLxwu7Q0WmibACp.jpg","rank":4.5,"genres":null,"status":null,"tagline":null,"runtime":null,"imdb_id":null,"type":"movie","credits":10,"release_date":"2008-01-01T00:00:00.000Z"}],"page":1,"total_results":11,"total_pages":1}
\ No newline at end of file
+{
+ "results": [
+ {
+ "id": 157336,
+ "title": "Interstellar",
+ "year": 2014,
+ "overview": "Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.",
+ "poster": "/nBNZadXqJSdt05SHLqgT0HuC5Gm.jpg",
+ "backdrop": "/xu9zaAevzQ5nnrsXN6JcahLnG4i.jpg",
+ "rank": 8.2,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "release_date": "2014-11-05T00:00:00.000Z"
+ },
+ {
+ "id": 301959,
+ "title": "Interstellar: Nolan's Odyssey",
+ "year": 2014,
+ "overview": "Behind the scenes of Christopher Nolan's sci-fi drama, which stars Matthew McConaughey and Anne Hathaway",
+ "poster": "/xZwUIPqBHyJ2QIfMPANOZ1mAld6.jpg",
+ "backdrop": "/bT5jpIZE50MI0COE8pOeq0kMpQo.jpg",
+ "rank": 7.9,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 1,
+ "release_date": "2014-11-05T00:00:00.000Z"
+ },
+ {
+ "id": 398188,
+ "title": "Interstellar Wars",
+ "year": 2016,
+ "overview": "For Millennia the Aliien force has watched and waited, a brooding menace that has now at last decided to take over the Earth. Communications systems worldwide are sent into chaos by a strange atmospheric interference and this has turned into a global phenomenon. A massive spaceship headed towards Earth and smaller spaceships began to cover entire cities around the world. Suddenly, the wonder turns into horror as the spaceships destroy the cities with energy weapons. When the world counterattacks, the alien ships are invincible to military weapons. The survivors have to use their wits to kill the aliens, or die.",
+ "poster": "/cjvTebuqD8wmhchHE286ltVcbX6.jpg",
+ "backdrop": "/yTnHa6lgIv8rNneSNBDkBe8MnZe.jpg",
+ "rank": 3.8,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 2,
+ "release_date": "2016-05-23T00:00:00.000Z"
+ },
+ {
+ "id": 287954,
+ "title": "Lolita from Interstellar Space",
+ "year": 2014,
+ "overview": "An undeniably beautiful alien is sent to Earth to study the complex mating rituals of human beings, which leads to the young interstellar traveler experiencing the passion that surrounds the centuries-old ritual of the species.",
+ "poster": "/buoq7zYO4J3ttkEAqEMWelPDC0G.jpg",
+ "backdrop": "/mgb6tVEieDYLpQt666ACzGz5cyE.jpg",
+ "rank": 7,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 3,
+ "release_date": "2014-03-08T00:00:00.000Z"
+ },
+ {
+ "id": 336592,
+ "title": "The Science of Interstellar",
+ "year": 2014,
+ "overview": "The science of Christopher Nolan's sci-fi, Interstellar.",
+ "poster": "/6KBD7YSBjCfgBgHwpsQo3G3GGdx.jpg",
+ "backdrop": null,
+ "rank": 7.8,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 4,
+ "release_date": "2014-11-25T00:00:00.000Z"
+ },
+ {
+ "id": 529107,
+ "title": "Inside Interstellar",
+ "year": 2015,
+ "overview": "Cast and crew of Christopher Nolan's 'Interstellar' discuss project origins, the film's imagery, ambitions, incorporating IMAX footage, the human element within the film, arm shooting locations outside of Calgary, the set construction and design, working with real corn, mechanical characters, including backstory, design, the blend of practical and digital effects in bringing them to life, the differences in the characters, the human performances behind the characters, the creative process behind the film's music, Icelandic locations, vehicle interiors, the processes of simulating the absence of gravity, the crucial end-film visuals and influence and inspiration for future generations",
+ "poster": "/vemBplPKQhVe5cRWL7kxtgp15Vq.jpg",
+ "backdrop": null,
+ "rank": 9,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 5,
+ "release_date": "2015-03-31T00:00:00.000Z"
+ },
+ {
+ "id": 552531,
+ "title": "The Prom Goer's Interstellar Excursion",
+ "year": null,
+ "overview": "High schooler Bennett lands the prom date of his dreams, Sophie, just days before the dance. Not long after, he witnesses Sophie being abducted by aliens in the middle of the New Mexico desert.",
+ "poster": null,
+ "backdrop": null,
+ "rank": 0,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 6,
+ "release_date": null
+ },
+ {
+ "id": 460616,
+ "title": "Interstellar Civil War: Shadows of the Empire",
+ "year": 2018,
+ "overview": "The Imperial Empire is attacked by an Alliance of rebels led by fanatical mystics. The ruler, Empress Nobu, the 8th generation of her family, wants to execute a bold plan to rescue a cyborg, Leah C6, trapped on the battle ravaged planet Endor. The Empress believes Leah C6 holds the secret to destroying the Alliance of Rebels before their insurgency can kill millions of citizens of the Empire. She recruits her heroic fleet commander, Lord General Luka Raan and asks him to gather a team from the Empire's elite soldiers, the Galactic Rangers. Raan assembles the team in the ruins of Endor which was attacked by depraved Rebels and outlaws led by, Kindo-Ker, a fanatical mystic in Dark Energy. The Galactic Rangers begin a desperate search to find and rescue Leah C6 before the Alliance Rebels can.",
+ "poster": "/1lDY7ZpEKOl3OaIQURjRbmFPfT8.jpg",
+ "backdrop": null,
+ "rank": 4,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 7,
+ "release_date": "2018-04-15T00:00:00.000Z"
+ },
+ {
+ "id": 47662,
+ "title": "Trancers 4: Jack of Swords",
+ "year": 1994,
+ "overview": "Jack is now back in the future. He had since lost Lena, and finds out that he's lost his other wife Alice to none other than Harris. While heading out for another assignment, something goes awry with the TCL chamber. Jack finds himself in a whole new dimension. He also runs across a different version of trancers. These guys seem to be in control of this planet. Jack manages to assist a rebel group known as the \"Tunnel Rats\" crush the rule of the evil Lord Calaban.",
+ "poster": "/69yr3oxBpSgua26RJkFmsm7plTG.jpg",
+ "backdrop": "/5ism2HNUGuQi5a3ajYaN9ypMQMf.jpg",
+ "rank": 5.2,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 8,
+ "release_date": "1994-02-02T00:00:00.000Z"
+ },
+ {
+ "id": 47663,
+ "title": "Trancers 5: Sudden Deth",
+ "year": 1994,
+ "overview": "Jack Deth is back for one more round with the trancers. Jack must attempt to find his way home from the other-dimensional world of Orpheus, where magic works and the trancers were the ruling class (before Trancers IV, that is). Unfortunately, Jack's quest to find the mystical Tiamond in the Castle of Unrelenting Terror may be thwarted by the return of Caliban, king of the trancers who was thought dead.",
+ "poster": "/epMaTjPDMbgC8TbW1ZToh4RNv0i.jpg",
+ "backdrop": "/an0xpUEX1P1BI80sCpkU1pSoREx.jpg",
+ "rank": 5,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 9,
+ "release_date": "1994-11-04T00:00:00.000Z"
+ },
+ {
+ "id": 261443,
+ "title": "Angry Planet",
+ "year": 2008,
+ "overview": "A criminal sentenced to life on a prison planet reveals his true purpose: to extract revenge on the killers who murdered his family.",
+ "poster": "/ie5luS87ess1c5VgFhbGECJTQVK.jpg",
+ "backdrop": "/u4JBwlGZN8hGeLxwu7Q0WmibACp.jpg",
+ "rank": 4.5,
+ "genres": null,
+ "status": null,
+ "tagline": null,
+ "runtime": null,
+ "imdb_id": null,
+ "type": "movie",
+ "credits": 10,
+ "release_date": "2008-01-01T00:00:00.000Z"
+ }
+ ],
+ "page": 1,
+ "total_results": 11,
+ "total_pages": 1
+}