11 Commits

Author SHA1 Message Date
3424325be4 Refactor: Python packages should be installed as modules, and not use source files (#149)
* Python packages should be installed as modules, and not use source files

* Use child_process spawn to call command and return data

* Consistent error rejection & resolve around spawn calls.

* Updated README to remove submodule ref & added how to install pip pcks.

* Resovled linting issue

* Removed python-shell package

* Disable stray functionality, unused & has dep to python-shell

* Uncommented/disabled python-shell dependency in strayRepository.
2022-10-02 16:05:29 +02:00
d6e5bdbd91 Updated docker image source repo url position within docker file 2022-09-25 17:08:01 +02:00
395dbe92f3 Updated docker image source repo url 2022-09-25 17:01:44 +02:00
212a6864f1 Fix: Redis call chain (#147)
* Moved redis mock client to separate file

* To keep cache response & api response consistent, alter call chain

* Resolved linting issues
2022-09-25 13:39:48 +02:00
c3f041d27f Simplified how we fetch and update requests that have landed in plex (#146) 2022-09-25 13:39:33 +02:00
2b6ad301a8 Resolved exception where readWithoutUserData was not defined (#145) 2022-09-25 13:39:23 +02:00
6732ed988d Register plex instance with token parameter for authenticated calls to plex (#144) 2022-09-25 13:39:07 +02:00
696f9f989c Fix: Plex errors (#141)
* inPlex function returns promise for more consistent return types

* plexUserId should default to null on falsey
2022-08-25 17:28:29 +02:00
93d394c2fe Return parsed json from redis cache (#142) 2022-08-25 17:20:52 +02:00
13470d7372 Custom errors for view hsitory/tautulli exceptions (#140) 2022-08-25 17:20:39 +02:00
7168950dfe Feat: es modules (#139)
* All file imports change from commonjs to es-module

* Improved plex error responses back from api

* Converted viewHistory to es module

* Es-module requires file extension, updated all imports

* Fix esmodule not having __dirname defined in scope

* Replace dynamic-require with fs readFileSync

* Short message service module function is exported as default

* Resolved lint issues & ignore import/extension rule until typescript

* All tests file imports changed from commonjs to es-module

* Import json fixtures with new helper
2022-08-25 17:19:23 +02:00
124 changed files with 1015 additions and 744 deletions

View File

@@ -7,6 +7,7 @@
"extends": ["eslint-config-airbnb-base", "plugin:prettier/recommended"],
"plugins": ["mocha"],
"rules": {
"import/extensions": 0,
"max-classes-per-file": 1,
"no-empty": [
2,

View File

@@ -1,4 +1,5 @@
FROM node:18
LABEL org.opencontainers.image.source https://github.com/kevinmidboe/seasonedShows
RUN mkdir -p /opt/seasonedShows/src
@@ -17,5 +18,3 @@ RUN cp configurations/development.json.example configurations/production.json
EXPOSE 31459
CMD ["yarn", "start"]
LABEL org.opencontainers.image.source https://github.com/kevinmidboe/seasoned

120
README.md
View File

@@ -1,4 +1,3 @@
<h1 align="center">
🌶 seasonedShows
</h1>
@@ -36,63 +35,73 @@
</p>
## <a name="demo-documentation"></a> Demo & Documentation
📺 [DEMO](https://kevinmidboe.com/request)
📺 [DEMO](https://request.movie)
📝 Documentation of the api.
💖 Checkout my [fancy vue.js page](https://github.com/KevinMidboe/seasonedRequest) for interfacing the api.
💖 Checkout my [fancy vue.js page](https://github.com/KevinMidboe/seasoned) for interfacing the api.
## <a name="about"></a> About
This is the backend api for [seasoned request] that allows for uesrs to request movies and shows by fetching movies from themoviedb api and checks them with your plex library to identify if a movie is already present or not. This api allows to search my query, get themoviedb movie lists like popular and now playing, all while checking if the item is already in your plex library. Your friends can create users to see what movies or shows they have requested and searched for.
The api also uses torrent_search to search for matching torrents and returns results from any site or service available from torrent_search. As a admin of the site you can query torrent_search and return a magnet link that can be added to a autoadd folder of your favorite torrent client.
This is the backend api for [seasoned request] that allows for uesrs to request movies and shows by fetching movies from themoviedb api and checks them with your plex library to identify if a movie is already present or not. This api allows to search my query, get themoviedb movie lists like popular and now playing, all while checking if the item is already in your plex library. Your friends can create users to see what movies or shows they have requested and searched for.
The api also uses torrent_search to search for matching torrents and returns results from any site or service available from torrent_search. As a admin of the site you can query torrent_search and return a magnet link that can be added to a autoadd folder of your favorite torrent client.
## <a name="key-features"></a> Key features
### Code
- Uses [tmdb api](https://www.themoviedb.org/documentation/api) with over 350k movies and 70k tv shows
- Written asynchronously
- Uses caching for external requests
- Test coverage
- CI and dependency integrated
- Use either config file or env_variables
- Uses [tmdb api](https://www.themoviedb.org/documentation/api) with over 350k movies and 70k tv shows
- Written asynchronously
- Uses caching for external requests
- Test coverage
- CI and dependency integrated
- Use either config file or env_variables
### Functionality
- Queries plex library to check if items exists
- Create admin and normal user accounts
- [torrent_search](https://github.com/KevinMidboe/torrent_search) to search for torrents
- Fetch curated lists from tmdb
- Queries plex library to check if items exists
- Create admin and normal user accounts
- [torrent_search](https://github.com/KevinMidboe/torrent_search) to search for torrents
- Fetch curated lists from tmdb
## <a name="installation"></a> Installation
Before we can use seasonedShows we need to download node and a package manager. For instructions on how to install [yarn](https://yarnpkg.com/en/) or [npm](https://www.npmjs.com) package managers refer to [wiki: install package manager](https://github.com/KevinMidboe/seasonedShows/wiki/Install-package-manager). This api is written with express using node.js as the JavaScript runtime engine. To download node.js head over the the official [node.js download page](https://nodejs.org/en/download/).
### Install seasonedShows
After you have downloaded a package manager and node.js javascript engine, the following will guide you through how to download, install and run seasonedShows.
### macOS
- Open terminal
- Install git. This can be done by running `xcode-select --install` in your favorite terminal.
- Install a package manager, refer to this [wiki page] for yarn or [wiki page] for npm
- Type: `git clone --recurse-submodules git@github.com:KevinMidboe/seasonedShows.git`
- Type: `git clone git@github.com:KevinMidboe/seasonedShows.git`
- Type: `cd seasonedShows/`
- Install required packages
* yarn: `yarn install`
* npm: `npm install`
- yarn: `yarn install`
- npm: `npm install`
- Start server:
* yarn: `yarn start`
* npm: `npm run start`
- yarn: `yarn start`
- npm: `npm run start`
- seasonedShows will now be running at http://localhost:31459
- To have seasonedShows run headless on startup, check out this wiki page to [install as a daemon].
### Linux
- Open terminal
- Install git
* Ubuntu/Debian: `sudo apt-get install git-core`
* Fedora: `sudo yum install git`
- Type: `git clone --recurse-submodules git@github.com:KevinMidboe/seasonedShows.git`
- Ubuntu/Debian: `sudo apt-get install git-core`
- Fedora: `sudo yum install git`
- Type: `git clone git@github.com:KevinMidboe/seasonedShows.git`
- Type: `cd seasonedShows/`
- Install required packages
* yarn: `yarn install`
* npm: `npm install`
- yarn: `yarn install`
- npm: `npm install`
- Start server:
* yarn: `yarn start`
* npm: `npm run start`
- yarn: `yarn start`
- npm: `npm run start`
- seasonedShows will now be running at http://localhost:31459
- To have seasonedShows run headless on startup, check out this wiki page to [install as a daemon].
@@ -101,42 +110,73 @@ After you have downloaded a package manager and node.js javascript engine, the f
After you have installed the required packages you will have a node_modules directory with all the packages required in packages.json.
### Requirements
- Node 7.6 < [wiki page]
- Plex library
## <a name="setup"></a> Setup and/ configuration
There is a config file template, what the values mean and how to change them.
Also show how to hide file from git if not want to show up as uncommitted file.
Also set variables in environment.
- Node 18 < [wiki page]
- Plex library
- Optional:
- redis
- deluge
- jackett
## <a name="setup"></a> Setup and configuration
Make a copy of configuration file in `configurations/` folder.
For use during development and with `yarn dev` command:
```bash
cp configurations/development.json.example configurations/development.json
```
For use during production and with `yarn start` command:
```bash
cp configurations/development.json.example configurations/production.json
```
Most important values to change here is adding [TMDB api key](https://developers.themoviedb.org/3/getting-started/introduction) and plex server IP.
### Optional setup
To allow authenticated or admin users add items [delugeClient](https://github.com/kevinmidboe/delugeClient) & [torrentSearch](https://github.com/KevinMidboe/torrent_search) can be setup to fetch and add magnet files. Both require python version >= 3.8 and can be downloaded using following pip command:
```bash
pip3 install delugeClient_kevin torrent_search
```
Both of these need to be configured, view their separate README's or find configuration files under `$HOME/.config/`.
## <a name="running"></a> Running/using
yarn/npm start. (can also say this above)
How to create service on linux. This means that
How to create service on linux. This means that
## <a name="daemon"></a> Setup a daemon
The next step is to setup seasonedShows api to run in the background as a daemon. I have written a [wiki page](https://github.com/KevinMidboe/seasonedShows/wiki/Install-as-a-daemon) on how to create a daemon on several unix distors and macOS.
*Please don't hesitate to add your own system if you get it running on something that is not yet lists on the formentioned wiki page.*
_Please don't hesitate to add your own system if you get it running on something that is not yet lists on the formentioned wiki page._
## <a name="contributing"></a> Contributing
- Fork it!
- Create your feature branch: git checkout -b my-new-feature
- Commit your changes: git commit -am 'Add some feature'
- Push to the branch: git push origin my-new-feature
- Submit a pull request
## Api documentation
The goal of this project is to create a full custom stack that can to everything surround downloading, organizing and notifiyng of new media. From the top down we have a website using [tmdb](https://www.themoviedb.com) api to search for from over 350k movies and 70k tv shows. Using [hjone72](https://github.com/hjone72/PlexAuth) great PHP reverse proxy we can have a secure way of allowing users to login with their plex credentials which limits request capabilites to only users that are authenticated to use your plex library.
seasonedShows is a intelligent organizer for your tv show episodes. It is made to automate and simplify to process of renaming and moving newly downloaded tv show episodes following Plex file naming and placement.
The goal of this project is to create a full custom stack that can to everything surround downloading, organizing and notifiyng of new media. From the top down we have a website using [tmdb](https://www.themoviedb.com) api to search for from over 350k movies and 70k tv shows. Using [hjone72](https://github.com/hjone72/PlexAuth) great PHP reverse proxy we can have a secure way of allowing users to login with their plex credentials which limits request capabilites to only users that are authenticated to use your plex library.
seasonedShows is a intelligent organizer for your tv show episodes. It is made to automate and simplify to process of renaming and moving newly downloaded tv show episodes following Plex file naming and placement.
So this is a multipart system that lets your plex users request movies, and then from the admin page the owner can.
## Installation
There are two main ways of
There are two main ways of
## Architecture
The flow of the system will first check for new folders in your tv shows directory, if a new file is found it's contents are analyzed, stored and tweets suggested changes to it's contents to use_admin.
Then there is a script for looking for replies on twitter by user_admin, if caanges are needed, it handles the changes specified and updates dtabbase.

View File

@@ -1,6 +1,6 @@
{
"database": {
"host": "../shows.db"
"host": "./shows.db"
},
"redis": {
"host": "localhost",

View File

@@ -6,6 +6,7 @@
"url": "https://www.opensource.org/licenses/mit-license.php"
},
"main": "webserver/server.js",
"type": "module",
"scripts": {
"start": "SEASONED_CONFIG=configurations/production.json NODE_ENV=production node src/webserver/server.js",
"dev": "SEASONED_CONFIG=configurations/development.json NODE_ENV=development node src/webserver/server.js",
@@ -26,7 +27,6 @@
"form-data": "^2.5.1",
"jsonwebtoken": "^8.5.1",
"km-moviedb": "^0.2.12",
"python-shell": "^0.5.0",
"raven": "^2.4.2",
"redis": "^3.0.2",
"sqlite3": "^5.0.1"

View File

@@ -1,44 +1,41 @@
const Plex = require("src/plex/plex");
const configuration = require("src/config/configuration").getInstance();
const plex = new Plex(configuration.get("plex", "ip"));
const establishedDatabase = require("src/database/database");
import Plex from "../src/plex/plex.js";
import establishedDatabase from "../src/database/database.js";
import Configuration from "../src/config/configuration.js";
const configuration = Configuration.getInstance();
const plex = new Plex(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
);
const queries = {
getRequestsNotYetInPlex: `SELECT * FROM requests WHERE status = 'requested' OR status = 'downloading'`,
saveNewStatus: `UPDATE requests SET status = ? WHERE id IS ? and type IS ?`
};
const getByStatus = () =>
const getRequestsNotYetInPlex = () =>
establishedDatabase.all(queries.getRequestsNotYetInPlex);
const checkIfRequestExistInPlex = async request => {
request.existsInPlex = await plex.existsInPlex(request);
return request;
};
async function getNewRequestMatchesInPlex() {
const requests = await getRequestsNotYetInPlex();
const exists = await Promise.all(
requests.map(request => plex.existsInPlex(request))
);
const commitNewStatus = (status, id, type, title) => {
console.log(type, title, "updated to:", status);
return requests.filter(() => exists.shift());
}
function commitNewStatus(status, id, type, title) {
console.log(`${type} ${title} updated to: ${status}`);
return establishedDatabase.run(queries.saveNewStatus, [status, id, type]);
};
}
const getNewRequestMatchesInPlex = async () => {
const requests = await getByStatus();
return Promise.all(requests.map(checkIfRequestExistInPlex))
.catch(error =>
console.log("error from checking plex for existance:", error)
)
.then(matchedRequests =>
matchedRequests.filter(request => request.existsInPlex)
);
};
const updateMatchInDb = (match, status) => {
function updateMatchInDb(match, status) {
return commitNewStatus(status, match.id, match.type, match.title);
};
}
getNewRequestMatchesInPlex()
.then(newMatches =>
Promise.all(newMatches.map(match => updateMatchInDb(match, "downloaded")))
)
.then(() => process.exit(0));
.finally(() => process.exit(0));

29
src/cache/redis.js vendored
View File

@@ -1,10 +1,11 @@
const configuration = require("../config/configuration").getInstance();
import redis from "redis";
import Configuration from "../config/configuration.js";
import redisMockClient from "./redisMock.js";
const configuration = Configuration.getInstance();
let client;
const mockCache = {};
try {
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");
@@ -19,21 +20,7 @@ try {
client.quit();
console.error("Unable to connect to redis, setting up redis-mock."); // eslint-disable-line no-console
client = {
get(key, callback) {
console.log(`redis-dummy get: ${key}`); // eslint-disable-line no-console
const hit = mockCache[key];
return Promise.resolve().then(callback(null, hit));
},
set(key, json, callback) {
console.log(`redis-dummy set: ${key}`); // eslint-disable-line no-console
mockCache[key] = JSON.stringify(json);
return Promise.resolve().then(callback(null, "OK"));
},
expire(key, TTL) {
console.log(`redis-dummy expire: ${key} with TTL ${TTL}`); // eslint-disable-line no-console
}
};
client = redisMockClient;
});
} catch (e) {}
@@ -72,7 +59,7 @@ function get(key) {
});
}
module.exports = {
get,
set
export default {
set,
get
};

19
src/cache/redisMock.js vendored Normal file
View File

@@ -0,0 +1,19 @@
const mockCache = {};
const redisMockClient = {
get(key, callback) {
// console.log(`redis-dummy get: ${key}`); // eslint-disable-line no-console
const hit = mockCache[key] || null;
return Promise.resolve(callback(null, hit));
},
set(key, json, callback) {
// console.log(`redis-dummy set: ${key}`); // eslint-disable-line no-console
mockCache[key] = JSON.stringify(json);
return Promise.resolve(callback(null, "OK"));
},
expire(key, TTL) {
console.log(`redis-dummy expire: ${key} with TTL ${TTL}`); // eslint-disable-line no-console
}
};
export default redisMockClient;

View File

@@ -1,13 +1,17 @@
const path = require("path");
const Field = require("./field");
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import Field from "./field.js";
let instance = null;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class Config {
constructor() {
this.location = Config.determineLocation();
// eslint-disable-next-line import/no-dynamic-require, global-require
this.fields = require(`${this.location}`);
this.fields = Config.readFileContents(this.location);
}
static getInstance() {
@@ -21,6 +25,17 @@ class Config {
return path.join(__dirname, "..", "..", process.env.SEASONED_CONFIG);
}
static readFileContents(location) {
let content = {};
try {
content = JSON.parse(fs.readFileSync(location, { encoding: "utf-8" }));
} catch (err) {
console.error(`Error loading configuration file at path: ${location}`);
}
return content;
}
get(section, option) {
if (
this.fields[section] === undefined ||
@@ -45,4 +60,4 @@ class Config {
}
}
module.exports = Config;
export default Config;

View File

@@ -12,4 +12,4 @@ class EnvironmentVariables {
}
}
module.exports = EnvironmentVariables;
export default EnvironmentVariables;

View File

@@ -1,5 +1,5 @@
const Filters = require("./filters");
const EnvironmentVariables = require("./environmentVariables");
import Filters from "./filters.js";
import EnvironmentVariables from "./environmentVariables.js";
class Field {
constructor(rawValue, environmentVariables) {
@@ -50,4 +50,4 @@ class Field {
}
}
module.exports = Field;
export default Field;

View File

@@ -31,4 +31,4 @@ class Filters {
}
}
module.exports = Filters;
export default Filters;

View File

@@ -1,5 +1,7 @@
const configuration = require("../config/configuration").getInstance();
const SqliteDatabase = require("./sqliteDatabase");
import SqliteDatabase from "./sqliteDatabase.js";
import Configuration from "../config/configuration.js";
const configuration = Configuration.getInstance();
const database = new SqliteDatabase(configuration.get("database", "host"));
/**
@@ -10,4 +12,4 @@ const database = new SqliteDatabase(configuration.get("database", "host"));
*/
Promise.resolve().then(() => database.setUp());
module.exports = database;
export default database;

View File

@@ -1,6 +1,10 @@
const fs = require("fs");
const path = require("path");
const sqlite3 = require("sqlite3").verbose();
import fs from "fs";
import path from "path";
import sqlite3 from "sqlite3";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class SqliteDatabase {
constructor(host) {
@@ -115,4 +119,4 @@ class SqliteDatabase {
}
}
module.exports = SqliteDatabase;
export default SqliteDatabase;

View File

@@ -5,4 +5,4 @@ class GitRepository {
}
}
module.exports = GitRepository;
export default GitRepository;

View File

@@ -15,4 +15,4 @@ class Media {
}
}
module.exports = Media;
export default Media;

View File

@@ -12,4 +12,4 @@ class MediaInfo {
}
}
module.exports = MediaInfo;
export default MediaInfo;

View File

@@ -9,4 +9,4 @@ class Player {
}
}
module.exports = Player;
export default Player;

View File

@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
const Media = require("./media");
import Media from "./media.js";
class Plex extends Media {
constructor(
@@ -30,4 +30,4 @@ class Plex extends Media {
}
}
module.exports = Plex;
export default Plex;

View File

@@ -1,6 +1,5 @@
/* eslint-disable camelcase */
const Media = require("./media");
import Media from "./media.js";
class TMDB extends Media {
// constructor(...args) {
@@ -45,4 +44,4 @@ class TMDB extends Media {
}
}
module.exports = TMDB;
export default TMDB;

View File

@@ -5,4 +5,4 @@ class User {
}
}
module.exports = User;
export default User;

View File

@@ -1,4 +1,6 @@
const configuration = require("../config/configuration").getInstance();
import Configuration from "../config/configuration.js";
const configuration = Configuration.getInstance();
class SMSUnexpectedError extends Error {
constructor(errorMessage) {
@@ -9,7 +11,7 @@ class SMSUnexpectedError extends Error {
}
}
const sendSMS = message => {
export default function sendSMS(message) {
const apiKey = configuration.get("sms", "apikey");
if (!apiKey) {
@@ -35,6 +37,4 @@ const sendSMS = message => {
.then(response => resolve(response))
.catch(error => reject(new SMSUnexpectedError(error)));
});
};
module.exports = { sendSMS };
}

View File

@@ -1,52 +1,124 @@
const http = require("http");
const { URL } = require("url");
const PythonShell = require("python-shell");
import http from "http";
import { URL } from "url";
import { spawn } from "child_process";
const establishedDatabase = require("../database/database");
import establishedDatabase from "../database/database.js";
import cache from "../cache/redis.js";
const cache = require("../cache/redis");
class SearchPackageNotFoundError extends Error {
constructor() {
const message = "Search is not setup, view logs.";
super(message);
const warningMessage = `Warning! Package 'torrentSearch' not setup! View project README.`;
console.log(warningMessage); /* eslint-disable-line no-console */
}
}
class AddMagnetPackageNotFoundError extends Error {
constructor() {
const message = "Adding magnet is not setup, view logs.";
super(message);
const warningMessage = `Warning! Package 'delugeClient' not setup! View project README.`;
console.log(warningMessage); /* eslint-disable-line no-console */
}
}
class InvalidMagnetUrlError extends Error {
constructor() {
const message = "Invalid magnet url.";
super(message);
}
}
class UnexpectedScriptError extends Error {
constructor(_package, error = null) {
const message = `There was an unexpected error while running package: ${_package}`;
super(message);
this.error = error;
// console.log("Unexpected script error:", error);
}
}
function getMagnetFromURL(url) {
return new Promise(resolve => {
const options = new URL(url);
if (options.protocol.includes("magnet")) resolve(url);
const options = new URL(url);
if (options?.protocol?.includes("magnet")) return Promise.resolve(url);
return new Promise((resolve, reject) => {
http.get(options, res => {
if (res.statusCode === 301 || res.statusCode === 302) {
resolve(res.headers.location);
}
if (res.statusCode !== 301 && res.statusCode !== 302)
reject(new InvalidMagnetUrlError());
if (!res?.headers?.location?.includes("magnet"))
reject(new InvalidMagnetUrlError());
return resolve(res.headers.location);
});
});
}
async function find(searchterm, callback) {
const options = {
pythonPath: "../torrent_search/env/bin/python3",
scriptPath: "../torrent_search",
args: [searchterm, "-s", "jackett", "--print"]
};
PythonShell.run("torrentSearch/search.py", options, callback);
// PythonShell does not support return
function removeNewLineListItem(list) {
return list.map(el => el.replace("\n", "")).filter(el => el.length !== 0);
}
async function callPythonAddMagnet(url, callback) {
getMagnetFromURL(url)
.then(magnet => {
const options = {
pythonPath: "../delugeClient/env/bin/python3",
scriptPath: "../delugeClient",
args: ["add", magnet]
};
function decodeBufferListToString(bufferList) {
let data = bufferList.map(bufferElement => bufferElement.toString());
if (data.length === 0) return null;
PythonShell.run("deluge_cli.py", options, callback);
})
.catch(err => {
throw new Error(err);
});
data = removeNewLineListItem(data);
return data.join("");
}
async function SearchPiratebay(_query) {
function addMagnetScript(magnet, callback) {
const data = [];
let error = null;
const args = ["add", magnet];
const addMagnet = spawn("delugeclient", args);
addMagnet.stdout.on("data", bufferedData => data.push(bufferedData));
addMagnet.stderr.on("data", bufferedError => {
error = bufferedError.toString();
});
addMagnet.on("exit", () => callback(error, decodeBufferListToString(data)));
addMagnet.on("error", error => {
callback(error);
});
}
function handleAddMagnetScriptError(error) {
if (error?.code === "ENOENT") return new AddMagnetPackageNotFoundError();
return new UnexpectedScriptError("delugeClient", error);
}
function searchScript(searchterm, callback) {
const data = [];
let error = null;
const args = [searchterm, "-s", "jackett", "--print"];
const torrentSearch = spawn("torrentsearch", args);
torrentSearch.stdout.on("data", bufferedData => data.push(bufferedData));
torrentSearch.stderr.on("data", bufferedError => {
error = bufferedError.toString();
});
torrentSearch.on("exit", () =>
callback(error, decodeBufferListToString(data))
);
torrentSearch.on("error", error => callback(error));
}
function handleSearchScriptError(error) {
if (error?.code === "ENOENT") return new SearchPackageNotFoundError();
return new UnexpectedScriptError("torrentSearch", error);
}
export async function SearchPiratebay(_query) {
let query = String(_query);
if (query?.includes("+")) {
@@ -54,49 +126,46 @@ async function SearchPiratebay(_query) {
}
const cacheKey = `pirate/${query}`;
try {
const hit = await cache.get(cacheKey);
return new Promise((resolve, reject) =>
cache
.get(cacheKey)
.then(resolve)
.catch(() =>
find(query, (err, results) => {
if (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"));
}
if (hit) {
return Promise.resolve(hit);
}
} catch (_) {}
if (results) {
const jsonData = JSON.parse(results[1], null, "\t");
cache.set(cacheKey, jsonData);
resolve(jsonData);
}
})
)
);
return new Promise((resolve, reject) => {
searchScript(query, (error, results) => {
if (error || !results) return reject(handleSearchScriptError(error));
const jsonData = JSON.parse(results, null, "\t");
cache.set(cacheKey, jsonData);
return resolve(jsonData);
});
});
}
function AddMagnet(magnet, name, tmdbId) {
return new Promise((resolve, reject) =>
callPythonAddMagnet(magnet, (err, results) => {
if (err) {
/* eslint-disable no-console */
console.log(err);
reject(Error("Enable to add torrent", err));
}
/* eslint-disable no-console */
console.log("result/error:", err, results);
export async function AddMagnet(magnetUrl, name, tmdbId) {
const magnet = await getMagnetFromURL(magnetUrl);
const insertRequestedMagnetQuery =
"INSERT INTO requested_torrent(magnet, torrent_name, tmdb_id) VALUES (?,?,?)";
return new Promise((resolve, reject) => {
addMagnetScript(magnet, (error, result) => {
if (error || !result) return reject(handleAddMagnetScriptError(error));
const magnetHash = result; // TODO save to database
const database = establishedDatabase;
const insertQuery =
"INSERT INTO requested_torrent(magnet,torrent_name,tmdb_id) VALUES (?,?,?)";
const response = database.run(insertQuery, [magnet, name, tmdbId]);
console.log(`Response from requsted_torrent insert: ${response}`);
resolve({ success: true });
})
);
return database
.run(insertRequestedMagnetQuery, [magnet, name, tmdbId])
.catch(error => reject(error))
.then(() =>
resolve({
success: true,
message: "Successfully added magnet",
hash: magnetHash
})
);
});
});
}
module.exports = { SearchPiratebay, AddMagnet };

View File

@@ -1,4 +1,4 @@
const Episode = require("./types/episode");
import Episode from "./types/episode.js";
function convertPlexToEpisode(plexEpisode) {
const episode = new Episode(
@@ -21,4 +21,5 @@ function convertPlexToEpisode(plexEpisode) {
return episode;
}
module.exports = convertPlexToEpisode;
export default convertPlexToEpisode;

View File

@@ -1,4 +1,4 @@
const Movie = require("./types/movie");
import Movie from "./types/movie.js";
function convertPlexToMovie(plexMovie) {
const movie = new Movie(plexMovie.title, plexMovie.year);
@@ -12,4 +12,4 @@ function convertPlexToMovie(plexMovie) {
return movie;
}
module.exports = convertPlexToMovie;
export default convertPlexToMovie;

View File

@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
const Plex = require("../media_classes/plex");
import Plex from "../media_classes/plex.js";
function translateAdded(date_string) {
return new Date(date_string * 1000);
@@ -32,4 +32,4 @@ function convertPlexToSeasoned(plex) {
return seasoned;
}
module.exports = convertPlexToSeasoned;
export default convertPlexToSeasoned;

View File

@@ -1,4 +1,4 @@
const Show = require("./types/show");
import Show from "./types/show.js";
function convertPlexToShow(plexShow) {
const show = new Show(plexShow.title, plexShow.year);
@@ -10,4 +10,4 @@ function convertPlexToShow(plexShow) {
return show;
}
module.exports = convertPlexToShow;
export default convertPlexToShow;

View File

@@ -1,8 +1,8 @@
const convertPlexToSeasoned = require("./convertPlexToSeasoned");
const convertStreamToMediaInfo = require("./convertStreamToMediaInfo");
const convertStreamToPlayer = require("./stream/convertStreamToPlayer");
const convertStreamToUser = require("./stream/convertStreamToUser");
const ConvertStreamToPlayback = require("./stream/convertStreamToPlayback");
import convertPlexToSeasoned from "./convertPlexToSeasoned.js";
import convertStreamToMediaInfo from "./convertStreamToMediaInfo.js";
import convertStreamToPlayer from "./stream/convertStreamToPlayer.js";
import convertStreamToUser from "./stream/convertStreamToUser.js";
import ConvertStreamToPlayback from "./stream/convertStreamToPlayback.js";
function convertPlexToStream(plexStream) {
const stream = convertPlexToSeasoned(plexStream);
@@ -16,4 +16,4 @@ function convertPlexToStream(plexStream) {
return stream;
}
module.exports = convertPlexToStream;
export default convertPlexToStream;

View File

@@ -1,4 +1,4 @@
const MediaInfo = require("../media_classes/mediaInfo");
import MediaInfo from "../media_classes/mediaInfo.js";
function convertStreamToMediaInfo(plexStream) {
const mediaInfo = new MediaInfo();
@@ -19,4 +19,4 @@ function convertStreamToMediaInfo(plexStream) {
return mediaInfo;
}
module.exports = convertStreamToMediaInfo;
export default convertStreamToMediaInfo;

View File

@@ -22,4 +22,4 @@ class mailTemplate {
}
}
module.exports = mailTemplate;
export default mailTemplate;

View File

@@ -1,7 +1,7 @@
const convertPlexToMovie = require("./convertPlexToMovie");
const convertPlexToShow = require("./convertPlexToShow");
const convertPlexToEpisode = require("./convertPlexToEpisode");
const redisCache = require("../cache/redis");
import convertPlexToMovie from "./convertPlexToMovie.js";
import convertPlexToShow from "./convertPlexToShow.js";
import convertPlexToEpisode from "./convertPlexToEpisode.js";
import redisCache from "../cache/redis.js";
class PlexRequestTimeoutError extends Error {
constructor() {
@@ -97,8 +97,9 @@ function mapResults(response) {
}
class Plex {
constructor(ip, port = 32400, cache = null) {
constructor(ip, token, port = 32400, cache = null) {
this.plexIP = ip;
this.token = token;
this.plexPort = port;
this.cache = cache || redisCache;
@@ -116,7 +117,7 @@ class Plex {
headers: { Accept: "application/json" }
};
return new Promise((resolve, reject) =>
return new Promise((resolve, reject) => {
this.cache
.get(cacheKey)
.then(machineInfo => resolve(machineInfo?.machineIdentifier))
@@ -132,8 +133,8 @@ class Plex {
}
reject(new PlexUnexpectedError());
})
);
});
});
}
async existsInPlex(tmdb) {
@@ -185,18 +186,24 @@ class Plex {
const url = `http://${this.plexIP}:${
this.plexPort
}/hubs/search?query=${fixedEncodeURIComponent(query)}`;
}/hubs/search?query=${fixedEncodeURIComponent(query)}&X-Plex-Token=${
this.token
}`;
const options = {
timeout: 20000,
headers: { Accept: "application/json" }
};
return new Promise((resolve, reject) =>
return new Promise((resolve, reject) => {
this.cache
.get(cacheKey)
.catch(() => fetch(url, options)) // else fetch fresh data
.then(successfullResponse)
.then(results => this.cache.set(cacheKey, results, 21600)) // 6 hours
.catch(() => {
// else fetch fresh data
return fetch(url, options)
.then(successfullResponse)
.then(results => this.cache.set(cacheKey, results, 21600)); // 6 hours
})
.then(mapResults)
.then(resolve)
.catch(error => {
@@ -205,8 +212,8 @@ class Plex {
}
reject(new PlexUnexpectedError());
})
);
});
});
}
// this is not guarenteed to work, but if we see a movie or
@@ -221,10 +228,10 @@ class Plex {
// 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");
console.log("Unable to delete, key might not exists"); // eslint-disable-line no-console
return response === 1;
});
}
}
module.exports = Plex;
export default Plex;

View File

@@ -1,5 +1,5 @@
const convertPlexToSeasoned = require("./convertPlexToSeasoned");
const convertPlexToStream = require("./convertPlexToStream");
import convertPlexToSeasoned from "./convertPlexToSeasoned.js";
import convertPlexToStream from "./convertPlexToStream.js";
// eslint-disable-next-line
function addAttributeIfTmdbInPlex(_tmdb, plexResult) {
@@ -41,9 +41,12 @@ class PlexRepository {
inPlex(_tmdbResult) {
const tmdbResult = { ..._tmdbResult };
this.search(tmdbResult.title)
return this.search(tmdbResult.title)
.then(plexResult => addAttributeIfTmdbInPlex(tmdbResult, plexResult))
.catch(() => {
// log("some error here::", error);
/**
* If something crashes with search from this function it probably
* fine to set the `matchedInPlex` attribute to false and return
@@ -103,4 +106,4 @@ class PlexRepository {
// }
}
module.exports = PlexRepository;
export default PlexRepository;

View File

@@ -1,8 +1,9 @@
const PlexRepository = require("./plexRepository");
const configuration = require("../config/configuration").getInstance();
const TMDB = require("../tmdb/tmdb");
const establishedDatabase = require("../database/database");
import PlexRepository from "./plexRepository.js";
import TMDB from "../tmdb/tmdb.js";
import establishedDatabase from "../database/database.js";
import Configuration from "../config/configuration.js";
const configuration = Configuration.getInstance();
const plexRepository = new PlexRepository(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
@@ -128,4 +129,4 @@ class RequestRepository {
}
}
module.exports = RequestRepository;
export default RequestRepository;

View File

@@ -11,4 +11,4 @@ class convertStreamToPlayback {
}
}
module.exports = convertStreamToPlayback;
export default convertStreamToPlayback;

View File

@@ -1,4 +1,4 @@
const Player = require("../../media_classes/player");
import Player from "../../media_classes/player.js";
function convertStreamToPlayer(plexStream) {
const player = new Player(plexStream.device, plexStream.address);
@@ -10,4 +10,4 @@ function convertStreamToPlayer(plexStream) {
return player;
}
module.exports = convertStreamToPlayer;
export default convertStreamToPlayer;

View File

@@ -1,7 +1,7 @@
const User = require("../../media_classes/user");
import User from "../../media_classes/user.js";
function convertStreamToUser(plexStream) {
return new User(plexStream.id, plexStream.title);
}
module.exports = convertStreamToUser;
export default convertStreamToUser;

View File

@@ -13,4 +13,4 @@ class Episode {
}
}
module.exports = Episode;
export default Episode;

View File

@@ -9,4 +9,4 @@ class Movie {
}
}
module.exports = Movie;
export default Movie;

View File

@@ -9,4 +9,4 @@ class Show {
}
}
module.exports = Show;
export default Show;

View File

@@ -1,7 +1,7 @@
const assert = require("assert");
import assert from "assert";
// const configuration = require("../config/configuration").getInstance();
// const TMDB = require("../tmdb/tmdb");
const establishedDatabase = require("../database/database");
import establishedDatabase from "../database/database.js";
// const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
@@ -33,7 +33,8 @@ class RequestRepository {
// downloaded: "(select status from requests where id is request.id and type is request.type limit 1)",
// deluge: '(select status from deluge_torrent where id is request.id and type is request.type limit 1)',
// fetchAllFilterStatus: 'select * from request where '
// readWithoutUserData: "select id, title, year, type, status, date from requests where id is ? and type is ?",
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 ?"
};
}
@@ -83,16 +84,16 @@ class RequestRepository {
return this.database
.get(this.queries.readWithoutUserData, [id, type])
.then(row => {
if (!row) return null;
if (!row) return Promise.resolve(null);
return {
return Promise.resolve({
id: row.id,
title: row.title,
year: row.year,
type: row.type,
status: row.status,
requested_date: new Date(row.date)
};
});
});
}
@@ -156,4 +157,4 @@ class RequestRepository {
}
}
module.exports = RequestRepository;
export default RequestRepository;

View File

@@ -45,4 +45,4 @@ function validFilter(filterParam) {
});
}
module.exports = { validSort, validFilter };
export default { validSort, validFilter };

View File

@@ -1,4 +1,4 @@
const establishedDatabase = require("../database/database");
import establishedDatabase from "../database/database.js";
class SearchHistoryCreateDatabaseError extends Error {
constructor(message = "an unexpected error occured", errorResponse = null) {
@@ -55,4 +55,4 @@ class SearchHistory {
}
}
module.exports = SearchHistory;
export default SearchHistory;

View File

@@ -4,4 +4,4 @@ class Stray {
}
}
module.exports = Stray;
export default Stray;

View File

@@ -1,7 +1,7 @@
const assert = require("assert");
const pythonShell = require("python-shell");
const Stray = require("./stray");
const establishedDatabase = require("../database/database");
import assert from "assert";
// import pythonShell from "python-shell";
import Stray from "./stray.js";
import establishedDatabase from "../database/database.js";
class StrayRepository {
constructor(database) {
@@ -48,21 +48,21 @@ class StrayRepository {
return this.database.get(this.queries.checkVerified, strayId).then(row => {
assert.notEqual(row, undefined, `Stray '${strayId}' already verified.`);
const options = {
pythonPath: "../app/env/bin/python3",
args: [strayId]
};
// const options = {
// pythonPath: "../app/env/bin/python3",
// args: [strayId]
// };
pythonShell.run("../app/moveSeasoned.py", options, (err, results) => {
if (err) throw err;
// TODO Add error handling!! StrayRepository.ERROR
// results is an array consisting of messages collected during execution
console.log("results: %j", results);
});
// pythonShell.run("../app/moveSeasoned.py", options, (err, results) => {
// if (err) throw err;
// // TODO Add error handling!! StrayRepository.ERROR
// // results is an array consisting of messages collected during execution
// console.log("results: %j", results);
// });
return this.database.run(this.queries.verify, strayId);
});
}
}
module.exports = StrayRepository;
export default StrayRepository;

View File

@@ -75,4 +75,4 @@ class Tautulli {
}
}
module.exports = Tautulli;
export default Tautulli;

View File

@@ -1,5 +1,5 @@
const assert = require("assert");
const establishedDatabase = require("../database/database");
import assert from "assert";
import establishedDatabase from "../database/database.js";
class Cache {
constructor(database) {
@@ -45,4 +45,4 @@ class Cache {
}
}
module.exports = Cache;
export default Cache;

View File

@@ -1,7 +1,7 @@
const moviedb = require("km-moviedb");
const redisCache = require("../cache/redis");
import moviedb from "km-moviedb";
import redisCache from "../cache/redis.js";
const { Movie, Show, Person, Credits, ReleaseDates } = require("./types");
import { Movie, Show, Person, Credits, ReleaseDates } from "./types.js";
class TMDBNotFoundError extends Error {
constructor(message) {
@@ -339,4 +339,4 @@ class TMDB {
}
}
module.exports = TMDB;
export default TMDB;

View File

@@ -1,7 +1,7 @@
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");
import Movie from "./types/movie.js";
import Show from "./types/show.js";
import Person from "./types/person.js";
import Credits from "./types/credits.js";
import ReleaseDates from "./types/releaseDates.js";
module.exports = { Movie, Show, Person, Credits, ReleaseDates };
export { Movie, Show, Person, Credits, ReleaseDates };

View File

@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
const Movie = require("./movie");
const Show = require("./show");
import Movie from "./movie.js";
import Show from "./show.js";
class CreditedMovie extends Movie {}
class CreditedShow extends Show {}
@@ -113,4 +113,4 @@ class Credits {
}
}
module.exports = Credits;
export default Credits;

View File

@@ -113,4 +113,4 @@ class Movie {
}
}
module.exports = Movie;
export default Movie;

View File

@@ -69,4 +69,4 @@ class Person {
}
}
module.exports = Person;
export default Person;

View File

@@ -89,4 +89,4 @@ class ReleaseDates {
}
}
module.exports = ReleaseDates;
export default ReleaseDates;

View File

@@ -86,4 +86,4 @@ class Show {
}
}
module.exports = Show;
export default Show;

View File

@@ -1,5 +1,5 @@
const jwt = require("jsonwebtoken");
const User = require("./user");
import jwt from "jsonwebtoken";
import User from "./user.js";
class Token {
constructor(user, admin = false, settings = null) {
@@ -38,4 +38,4 @@ class Token {
}
}
module.exports = Token;
export default Token;

View File

@@ -5,4 +5,4 @@ class User {
}
}
module.exports = User;
export default User;

View File

@@ -1,5 +1,5 @@
const assert = require("assert");
const establishedDatabase = require("../database/database");
import assert from "assert";
import establishedDatabase from "../database/database.js";
class LinkPlexUserError extends Error {
constructor(errorMessage = null) {
@@ -263,4 +263,4 @@ class UserRepository {
}
}
module.exports = UserRepository;
export default UserRepository;

View File

@@ -1,5 +1,5 @@
const bcrypt = require("bcrypt");
const UserRepository = require("./userRepository");
import bcrypt from "bcrypt";
import UserRepository from "./userRepository.js";
class UserSecurity {
constructor(database) {
@@ -72,4 +72,4 @@ class UserSecurity {
}
}
module.exports = UserSecurity;
export default UserSecurity;

View File

@@ -1,20 +1,73 @@
const express = require("express");
const Raven = require("raven");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
import express from "express";
import Raven from "raven";
import cookieParser from "cookie-parser";
import bodyParser from "body-parser";
const configuration = require("../config/configuration").getInstance();
import Configuration from "../config/configuration.js";
const reqTokenToUser = require("./middleware/reqTokenToUser");
const mustBeAuthenticated = require("./middleware/mustBeAuthenticated");
const mustBeAdmin = require("./middleware/mustBeAdmin");
const mustHaveAccountLinkedToPlex = require("./middleware/mustHaveAccountLinkedToPlex");
import reqTokenToUser from "./middleware/reqTokenToUser.js";
import mustBeAuthenticated from "./middleware/mustBeAuthenticated.js";
import mustBeAdmin from "./middleware/mustBeAdmin.js";
import mustHaveAccountLinkedToPlex from "./middleware/mustHaveAccountLinkedToPlex.js";
const listController = require("./controllers/list/listController");
const tautulli = require("./controllers/user/viewHistory");
const SettingsController = require("./controllers/user/settings");
const AuthenticatePlexAccountController = require("./controllers/user/authenticatePlexAccount");
import tautulli from "./controllers/user/viewHistory.js";
import {
getSettingsController,
updateSettingsController
} from "./controllers/user/settings.js";
import AuthenticatePlexAccountController from "./controllers/user/authenticatePlexAccount.js";
import UserRegisterController from "./controllers/user/register.js";
import UserLoginController from "./controllers/user/login.js";
import UserLogoutController from "./controllers/user/logout.js";
import UserSearchHistoryController from "./controllers/user/searchHistory.js";
import UserRequestsController from "./controllers/user/requests.js";
import SearchMultiController from "./controllers/search/multiSearch.js";
import SearchMovieController from "./controllers/search/movieSearch.js";
import SearchShowController from "./controllers/search/showSearch.js";
import SearchPersonController from "./controllers/search/personSearch.js";
import listController from "./controllers/list/listController.js";
import MovieCreditsController from "./controllers/movie/credits.js";
import MovieReleaseDatesController from "./controllers/movie/releaseDates.js";
import MovieInfoController from "./controllers/movie/info.js";
import ShowCreditsController from "./controllers/show/credits.js";
import ShowInfoController from "./controllers/show/info.js";
import PersonCreditsController from "./controllers/person/credits.js";
import PersonInfoController from "./controllers/person/info.js";
// TODO refactor python-shell dependency or remove stray admin functions
// import SeasonedAllController from "./controllers/seasoned/readStrays.js";
// import SeasonedInfoController from "./controllers/seasoned/strayById.js";
// import SeasonedVerifyController from "./controllers/seasoned/verifyStray.js";
import PlexSearchController from "./controllers/plex/search.js";
import PlexFetchRequestedController from "./controllers/plex/fetchRequested.js";
// import PlexRequestsInfo from "./controllers/plex/updateRequested.js";
import PlexWatchLinkController from "./controllers/plex/watchDirectLink.js";
import PlexHookController from "./controllers/plex/hookDump.js";
import PlexSubmitRequestController from "./controllers/plex/submitRequest.js";
import PlexRequestInfo from "./controllers/plex/readRequest.js";
import PlexSearchRequestController from "./controllers/plex/searchRequest.js";
import PlexPlayingController from "./controllers/plex/plexPlaying.js";
import PlexSearchMediaController from "./controllers/plex/searchMedia.js";
import PlexUpdateRequestedController from "./controllers/plex/updateRequested.js";
import RequestFetchAllController from "./controllers/request/fetchAllRequests.js";
import RequestInfoController from "./controllers/request/getRequest.js";
import RequestSubmitController from "./controllers/request/requestTmdbId.js";
import PirateSearchController from "./controllers/pirate/searchTheBay.js";
import PirateAddController from "./controllers/pirate/addMagnet.js";
import GitDumpController from "./controllers/git/dumpHook.js";
import EmojiController from "./controllers/misc/emoji.js";
const configuration = Configuration.getInstance();
// TODO: Have our raven router check if there is a value, if not don't enable raven.
Raven.config(configuration.get("raven", "DSN")).install();
@@ -65,30 +118,18 @@ router.get("/", (req, res) => {
/**
* User
*/
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.post("/v1/user", UserRegisterController);
router.post("/v1/user/login", UserLoginController);
router.post("/v1/user/logout", UserLogoutController);
router.get(
"/v1/user/settings",
mustBeAuthenticated,
SettingsController.getSettingsController
);
router.put(
"/v1/user/settings",
mustBeAuthenticated,
SettingsController.updateSettingsController
);
router.get("/v1/user/settings", mustBeAuthenticated, getSettingsController);
router.put("/v1/user/settings", mustBeAuthenticated, updateSettingsController);
router.get(
"/v1/user/search_history",
mustBeAuthenticated,
require("./controllers/user/searchHistory")
);
router.get(
"/v1/user/requests",
mustBeAuthenticated,
require("./controllers/user/requests")
UserSearchHistoryController
);
router.get("/v1/user/requests", mustBeAuthenticated, UserRequestsController);
router.post(
"/v1/user/link_plex",
@@ -125,111 +166,80 @@ router.get(
/**
* Seasoned
*/
router.get("/v1/seasoned/all", require("./controllers/seasoned/readStrays"));
router.get(
"/v1/seasoned/:strayId",
require("./controllers/seasoned/strayById")
);
router.post(
"/v1/seasoned/verify/:strayId",
require("./controllers/seasoned/verifyStray")
);
// router.get("/v1/seasoned/all", SeasonedAllController);
// router.get("/v1/seasoned/:strayId", SeasonedInfoController);
// router.post("/v1/seasoned/verify/:strayId", SeasonedVerifyController);
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/search/", SearchMultiController);
router.get("/v2/search/movie", SearchMovieController);
router.get("/v2/search/show", SearchShowController);
router.get("/v2/search/person", SearchPersonController);
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"));
router.get(
"/v2/movie/:id/release_dates",
require("./controllers/movie/releaseDates")
);
router.get("/v2/movie/:id", require("./controllers/movie/info"));
router.get("/v2/movie/:id/credits", MovieCreditsController);
router.get("/v2/movie/:id/release_dates", MovieReleaseDatesController);
router.get("/v2/movie/:id", MovieInfoController);
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"));
router.get("/v2/show/:id", require("./controllers/show/info"));
router.get("/v2/show/:id/credits", ShowCreditsController);
router.get("/v2/show/:id", ShowInfoController);
router.get("/v2/person/:id/credits", require("./controllers/person/credits"));
router.get("/v2/person/:id", require("./controllers/person/info"));
router.get("/v2/person/:id/credits", PersonCreditsController);
router.get("/v2/person/:id", PersonInfoController);
/**
* Plex
*/
router.get("/v2/plex/search", require("./controllers/plex/search"));
router.get("/v2/plex/search", PlexSearchController);
/**
* List
*/
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")
);
router.post(
"/v1/plex/request/:mediaId",
require("./controllers/plex/submitRequest")
);
router.post("/v1/plex/hook", require("./controllers/plex/hookDump"));
router.get("/v1/plex/search", PlexSearchMediaController);
router.get("/v1/plex/playing", PlexPlayingController);
router.get("/v1/plex/request", PlexSearchRequestController);
router.get("/v1/plex/request/:mediaId", PlexRequestInfo);
router.post("/v1/plex/request/:mediaId", PlexSubmitRequestController);
router.post("/v1/plex/hook", PlexHookController);
router.get(
"/v1/plex/watch-link",
mustBeAuthenticated,
require("./controllers/plex/watchDirectLink")
);
router.get("/v1/plex/watch-link", mustBeAuthenticated, PlexWatchLinkController);
/**
* Requests
*/
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")
);
router.get("/v2/request", RequestFetchAllController);
router.get("/v2/request/:id", RequestInfoController);
router.post("/v2/request", RequestSubmitController);
router.get("/v1/plex/requests/all", PlexFetchRequestedController);
router.put(
"/v1/plex/request/:requestId",
mustBeAuthenticated,
require("./controllers/plex/updateRequested")
PlexUpdateRequestedController
);
/**
* Pirate
*/
router.get(
"/v1/pirate/search",
mustBeAdmin,
require("./controllers/pirate/searchTheBay")
);
router.post(
"/v1/pirate/add",
mustBeAdmin,
require("./controllers/pirate/addMagnet")
);
router.get("/v1/pirate/search", mustBeAdmin, PirateSearchController);
router.post("/v1/pirate/add", mustBeAdmin, PirateAddController);
/**
* git
*/
router.post("/v1/git/dump", require("./controllers/git/dumpHook"));
router.post("/v1/git/dump", GitDumpController);
/**
* misc
*/
router.get("/v1/emoji", require("./controllers/misc/emoji"));
router.get("/v1/emoji", EmojiController);
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use("/api", router);
module.exports = app;
export default app;

View File

@@ -1,4 +1,4 @@
const GitRepository = require("../../../git/gitRepository");
import GitRepository from "../../../git/gitRepository.js";
const gitRepository = new GitRepository();
@@ -9,4 +9,4 @@ function dumpHookController(req, res) {
.catch(() => res.status(500));
}
module.exports = dumpHookController;
export default dumpHookController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
// there should be a translate function from query params to
@@ -65,7 +66,7 @@ const popularShows = (req, res) =>
const topRatedShows = (req, res) =>
fetchTmdbList(req, res, "miscTopRatedTvs", "show");
module.exports = {
export default {
nowPlayingMovies,
popularMovies,
topRatedMovies,

View File

@@ -16,12 +16,10 @@ const randomEmoji = () => {
* @param {Response} res
* @returns {Callback}
*/
function emojiController(req, res) {
export default function emojiController(req, res) {
res.send({
success: true,
emoji: randomEmoji(),
message: "Happy emoji-ing! 🌝"
});
}
module.exports = emojiController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const movieCreditsController = (req, res) => {
@@ -19,4 +20,4 @@ const movieCreditsController = (req, res) => {
});
};
module.exports = movieCreditsController;
export default movieCreditsController;

View File

@@ -1,9 +1,13 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const Plex = require("../../../plex/plex");
import TMDB from "../../../tmdb/tmdb.js";
import Plex from "../../../plex/plex.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const plex = new Plex(configuration.get("plex", "ip"));
const plex = new Plex(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
);
/**
* Controller: Retrieve information for a movie
@@ -52,4 +56,4 @@ async function movieInfoController(req, res) {
}
}
module.exports = movieInfoController;
export default movieInfoController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const movieReleaseDatesController = (req, res) => {
@@ -19,4 +20,4 @@ const movieReleaseDatesController = (req, res) => {
});
};
module.exports = movieReleaseDatesController;
export default movieReleaseDatesController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const personCreditsController = (req, res) => {
@@ -19,4 +20,4 @@ const personCreditsController = (req, res) => {
});
};
module.exports = personCreditsController;
export default personCreditsController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
/**
@@ -36,4 +37,4 @@ async function personInfoController(req, res) {
}
}
module.exports = personInfoController;
export default personInfoController;

View File

@@ -5,17 +5,20 @@
* @Last Modified time: 2017-10-21 15:32:43
*/
const PirateRepository = require("../../../pirate/pirateRepository");
import { AddMagnet } from "../../../pirate/pirateRepository.js";
function addMagnet(req, res) {
const { magnet, name } = req.body;
const tmdbId = req.body?.tmdb_id;
PirateRepository.AddMagnet(magnet, name, tmdbId)
AddMagnet(magnet, name, tmdbId)
.then(result => res.send(result))
.catch(error => {
res.status(500).send({ success: false, message: error.message });
res.status(error?.statusCode || 500).send({
success: false,
message: error?.message || "Unexpected error while adding magnet."
});
});
}
module.exports = addMagnet;
export default addMagnet;

View File

@@ -5,7 +5,7 @@
* @Last Modified time: 2018-02-26 19:56:32
*/
const PirateRepository = require("../../../pirate/pirateRepository");
import { SearchPiratebay } from "../../../pirate/pirateRepository.js";
// const pirateRepository = new PirateRepository();
/**
@@ -17,13 +17,16 @@ const PirateRepository = require("../../../pirate/pirateRepository");
function updateRequested(req, res) {
const { query, page, type } = req.query;
PirateRepository.SearchPiratebay(query, page, type)
SearchPiratebay(query, page, type)
.then(result => {
res.send({ success: true, results: result });
})
.catch(error => {
res.status(401).send({ success: false, message: error.message });
res.status(error?.statusCode || 500).send({
success: false,
message: error?.message || "Unexpected error while searching."
});
});
}
module.exports = updateRequested;
export default updateRequested;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../plex/requestRepository");
import RequestRepository from "../../../plex/requestRepository.js";
const requestRepository = new RequestRepository();
@@ -26,4 +26,4 @@ function fetchRequestedController(req, res) {
});
}
module.exports = fetchRequestedController;
export default fetchRequestedController;

View File

@@ -5,4 +5,4 @@ function hookDumpController(req, res) {
res.status(200);
}
module.exports = hookDumpController;
export default hookDumpController;

View File

@@ -1,5 +1,7 @@
const PlexRepository = require("../../../plex/plexRepository");
const configuration = require("../../../config/configuration").getInstance();
import PlexRepository from "../../../plex/plexRepository.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const plexRepository = new PlexRepository(
configuration.get("plex", "ip"),
@@ -17,4 +19,4 @@ function playingController(req, res) {
});
}
module.exports = playingController;
export default playingController;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../plex/requestRepository");
import RequestRepository from "../../../plex/requestRepository.js";
const requestRepository = new RequestRepository();
@@ -14,11 +14,11 @@ function readRequestController(req, res) {
requestRepository
.lookup(mediaId, type)
.then(movies => {
res.send(movies);
res.send(movies || {});
})
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
}
module.exports = readRequestController;
export default readRequestController;

View File

@@ -1,7 +1,12 @@
const configuration = require("../../../config/configuration").getInstance();
const Plex = require("../../../plex/plex");
import Plex from "../../../plex/plex.js";
import Configuration from "../../../config/configuration.js";
const plex = new Plex(configuration.get("plex", "ip"));
const configuration = Configuration.getInstance();
const plex = new Plex(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
);
/**
* Controller: Search plex for movies, shows and episodes by query
@@ -28,4 +33,4 @@ function searchPlexController(req, res) {
});
}
module.exports = searchPlexController;
export default searchPlexController;

View File

@@ -1,5 +1,7 @@
const PlexRepository = require("../../../plex/plexRepository");
const configuration = require("../../../config/configuration").getInstance();
import PlexRepository from "../../../plex/plexRepository.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const plexRepository = new PlexRepository(
configuration.get("plex", "ip"),
@@ -33,4 +35,4 @@ function searchMediaController(req, res) {
});
}
module.exports = searchMediaController;
export default searchMediaController;

View File

@@ -1,6 +1,6 @@
const SearchHistory = require("../../../searchHistory/searchHistory");
const Cache = require("../../../tmdb/cache");
const RequestRepository = require("../../../plex/requestRepository");
import SearchHistory from "../../../searchHistory/searchHistory.js";
import Cache from "../../../tmdb/cache.js";
import RequestRepository from "../../../plex/requestRepository.js";
const cache = new Cache();
const requestRepository = new RequestRepository(cache);
@@ -21,4 +21,4 @@ function searchRequestController(req, res) {
});
}
module.exports = searchRequestController;
export default searchRequestController;

View File

@@ -1,7 +1,8 @@
const configuration = require("../../../config/configuration").getInstance();
const RequestRepository = require("../../../request/request");
const TMDB = require("../../../tmdb/tmdb");
import RequestRepository from "../../../request/request.js";
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const request = new RequestRepository();
@@ -57,4 +58,4 @@ function submitRequestController(req, res) {
);
}
module.exports = submitRequestController;
export default submitRequestController;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../plex/requestRepository");
import RequestRepository from "../../../plex/requestRepository.js";
const requestRepository = new RequestRepository();
@@ -23,4 +23,4 @@ function updateRequested(req, res) {
});
}
module.exports = updateRequested;
export default updateRequested;

View File

@@ -1,7 +1,11 @@
const configuration = require("../../../config/configuration").getInstance();
const Plex = require("../../../plex/plex");
import Plex from "../../../plex/plex.js";
import Configuration from "../../../config/configuration.js";
const plex = new Plex(configuration.get("plex", "ip"));
const configuration = Configuration.getInstance();
const plex = new Plex(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
);
/**
* Controller: Search plex for movies, shows and episodes by query
@@ -25,4 +29,4 @@ function watchDirectLink(req, res) {
});
}
module.exports = watchDirectLink;
export default watchDirectLink;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../request/request");
import RequestRepository from "../../../request/request.js";
const request = new RequestRepository();
@@ -24,4 +24,4 @@ function fetchAllRequests(req, res) {
});
}
module.exports = fetchAllRequests;
export default fetchAllRequests;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../request/request");
import RequestRepository from "../../../request/request.js";
const request = new RequestRepository();
@@ -12,7 +12,7 @@ function fetchAllRequests(req, res) {
const { id } = req.params;
const { type } = req.query;
request
return request
.getRequestByIdAndType(id, type)
.then(result => {
if (!result) {
@@ -37,4 +37,4 @@ function fetchAllRequests(req, res) {
});
}
module.exports = fetchAllRequests;
export default fetchAllRequests;

View File

@@ -1,10 +1,11 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const RequestRepository = require("../../../request/request");
import TMDB from "../../../tmdb/tmdb.js";
import RequestRepository from "../../../request/request.js";
import Configuration from "../../../config/configuration.js";
// import sendSMS from "../../../notifications/sms.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const request = new RequestRepository();
// const { sendSMS } = require("src/notifications/sms");
const tmdbMovieInfo = id => {
return tmdb.movieInfo(id);
@@ -65,4 +66,4 @@ function requestTmdbIdController(req, res) {
});
}
module.exports = requestTmdbIdController;
export default requestTmdbIdController;

View File

@@ -1,7 +1,8 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const SearchHistory = require("../../../searchHistory/searchHistory");
import TMDB from "../../../tmdb/tmdb.js";
import SearchHistory from "../../../searchHistory/searchHistory.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const searchHistory = new SearchHistory();
@@ -33,4 +34,4 @@ function movieSearchController(req, res) {
});
}
module.exports = movieSearchController;
export default movieSearchController;

View File

@@ -1,7 +1,8 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const SearchHistory = require("../../../searchHistory/searchHistory");
import TMDB from "../../../tmdb/tmdb.js";
import SearchHistory from "../../../searchHistory/searchHistory.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const searchHistory = new SearchHistory();
@@ -33,4 +34,4 @@ function multiSearchController(req, res) {
});
}
module.exports = multiSearchController;
export default multiSearchController;

View File

@@ -1,7 +1,8 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const SearchHistory = require("../../../searchHistory/searchHistory");
import TMDB from "../../../tmdb/tmdb.js";
import SearchHistory from "../../../searchHistory/searchHistory.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const searchHistory = new SearchHistory();
@@ -33,4 +34,4 @@ function personSearchController(req, res) {
});
}
module.exports = personSearchController;
export default personSearchController;

View File

@@ -1,7 +1,8 @@
const SearchHistory = require("../../../searchHistory/searchHistory");
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import SearchHistory from "../../../searchHistory/searchHistory.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const searchHistory = new SearchHistory();
@@ -35,4 +36,4 @@ function showSearchController(req, res) {
});
}
module.exports = showSearchController;
export default showSearchController;

View File

@@ -1,4 +1,4 @@
const StrayRepository = require("../../../seasoned/strayRepository");
import StrayRepository from "../../../seasoned/strayRepository.js";
const strayRepository = new StrayRepository();
@@ -14,4 +14,4 @@ function readStraysController(req, res) {
});
}
module.exports = readStraysController;
export default readStraysController;

View File

@@ -1,4 +1,4 @@
const StrayRepository = require("../../../seasoned/strayRepository");
import StrayRepository from "../../../seasoned/strayRepository.js";
const strayRepository = new StrayRepository();
@@ -15,4 +15,4 @@ function strayByIdController(req, res) {
});
}
module.exports = strayByIdController;
export default strayByIdController;

View File

@@ -1,4 +1,4 @@
const StrayRepository = require("../../../seasoned/strayRepository");
import StrayRepository from "../../../seasoned/strayRepository.js";
const strayRepository = new StrayRepository();
@@ -15,4 +15,4 @@ function verifyStrayController(req, res) {
});
}
module.exports = verifyStrayController;
export default verifyStrayController;

View File

@@ -1,6 +1,7 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
import TMDB from "../../../tmdb/tmdb.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const showCreditsController = (req, res) => {
@@ -19,4 +20,4 @@ const showCreditsController = (req, res) => {
});
};
module.exports = showCreditsController;
export default showCreditsController;

View File

@@ -1,9 +1,13 @@
const configuration = require("../../../config/configuration").getInstance();
const TMDB = require("../../../tmdb/tmdb");
const Plex = require("../../../plex/plex");
import TMDB from "../../../tmdb/tmdb.js";
import Plex from "../../../plex/plex.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const tmdb = new TMDB(configuration.get("tmdb", "apiKey"));
const plex = new Plex(configuration.get("plex", "ip"));
const plex = new Plex(
configuration.get("plex", "ip"),
configuration.get("plex", "token")
);
/**
* Controller: Retrieve information for a show
@@ -47,4 +51,4 @@ async function showInfoController(req, res) {
}
}
module.exports = showInfoController;
export default showInfoController;

View File

@@ -1,45 +1,39 @@
const FormData = require("form-data");
const UserRepository = require("../../../user/userRepository");
import FormData from "form-data";
import UserRepository from "../../../user/userRepository.js";
const userRepository = new UserRepository();
class PlexAuthenticationError extends Error {
constructor(errorResponse, statusCode) {
constructor(errorResponse) {
const message =
"Unexptected error while authenticating to plex signin api. View error response.";
super(message);
this.errorResponse = errorResponse;
this.statusCode = statusCode;
this.statusCode = 500;
this.success = false;
this.source = "plex";
}
}
function handleError(error, res) {
const status = error?.status;
let { message, source } = error;
class PlexUnauthorizedError extends Error {
constructor(errorResponse) {
const message = "Unauthorized. Please check plex credentials.";
super(message);
if (status && message) {
if (status === 401) {
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:
"An unexpected error occured while authenticating your account with plex",
source
});
this.errorResponse = errorResponse;
this.statusCode = 401;
this.success = false;
this.source = "plex";
}
}
function handleResponse(response) {
if (!response.ok) {
throw new PlexAuthenticationError(response.statusText, response.status);
if (response?.status === 401)
throw new PlexUnauthorizedError(response?.statusText);
throw new PlexAuthenticationError(response?.statusText);
}
return response.json();
@@ -80,7 +74,16 @@ function link(req, res) {
"Successfully authenticated and linked plex account with seasoned request."
})
)
.catch(error => handleError(error, res));
.catch(error =>
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"Unexptected error occured while linking plex account",
success: error?.success || false,
source: error?.source,
errorResponse: error?.errorResponse
})
);
}
function unlink(req, res) {
@@ -94,10 +97,16 @@ function unlink(req, res) {
message: "Successfully unlinked plex account from seasoned request."
})
)
.catch(error => handleError(error, res));
.catch(error =>
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"Unexptected error occured while unlinking plex account",
success: error?.success || false,
source: error?.source,
errorResponse: error?.errorResponse
})
);
}
module.exports = {
link,
unlink
};
export default { link, unlink };

View File

@@ -1,9 +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();
import User from "../../../user/user.js";
import Token from "../../../user/token.js";
import UserSecurity from "../../../user/userSecurity.js";
import UserRepository from "../../../user/userRepository.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const secret = configuration.get("authentication", "secret");
const userSecurity = new UserSecurity();
const userRepository = new UserRepository();
@@ -54,4 +55,4 @@ async function loginController(req, res) {
}
}
module.exports = loginController;
export default loginController;

View File

@@ -13,4 +13,4 @@ async function logoutController(req, res) {
});
}
module.exports = logoutController;
export default logoutController;

View File

@@ -1,8 +1,9 @@
const User = require("../../../user/user");
const Token = require("../../../user/token");
const UserSecurity = require("../../../user/userSecurity");
const configuration = require("../../../config/configuration").getInstance();
import User from "../../../user/user.js";
import Token from "../../../user/token.js";
import UserSecurity from "../../../user/userSecurity.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const secret = configuration.get("authentication", "secret");
const userSecurity = new UserSecurity();
@@ -42,4 +43,4 @@ function registerController(req, res) {
});
}
module.exports = registerController;
export default registerController;

View File

@@ -1,4 +1,4 @@
const RequestRepository = require("../../../plex/requestRepository");
import RequestRepository from "../../../plex/requestRepository.js";
const requestRepository = new RequestRepository();
@@ -25,4 +25,4 @@ function requestsController(req, res) {
});
}
module.exports = requestsController;
export default requestsController;

View File

@@ -1,4 +1,4 @@
const SearchHistory = require("../../../searchHistory/searchHistory");
import SearchHistory from "../../../searchHistory/searchHistory.js";
const searchHistory = new SearchHistory();
@@ -21,4 +21,4 @@ function historyController(req, res) {
});
}
module.exports = historyController;
export default historyController;

View File

@@ -1,4 +1,4 @@
const UserRepository = require("../../../user/userRepository");
import UserRepository from "../../../user/userRepository.js";
const userRepository = new UserRepository();
/**
@@ -7,7 +7,7 @@ const userRepository = new UserRepository();
* @param {Response} res
* @returns {Callback}
*/
const getSettingsController = (req, res) => {
export function getSettingsController(req, res) {
const username = req.loggedInUser ? req.loggedInUser.username : null;
userRepository
@@ -18,9 +18,9 @@ const getSettingsController = (req, res) => {
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
};
}
const updateSettingsController = (req, res) => {
export function updateSettingsController(req, res) {
const username = req.loggedInUser ? req.loggedInUser.username : null;
// const idempotencyKey = req.headers("Idempotency-Key"); // TODO implement better transactions
@@ -35,9 +35,4 @@ const updateSettingsController = (req, res) => {
.catch(error => {
res.status(404).send({ success: false, message: error.message });
});
};
module.exports = {
getSettingsController,
updateSettingsController
};
}

View File

@@ -1,21 +1,46 @@
const configuration = require("../../../config/configuration").getInstance();
const Tautulli = require("../../../tautulli/tautulli");
import Tautulli from "../../../tautulli/tautulli.js";
import Configuration from "../../../config/configuration.js";
const configuration = Configuration.getInstance();
const apiKey = configuration.get("tautulli", "apiKey");
const ip = configuration.get("tautulli", "ip");
const port = configuration.get("tautulli", "port");
const tautulli = new Tautulli(apiKey, ip, port);
function handleError(error, res) {
const { status, message } = error;
class MissingDaysParameterError extends Error {
constructor() {
const message = "Missing parameter: days (number)";
super(message);
if (status && message) {
return res.status(status).send({ success: false, message });
this.statusCode = 422;
}
}
class MissingYAxisParameterError extends Error {
constructor(message = "Missing parameter: y_axis") {
super(message);
this.statusCode = 422;
}
}
function requiredPlaysByDayParams(req) {
const days = req.query?.days;
const yAxis = req.query?.y_axis;
let error;
if (days === undefined) {
error = new MissingDaysParameterError();
}
return res.status(500).send({
message: "An unexpected error occured while fetching view history"
});
const allowedYAxisDataType = ["plays", "duration"];
if (!allowedYAxisDataType.includes(yAxis)) {
error = new MissingYAxisParameterError(
`Y axis parameter must be one of values: [${allowedYAxisDataType}]`
);
}
return error ? Promise.reject(error) : Promise.resolve();
}
function watchTimeStatsController(req, res) {
@@ -30,7 +55,15 @@ function watchTimeStatsController(req, res) {
message: "watch time successfully fetched from tautulli"
});
})
.catch(error => handleError(error, res));
.catch(error => {
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"An unexpected error occured while fetching watch time",
errorResponse: error?.errorResponse,
success: false
});
});
}
function getPlaysByDayOfWeekController(req, res) {
@@ -38,8 +71,8 @@ function getPlaysByDayOfWeekController(req, res) {
const days = req.query?.days;
const yAxis = req.query?.y_axis;
return tautulli
.getPlaysByDayOfWeek(user.plexUserId, days, yAxis)
return requiredPlaysByDayParams(req)
.then(() => tautulli.getPlaysByDayOfWeek(user.plexUserId, days, yAxis))
.then(data =>
res.send({
success: true,
@@ -47,7 +80,15 @@ function getPlaysByDayOfWeekController(req, res) {
message: "play by day of week successfully fetched from tautulli"
})
)
.catch(error => handleError(error, res));
.catch(error => {
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"An unexpected error occured while fetching plays by day of week",
errorResponse: error?.errorResponse,
success: false
});
});
}
function getPlaysByDaysController(req, res) {
@@ -55,30 +96,23 @@ function getPlaysByDaysController(req, res) {
const days = req.query?.days;
const yAxis = req.query?.y_axis;
if (days === undefined) {
return res.status(422).send({
success: false,
message: "Missing parameter: days (number)"
});
}
const allowedYAxisDataType = ["plays", "duration"];
if (!allowedYAxisDataType.includes(yAxis)) {
return res.status(422).send({
success: false,
message: `Y axis parameter must be one of values: [${allowedYAxisDataType}]`
});
}
return tautulli
.getPlaysByDays(user.plexUserId, days, yAxis)
return requiredPlaysByDayParams(req, res)
.then(() => tautulli.getPlaysByDays(user.plexUserId, days, yAxis))
.then(data =>
res.send({
success: true,
data: data.response.data
})
)
.catch(error => handleError(error, res));
.catch(error => {
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"An unexpected error occured while fetching plays by days",
errorResponse: error?.errorResponse,
success: false
});
});
}
function userViewHistoryController(req, res) {
@@ -96,14 +130,22 @@ function userViewHistoryController(req, res) {
message: "view history successfully fetched from tautulli"
});
})
.catch(error => handleError(error, res));
.catch(error => {
res.status(error?.statusCode || 500).send({
message:
error?.message ||
"An unexpected error occured while fetching view history",
errorResponse: error?.errorResponse,
success: false
});
});
// const username = user.username;
}
module.exports = {
export default {
watchTimeStatsController,
getPlaysByDaysController,
getPlaysByDayOfWeekController,
getPlaysByDaysController,
userViewHistoryController
};

View File

@@ -1,4 +1,4 @@
const establishedDatabase = require("../../database/database");
import establishedDatabase from "../../database/database.js";
// eslint-disable-next-line consistent-return
const mustBeAdmin = (req, res, next) => {
@@ -28,4 +28,4 @@ const mustBeAdmin = (req, res, next) => {
});
};
module.exports = mustBeAdmin;
export default mustBeAdmin;

View File

@@ -10,4 +10,4 @@ const mustBeAuthenticated = (req, res, next) => {
next();
};
module.exports = mustBeAuthenticated;
export default mustBeAuthenticated;

Some files were not shown because too many files have changed in this diff Show More