Compare commits
2 Commits
feat/types
...
research/g
| Author | SHA1 | Date | |
|---|---|---|---|
| 488da889d8 | |||
| 8da7c159b1 |
@@ -1 +0,0 @@
|
||||
**/node_modules
|
||||
111
.drone.yml
111
.drone.yml
@@ -1,111 +0,0 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: seasoned api build
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/cache
|
||||
|
||||
steps:
|
||||
- name: Load cached packages
|
||||
image: sinlead/drone-cache:1.0.0
|
||||
settings:
|
||||
action: load
|
||||
key: yarn.lock
|
||||
mount: node_modules
|
||||
prefix: yarn-modules-seasoned_api
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /cache
|
||||
|
||||
- name: Install dependencies
|
||||
image: node:18.2.0
|
||||
commands:
|
||||
- node -v
|
||||
- yarn --version
|
||||
- yarn
|
||||
|
||||
- name: Cache packages
|
||||
image: sinlead/drone-cache:1.0.0
|
||||
settings:
|
||||
action: save
|
||||
key: yarn.lock
|
||||
mount: node_modules
|
||||
prefix: yarn-modules-seasoned_api
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /cache
|
||||
|
||||
# - name: Compile typescript
|
||||
# image: node:18.2.0
|
||||
# commands:
|
||||
# - yarn build:ts
|
||||
|
||||
- name: Run test suite
|
||||
image: node:18.2.0
|
||||
commands:
|
||||
- yarn test
|
||||
failure: ignore
|
||||
|
||||
- name: Lint project using eslint
|
||||
image: node:18.2.0
|
||||
commands:
|
||||
- yarn lint
|
||||
failure: ignore
|
||||
|
||||
- name: Build and publish docker image
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: ghcr.io
|
||||
repo: ghcr.io/kevinmidboe/seasoned_shows
|
||||
dockerfile: Dockerfile
|
||||
username:
|
||||
from_secret: GITHUB_USERNAME
|
||||
password:
|
||||
from_secret: GITHUB_PASSWORD
|
||||
tags: latest
|
||||
environment:
|
||||
TMDB_APIKEY:
|
||||
from_secret: TMDB_APIKEY
|
||||
PLEX_IP:
|
||||
from_secret: PLEX_IP
|
||||
PLEX_TOKEN:
|
||||
from_secret: PLEX_TOKEN
|
||||
when:
|
||||
event:
|
||||
- push
|
||||
branch:
|
||||
- master
|
||||
|
||||
# - name: deploy
|
||||
# image: appleboy/drone-ssh
|
||||
# pull: true
|
||||
# secrets:
|
||||
# - ssh_key
|
||||
# when:
|
||||
# event:
|
||||
# - push
|
||||
# branch:
|
||||
# - master
|
||||
# - drone-test
|
||||
# status: success
|
||||
# settings:
|
||||
# host: 10.0.0.54
|
||||
# username: root
|
||||
# key:
|
||||
# from_secret: ssh_key
|
||||
# command_timeout: 600s
|
||||
# script:
|
||||
# - /home/kevin/deploy/seasoned.sh
|
||||
|
||||
trigger:
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
# - pull_request
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"eslint-config-airbnb-base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["mocha", "@typescript-eslint"],
|
||||
"rules": {
|
||||
"lines-between-class-members": [
|
||||
"error",
|
||||
"always",
|
||||
{ "exceptAfterSingleLine": true }
|
||||
],
|
||||
"max-classes-per-file": 1,
|
||||
"no-empty": [
|
||||
2,
|
||||
{
|
||||
"allowEmptyCatch": true
|
||||
}
|
||||
],
|
||||
"no-promise-executor-return": 1,
|
||||
"no-shadow": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,12 +1,7 @@
|
||||
.DS_Store
|
||||
|
||||
configurations/development.json
|
||||
configurations/production.json
|
||||
.env
|
||||
development.json
|
||||
env
|
||||
shows.db
|
||||
lib/
|
||||
|
||||
node_modules
|
||||
*/package-lock.json
|
||||
.nyc_output
|
||||
yarn-error.log
|
||||
|
||||
10
.gitmodules
vendored
Normal file
10
.gitmodules
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Docs : https://git-scm.com/book/en/v2/Git-Tools-Submodules
|
||||
|
||||
[submodule "torrent_search"]
|
||||
path = torrent_search
|
||||
url = https://github.com/KevinMidboe/torrent_search.git
|
||||
branch = master
|
||||
|
||||
[submodule "delugeClient"]
|
||||
path = delugeClient
|
||||
url = https://github.com/KevinMidboe/delugeClient.git
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -7,7 +7,6 @@ script:
|
||||
- yarn coverage
|
||||
before_install:
|
||||
- cd seasoned_api
|
||||
- cp conf/development.json.example conf/development.json
|
||||
before_script:
|
||||
- yarn
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
|
||||
21
Dockerfile
21
Dockerfile
@@ -1,21 +0,0 @@
|
||||
FROM node:18
|
||||
|
||||
RUN mkdir -p /opt/seasonedShows/src
|
||||
|
||||
WORKDIR /opt/seasonedShows
|
||||
|
||||
COPY src/ src
|
||||
COPY configurations/ configurations
|
||||
COPY package.json .
|
||||
COPY yarn.lock .
|
||||
|
||||
RUN apt update
|
||||
RUN apt install node-pre-gyp -y
|
||||
RUN yarn
|
||||
RUN cp configurations/development.json.example configurations/production.json
|
||||
|
||||
EXPOSE 31459
|
||||
|
||||
CMD ["yarn", "start"]
|
||||
|
||||
LABEL org.opencontainers.image.source https://github.com/kevinmidboe/seasoned
|
||||
13
README.md
13
README.md
@@ -6,19 +6,16 @@
|
||||
<h4 align="center"> Season your media library with the shows and movies that you and your friends want.</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://drone.schleppe.cloud/KevinMidboe/seasonedShows">
|
||||
<img src="https://drone.schleppe.cloud/api/badges/KevinMidboe/seasonedShows/status.svg"
|
||||
alt="Drone CI">
|
||||
<a href="https://travis-ci.org/KevinMidboe/seasonedShows">
|
||||
<img src="https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=master"
|
||||
alt="Travis CI">
|
||||
</a>
|
||||
|
||||
<a href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=api/v2">
|
||||
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=api/v2" alt="">
|
||||
</a>
|
||||
|
||||
<a href="https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=package.json">
|
||||
<img src="https://snyk.io/test/github/KevinMidboe/seasonedShows/badge.svg?targetFile=package.json" alt="">
|
||||
<a href="https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json">
|
||||
<img src="https://snyk.io/test/github/KevinMidboe/seasonedShows/badge.svg?targetFile=seasoned_api/package.json" alt="">
|
||||
</a>
|
||||
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="">
|
||||
</a>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"database": {
|
||||
"host": "../shows.db"
|
||||
},
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379
|
||||
},
|
||||
"webserver": {
|
||||
"port": 31459,
|
||||
"origins": []
|
||||
},
|
||||
"tmdb": {
|
||||
"apiKey": ""
|
||||
},
|
||||
"plex": {
|
||||
"ip": "localhost",
|
||||
"token": ""
|
||||
},
|
||||
"tautulli": {
|
||||
"apiKey": "",
|
||||
"ip": "",
|
||||
"port": ""
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"database": {
|
||||
"host": ":memory:"
|
||||
},
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379
|
||||
},
|
||||
"webserver": {
|
||||
"port": 31400,
|
||||
"origins": []
|
||||
},
|
||||
"tmdb": {
|
||||
"apiKey": "bogus-api-key"
|
||||
},
|
||||
"plex": {
|
||||
"ip": "localhost",
|
||||
"token": ""
|
||||
},
|
||||
"tautulli": {
|
||||
"apiKey": "",
|
||||
"ip": "",
|
||||
"port": ""
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
60
package.json
60
package.json
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"name": "seasoned-api",
|
||||
"description": "Packages needed to build and commands to run seasoned api node server.",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "https://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "SEASONED_CONFIG=configurations/production.json NODE_ENV=production node lib/webserver/server.js",
|
||||
"dev": "SEASONED_CONFIG=configurations/development.json NODE_ENV=development node src/webserver/server.js",
|
||||
"test": "SEASONED_CONFIG=configurations/test.json mocha --recursive tests/unit tests/system",
|
||||
"build": "yarn tsc",
|
||||
"coverage:upload": "SEASONED_CONFIG=configurations/test.json mocha --recursive tests/unit && nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage": "SEASONED_CONFIG=configurations/test.json mocha --recursive tests/unit && nyc report",
|
||||
"lint": "eslint src tests",
|
||||
"update": "SEASONED_CONFIG=configurations/production.json node scripts/updateRequestsInPlex.js",
|
||||
"docs": "yarn apiDocs; yarn classDocs",
|
||||
"apiDocs": "",
|
||||
"classDocs": "scripts/generate-class-docs.sh",
|
||||
"postbuild": "cp -r src/database/schemas lib/database"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.0.1",
|
||||
"body-parser": "~1.18.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express": "~4.16.0",
|
||||
"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",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@babel/register": "^7.5.5",
|
||||
"@types/node": "^18.7.8",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"chai": "^4.3.6",
|
||||
"chai-http": "^4.3.0",
|
||||
"coveralls": "^3.0.5",
|
||||
"documentation": "^12.0.3",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-mocha": "10.1.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "8.4.0",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "15.1.0",
|
||||
"prettier": "^2.7.1"
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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");
|
||||
|
||||
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 = () =>
|
||||
establishedDatabase.all(queries.getRequestsNotYetInPlex);
|
||||
|
||||
const checkIfRequestExistInPlex = async request => {
|
||||
request.existsInPlex = await plex.existsInPlex(request);
|
||||
return request;
|
||||
};
|
||||
|
||||
const 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) => {
|
||||
return commitNewStatus(status, match.id, match.type, match.title);
|
||||
};
|
||||
|
||||
getNewRequestMatchesInPlex()
|
||||
.then(newMatches =>
|
||||
Promise.all(newMatches.map(match => updateMatchInDb(match, "downloaded")))
|
||||
)
|
||||
.then(() => process.exit(0));
|
||||
14
seasoned_api/.eslintrc.json
Normal file
14
seasoned_api/.eslintrc.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": [
|
||||
"airbnb-base"
|
||||
],
|
||||
"rules": {
|
||||
"indent": ["error", 3],
|
||||
"prefer-destructuring": 0,
|
||||
"camelcase": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"object-shorthand": 0,
|
||||
"comma-dangle": 0
|
||||
}
|
||||
}
|
||||
66
seasoned_api/.gitignore
vendored
Normal file
66
seasoned_api/.gitignore
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
|
||||
# - - - - -
|
||||
# My own gitignore files and folders
|
||||
shows.db
|
||||
conf/development.json
|
||||
|
||||
# conf/development-prod.json
|
||||
20
seasoned_api/conf/development.json.example
Normal file
20
seasoned_api/conf/development.json.example
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"database": {
|
||||
"host": "../shows.db"
|
||||
},
|
||||
"webserver": {
|
||||
"port": 31459
|
||||
},
|
||||
"tmdb": {
|
||||
"apiKey": ""
|
||||
},
|
||||
"plex": {
|
||||
"ip": ""
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
20
seasoned_api/conf/test.json
Normal file
20
seasoned_api/conf/test.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"database": {
|
||||
"host": ":memory:"
|
||||
},
|
||||
"webserver": {
|
||||
"port": 31400
|
||||
},
|
||||
"tmdb": {
|
||||
"apiKey": "bogus-api-key"
|
||||
},
|
||||
"plex": {
|
||||
"ip": "0.0.0.0"
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
57
seasoned_api/package.json
Normal file
57
seasoned_api/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "seasoned-api",
|
||||
"description": "Packages needed to build and commands to run seasoned api node server.",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "https://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "cross-env SEASONED_CONFIG=conf/development.json PROD=true NODE_PATH=. babel-node src/webserver/server.js",
|
||||
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --require @babel/register --recursive test/unit test/system",
|
||||
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --require @babel/register --recursive test && nyc report --reporter=text-lcov | coveralls",
|
||||
"lint": "./node_modules/.bin/eslint src/",
|
||||
"update": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/plex/updateRequestsInPlex.js",
|
||||
"docs": "yarn apiDocs; yarn classDocs",
|
||||
"apiDocs": "",
|
||||
"classDocs": "./script/generate-class-docs.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"bcrypt": "^3.0.6",
|
||||
"body-parser": "~1.18.2",
|
||||
"cross-env": "~5.1.4",
|
||||
"express": "~4.16.0",
|
||||
"express-graphql": "^0.9.0",
|
||||
"express-reload": "^1.2.0",
|
||||
"graphql": "^14.5.8",
|
||||
"jsonwebtoken": "^8.2.0",
|
||||
"km-moviedb": "^0.2.12",
|
||||
"node-cache": "^4.1.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"python-shell": "^0.5.0",
|
||||
"raven": "^2.4.2",
|
||||
"request": "^2.87.0",
|
||||
"request-promise": "^4.2",
|
||||
"sqlite3": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/node": "^7.5.5",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@babel/register": "^7.5.5",
|
||||
"@types/node": "^12.6.8",
|
||||
"coveralls": "^3.0.5",
|
||||
"documentation": "^12.0.3",
|
||||
"eslint": "^4.9.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^6.2.0",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^11.6.0",
|
||||
"supertest": "^3.0.0",
|
||||
"supertest-as-promised": "^4.0.1",
|
||||
"typescript": "^3.5.3"
|
||||
}
|
||||
}
|
||||
43
seasoned_api/src/config/configuration.js
Normal file
43
seasoned_api/src/config/configuration.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const path = require('path');
|
||||
const Field = require('./field.js');
|
||||
|
||||
let instance = null;
|
||||
|
||||
class Config {
|
||||
constructor() {
|
||||
this.location = Config.determineLocation();
|
||||
this.fields = require(`${this.location}`);
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Config();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static determineLocation() {
|
||||
return path.join(__dirname, '..', '..', process.env.SEASONED_CONFIG);
|
||||
}
|
||||
|
||||
get(section, option) {
|
||||
if (this.fields[section] === undefined || this.fields[section][option] === undefined) {
|
||||
throw new Error(`Filed "${section} => ${option}" does not exist.`);
|
||||
}
|
||||
|
||||
const field = new Field(this.fields[section][option]);
|
||||
|
||||
if (field.value === '') {
|
||||
const envField = process.env[['SEASONED', section.toUpperCase(), option.toUpperCase()].join('_')];
|
||||
if (envField !== undefined && envField.length !== 0) { return envField; }
|
||||
}
|
||||
|
||||
if (field.value === undefined) {
|
||||
throw new Error(`${section} => ${option} is empty.`);
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Config;
|
||||
15
seasoned_api/src/config/environmentVariables.js
Normal file
15
seasoned_api/src/config/environmentVariables.js
Normal file
@@ -0,0 +1,15 @@
|
||||
class EnvironmentVariables {
|
||||
constructor(variables) {
|
||||
this.variables = variables || process.env;
|
||||
}
|
||||
|
||||
get(variable) {
|
||||
return this.variables[variable];
|
||||
}
|
||||
|
||||
has(variable) {
|
||||
return this.get(variable) !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EnvironmentVariables;
|
||||
49
seasoned_api/src/config/field.js
Normal file
49
seasoned_api/src/config/field.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const Filters = require('./filters.js');
|
||||
const EnvironmentVariables = require('./environmentVariables.js');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return this.valueWithoutFilters;
|
||||
}
|
||||
|
||||
static base64Decode(string) {
|
||||
return new Buffer(string, 'base64').toString('utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Field;
|
||||
34
seasoned_api/src/config/filters.js
Normal file
34
seasoned_api/src/config/filters.js
Normal file
@@ -0,0 +1,34 @@
|
||||
class Filters {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.delimiter = '|';
|
||||
}
|
||||
|
||||
get filters() {
|
||||
return this.value.split(this.delimiter).slice(0, -1);
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return !this.hasValidType() || this.value.length === 0;
|
||||
}
|
||||
|
||||
has(filter) {
|
||||
return this.filters.includes(filter);
|
||||
}
|
||||
|
||||
hasValidType() {
|
||||
return (typeof this.value === 'string');
|
||||
}
|
||||
|
||||
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, '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Filters;
|
||||
14
seasoned_api/src/database/database.js
Normal file
14
seasoned_api/src/database/database.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const SqliteDatabase = require('src/database/sqliteDatabase');
|
||||
|
||||
const database = new SqliteDatabase(configuration.get('database', 'host'));
|
||||
/**
|
||||
* This module establishes a connection to the database
|
||||
* specified in the confgiuration file. It tries to setup
|
||||
* the required tables after successfully connecting.
|
||||
* If the tables already exists, it simply proceeds.
|
||||
*/
|
||||
Promise.resolve()
|
||||
.then(() => database.setUp());
|
||||
|
||||
module.exports = database;
|
||||
@@ -1,19 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS user (
|
||||
user_name varchar(127) UNIQUE,
|
||||
password varchar(127),
|
||||
admin boolean DEFAULT 0,
|
||||
email varchar(127) UNIQUE,
|
||||
admin boolean DEFAULT 0,
|
||||
primary key (user_name)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
user_name varchar(127) UNIQUE,
|
||||
dark_mode boolean DEFAULT 0,
|
||||
plex_userid varchar(127) DEFAULT NULL,
|
||||
emoji varchar(16) DEFAULT NULL,
|
||||
foreign key(user_name) REFERENCES user(user_name) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cache (
|
||||
key varchar(255),
|
||||
value blob,
|
||||
@@ -30,19 +22,18 @@ CREATE TABLE IF NOT EXISTS search_history (
|
||||
foreign key(user_name) REFERENCES user(user_name) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS requests (
|
||||
id NUMBER,
|
||||
CREATE TABLE IF NOT EXISTS requests(
|
||||
id TEXT,
|
||||
title TEXT,
|
||||
year NUMBER,
|
||||
poster_path TEXT DEFAULT NULL,
|
||||
background_path TEXT DEFAULT NULL,
|
||||
requested_by varchar(127) DEFAULT NULL,
|
||||
requested_by TEXT,
|
||||
ip TEXT,
|
||||
date DATE DEFAULT CURRENT_TIMESTAMP,
|
||||
status CHAR(25) DEFAULT 'requested' NOT NULL,
|
||||
user_agent CHAR(255) DEFAULT NULL,
|
||||
type CHAR(50) DEFAULT 'movie'
|
||||
-- foreign key(requested_by) REFERENCES user(user_name) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS request(
|
||||
@@ -1,5 +1,4 @@
|
||||
DROP TABLE IF EXISTS user;
|
||||
DROP TABLE IF EXISTS settings;
|
||||
DROP TABLE IF EXISTS search_history;
|
||||
DROP TABLE IF EXISTS requests;
|
||||
DROP TABLE IF EXISTS request;
|
||||
119
seasoned_api/src/database/sqliteDatabase.js
Normal file
119
seasoned_api/src/database/sqliteDatabase.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
|
||||
class SqliteDatabase {
|
||||
constructor(host) {
|
||||
this.host = host;
|
||||
this.connection = new sqlite3.Database(this.host);
|
||||
this.schemaDirectory = path.join(__dirname, 'schemas');
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the database.
|
||||
* @returns {Promise} succeeds if connection was established
|
||||
*/
|
||||
// connect() {
|
||||
// let database = ;
|
||||
// this.connection = database;
|
||||
// return database;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Run a SQL query against the database.
|
||||
* @param {String} sql SQL query
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
run(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.run(sql, parameters, (error, result) => {
|
||||
if (error)
|
||||
reject(error);
|
||||
resolve(result)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a SQL query against the database and retrieve all the rows.
|
||||
* @param {String} sql SQL query
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
all(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.all(sql, parameters, (err, rows) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(rows);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a SQL query against the database and retrieve one row.
|
||||
* @param {String} sql SQL query
|
||||
* @param {Array} parameters in the SQL query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get(sql, parameters) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connection.get(sql, parameters, (err, rows) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(rows);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
if (err) {
|
||||
console.log('ERROR: ', err);
|
||||
reject(err);
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the database by running setup.sql file in schemas/.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setUp() {
|
||||
const setupSchema = this.readSqlFile('setup.sql');
|
||||
return Promise.resolve(this.execute(setupSchema));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tears down the database by running tearDown.sql file in schemas/.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
tearDown() {
|
||||
const tearDownSchema = this.readSqlFile('teardown.sql');
|
||||
return Promise.resolve(this.execute(tearDownSchema));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file contents of a SQL file in schemas/.
|
||||
* @returns {String}
|
||||
*/
|
||||
readSqlFile(filename) {
|
||||
const schemaPath = path.join(this.schemaDirectory, filename);
|
||||
const schema = fs.readFileSync(schemaPath).toString('utf-8');
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SqliteDatabase;
|
||||
9
seasoned_api/src/git/gitRepository.js
Normal file
9
seasoned_api/src/git/gitRepository.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
class GitRepository {
|
||||
static dumpHook(body) {
|
||||
/* eslint-disable no-console */
|
||||
console.log(body);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GitRepository;
|
||||
19
seasoned_api/src/media_classes/media.js
Normal file
19
seasoned_api/src/media_classes/media.js
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
class Media {
|
||||
constructor(title, year, type) {
|
||||
this.title = title;
|
||||
this.year = year;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `N: ${this.title} | Y: ${this.year} | T: ${this.type}`;
|
||||
}
|
||||
|
||||
print() {
|
||||
/* eslint-disable no-console */
|
||||
console.log(this.toString());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Media;
|
||||
15
seasoned_api/src/media_classes/mediaInfo.js
Normal file
15
seasoned_api/src/media_classes/mediaInfo.js
Normal file
@@ -0,0 +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;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MediaInfo;
|
||||
12
seasoned_api/src/media_classes/player.js
Normal file
12
seasoned_api/src/media_classes/player.js
Normal file
@@ -0,0 +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;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Player;
|
||||
22
seasoned_api/src/media_classes/plex.js
Normal file
22
seasoned_api/src/media_classes/plex.js
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
const Media = require('src/media_classes/media');
|
||||
|
||||
class Plex extends Media {
|
||||
constructor(title, year, type, summary, poster_path, background_path, added, seasons, episodes) {
|
||||
super(title, year, type);
|
||||
|
||||
this.summary = summary;
|
||||
this.poster_path = poster_path;
|
||||
this.background_path = background_path;
|
||||
this.added = added;
|
||||
|
||||
this.seasons = seasons;
|
||||
this.episodes = episodes;
|
||||
}
|
||||
|
||||
print() {
|
||||
super.print();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Plex;
|
||||
33
seasoned_api/src/media_classes/tmdb.js
Normal file
33
seasoned_api/src/media_classes/tmdb.js
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
const Media = require('src/media_classes/media');
|
||||
|
||||
class TMDB extends Media {
|
||||
// constructor(...args) {
|
||||
constructor(title, year, type, id, summary, poster_path, background_path, popularity, score, release_status, tagline, seasons, episodes) {
|
||||
super(title, year, type);
|
||||
|
||||
this.id = id;
|
||||
this.summary = summary;
|
||||
this.poster_path = poster_path;
|
||||
this.background_path = background_path;
|
||||
this.popularity = popularity;
|
||||
this.score = score;
|
||||
|
||||
this.release_status = release_status;
|
||||
this.tagline = tagline;
|
||||
|
||||
this.seasons = seasons;
|
||||
this.episodes = episodes;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${super.toString()} | ID: ${this.id}`;
|
||||
}
|
||||
|
||||
print() {
|
||||
/* eslint-disable no-console */
|
||||
console.log(this.toString());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TMDB;
|
||||
8
seasoned_api/src/media_classes/user.js
Normal file
8
seasoned_api/src/media_classes/user.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class User {
|
||||
constructor(id, title) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
84
seasoned_api/src/pirate/pirateRepository.js
Normal file
84
seasoned_api/src/pirate/pirateRepository.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
const { URL } = require('url');
|
||||
const PythonShell = require('python-shell');
|
||||
|
||||
const establishedDatabase = require('src/database/database');
|
||||
|
||||
function getMagnetFromURL(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = new URL(url);
|
||||
if (options.protocol.includes('magnet'))
|
||||
resolve(url)
|
||||
|
||||
http.get(options, (res) => {
|
||||
if (res.statusCode == 301 || res.statusCode == 302) {
|
||||
resolve(res.headers.location)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function find(searchterm, callback) {
|
||||
const options = {
|
||||
pythonPath: '../torrent_search/env/bin/python3',
|
||||
scriptPath: '../torrent_search',
|
||||
args: [searchterm, '-s', 'jackett', '-f', '--print']
|
||||
}
|
||||
|
||||
PythonShell.run('torrentSearch/search.py', options, callback);
|
||||
// PythonShell does not support return
|
||||
}
|
||||
|
||||
|
||||
async function callPythonAddMagnet(url, callback) {
|
||||
getMagnetFromURL(url)
|
||||
.then((magnet) => {
|
||||
const options = {
|
||||
pythonPath: '../delugeClient/env/bin/python3',
|
||||
scriptPath: '../delugeClient',
|
||||
args: ['add', magnet]
|
||||
};
|
||||
|
||||
PythonShell.run('deluge_cli.py', options, callback);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
throw new Error(err);
|
||||
})
|
||||
}
|
||||
|
||||
async function SearchPiratebay(query) {
|
||||
return await new Promise((resolve, reject) => find(query, (err, results) => {
|
||||
if (err) {
|
||||
console.log('THERE WAS A FUCKING ERROR!\n', err);
|
||||
reject(Error('There was a error when searching for torrents'));
|
||||
}
|
||||
if (results) {
|
||||
resolve(JSON.parse(results, null, '\t'));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async function AddMagnet(magnet, name, tmdb_id) {
|
||||
return await 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);
|
||||
|
||||
database = establishedDatabase;
|
||||
insert_query = "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);
|
||||
|
||||
resolve({ success: true });
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = { SearchPiratebay, AddMagnet };
|
||||
@@ -1,11 +1,7 @@
|
||||
const Episode = require("./types/episode");
|
||||
const Episode = require('src/plex/types/episode');
|
||||
|
||||
function convertPlexToEpisode(plexEpisode) {
|
||||
const episode = new Episode(
|
||||
plexEpisode.title,
|
||||
plexEpisode.grandparentTitle,
|
||||
plexEpisode.year
|
||||
);
|
||||
const episode = new Episode(plexEpisode.title, plexEpisode.grandparentTitle, plexEpisode.year);
|
||||
episode.season = plexEpisode.parentIndex;
|
||||
episode.episode = plexEpisode.index;
|
||||
episode.summary = plexEpisode.summary;
|
||||
@@ -16,7 +12,7 @@ function convertPlexToEpisode(plexEpisode) {
|
||||
}
|
||||
|
||||
if (plexEpisode.originallyAvailableAt !== undefined) {
|
||||
episode.airdate = new Date(plexEpisode.originallyAvailableAt);
|
||||
episode.airdate = new Date(plexEpisode.originallyAvailableAt)
|
||||
}
|
||||
|
||||
return episode;
|
||||
@@ -1,10 +1,10 @@
|
||||
const Movie = require("./types/movie");
|
||||
const Movie = require('src/plex/types/movie');
|
||||
|
||||
function convertPlexToMovie(plexMovie) {
|
||||
const movie = new Movie(plexMovie.title, plexMovie.year);
|
||||
movie.rating = plexMovie.rating;
|
||||
movie.tagline = plexMovie.tagline;
|
||||
|
||||
|
||||
if (plexMovie.summary !== undefined) {
|
||||
movie.summary = plexMovie.summary;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user