1 Commits

Author SHA1 Message Date
Renovate Bot
e981b32f6b Add renovate.json 2019-07-13 04:53:32 +00:00
322 changed files with 7545 additions and 14052 deletions

View File

@@ -1 +0,0 @@
**/node_modules

View File

@@ -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

View File

@@ -1,25 +0,0 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"extends": ["eslint-config-airbnb-base", "plugin:prettier/recommended"],
"plugins": ["mocha"],
"rules": {
"import/extensions": 0,
"max-classes-per-file": 1,
"no-empty": [
2,
{
"allowEmptyCatch": true
}
],
"no-promise-executor-return": 1,
"no-shadow": "off",
"no-underscore-dangle": "off"
},
"env": {
"mocha": true
}
}

View File

@@ -1,18 +0,0 @@
{
"root": true,
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"extends": [
"eslint-config-airbnb-base",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"rules": {
"no-underscore-dangle": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"]
}
}

8
.gitignore vendored
View File

@@ -1,11 +1,7 @@
.DS_Store
configurations/development.json
configurations/production.json
.env
development.json
env
shows.db
node_modules
*/package-lock.json
.nyc_output
yarn-error.log

10
.gitmodules vendored Normal file
View 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

View File

@@ -1,9 +0,0 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"bracketSpacing": true,
"arrowParens": "avoid",
"trailingComma": "none"
}

View File

@@ -1,5 +1,5 @@
language: node_js
node_js: '11.9.0'
node_js: '8.7.0'
git:
submodules: true
script:
@@ -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

View File

@@ -1,20 +0,0 @@
FROM node:18
LABEL org.opencontainers.image.source https://github.com/kevinmidboe/seasonedShows
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"]

137
README.md
View File

@@ -1,3 +1,4 @@
<h1 align="center">
🌶 seasonedShows
</h1>
@@ -5,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 href="https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage">
<img src="https://coveralls.io/repos/github/KevinMidboe/seasonedShows/badge.svg?branch=coverage" 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>
@@ -35,73 +33,63 @@
</p>
## <a name="demo-documentation"></a> Demo & Documentation
📺 [DEMO](https://request.movie)
📺 [DEMO](https://kevinmidboe.com/request)
📝 Documentation of the api.
💖 Checkout my [fancy vue.js page](https://github.com/KevinMidboe/seasoned) for interfacing the api.
💖 Checkout my [fancy vue.js page](https://github.com/KevinMidboe/seasonedRequest) 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.
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.
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 git@github.com:KevinMidboe/seasonedShows.git`
- Type: `git clone --recurse-submodules 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 git@github.com:KevinMidboe/seasonedShows.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`
- 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].
@@ -110,73 +98,42 @@ 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
- 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="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.
## <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

@@ -12,7 +12,7 @@
},
"dependencies": {
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^1.0.0",
"css-loader": "^0.28.4",
"html-webpack-plugin": "^2.28.0",
"path": "^0.12.7",
"react": "^15.6.1",
@@ -30,8 +30,8 @@
"redux-thunk": "^2.2.0",
"urijs": "^1.18.12",
"webfontloader": "^1.6.28",
"webpack": "^4.0.0",
"webpack-dev-server": "^3.1.11",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.4.5",
"webpack-merge": "^4.1.0"
},
"devDependencies": {

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -1,55 +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",
"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",
"test": "SEASONED_CONFIG=configurations/test.json mocha --recursive tests/unit tests/system",
"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/development.json node scripts/updateRequestsInPlex.js",
"docs": "yarn apiDocs; yarn classDocs",
"apiDocs": "",
"classDocs": "scripts/generate-class-docs.sh"
},
"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",
"raven": "^2.4.2",
"redis": "^3.0.2",
"sqlite3": "^5.0.1"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"@types/node": "^12.6.8",
"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"
}
}

5
renovate.json Normal file
View File

@@ -0,0 +1,5 @@
{
"extends": [
"config:base"
]
}

View File

@@ -1,41 +0,0 @@
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 getRequestsNotYetInPlex = () =>
establishedDatabase.all(queries.getRequestsNotYetInPlex);
async function getNewRequestMatchesInPlex() {
const requests = await getRequestsNotYetInPlex();
const exists = await Promise.all(
requests.map(request => plex.existsInPlex(request))
);
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]);
}
function updateMatchInDb(match, status) {
return commitNewStatus(status, match.id, match.type, match.title);
}
getNewRequestMatchesInPlex()
.then(newMatches =>
Promise.all(newMatches.map(match => updateMatchInDb(match, "downloaded")))
)
.finally(() => process.exit(0));

View 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
View 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

View File

@@ -0,0 +1,20 @@
{
"database": {
"host": "../shows.db"
},
"webserver": {
"port": 31459
},
"tmdb": {
"apiKey": ""
},
"plex": {
"ip": ""
},
"raven": {
"DSN": ""
},
"authentication": {
"secret": "secret"
}
}

View 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"
}
}

45
seasoned_api/package.json Normal file
View File

@@ -0,0 +1,45 @@
{
"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=. node src/webserver/server.js",
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --recursive test/unit test/system",
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --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"
},
"dependencies": {
"axios": "^0.18.0",
"bcrypt-nodejs": "^0.0.3",
"body-parser": "~1.18.2",
"cross-env": "~5.1.4",
"express": "~4.16.0",
"jsonwebtoken": "^8.0.1",
"km-moviedb": "^0.2.13",
"mongoose": "~5.0.11",
"km-moviedb": "^0.2.12",
"node-cache": "^4.1.1",
"python-shell": "^0.5.0",
"request": "^2.85.0",
"request-promise": "^4.2",
"sqlite3": "^4.0.0"
},
"devDependencies": {
"coveralls": "^3.0.0",
"eslint": "^4.9.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"istanbul": "^0.4.5",
"mocha": "^5.0.4",
"mocha-lcov-reporter": "^1.3.0",
"nyc": "^11.6.0",
"raven": "^2.4.2",
"supertest": "^3.0.0",
"supertest-as-promised": "^4.0.1"
}
}

View 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;

View 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;

View 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;

View 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;

View 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;

View File

@@ -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(

View File

@@ -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;

View 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}
*/
async 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}
*/
async 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}
*/
async 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}
*/
async 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;

View File

@@ -0,0 +1,9 @@
class GitRepository {
static dumpHook(body) {
/* eslint-disable no-console */
console.log(body);
}
}
module.exports = GitRepository;

View 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;

View 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;

View 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;

View 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;

View 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;

View File

@@ -0,0 +1,8 @@
class User {
constructor(id, title) {
this.id = id;
this.title = title;
}
}
module.exports = User;

View File

@@ -0,0 +1,87 @@
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) {
resolve(res.headers.location)
}
});
});
}
async function find(searchterm, callback) {
const options = {
pythonPath: '../torrent_search/env/bin/python3.6',
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.6',
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) {
/* eslint-disable no-console */
console.log('THERE WAS A FUCKING ERROR!\n', err);
reject(Error('There was a error when searching for torrents'));
}
if (results) {
/* eslint-disable no-console */
console.log('result', 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 };

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