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