From 00ad5cf7a8f42cdc0298f0bc78f35c27efdf2e90 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 21:07:13 +0100 Subject: [PATCH 01/38] Added mocha, istanbul and supertest packages. Added coverange and test script. --- seasoned_api/package.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/seasoned_api/package.json b/seasoned_api/package.json index 8ac0fe3..a93104f 100644 --- a/seasoned_api/package.json +++ b/seasoned_api/package.json @@ -3,13 +3,14 @@ "main": "webserver/server.js", "scripts": { "start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js", + "test": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. mocha --recursive test/unit test/system", + "coverage": "cross-env PLANFLIX_CONFIG=conf/test.json NODE_PATH=. istanbul cover -x script/autogenerate-documentation.js --include-all-sources --dir test/.coverage node_modules/mocha/bin/_mocha --recursive test/**/* -- --report lcovonly && cat test/.coverage/lcov.info | coveralls && rm -rf test/.coverage", "lint": "./node_modules/.bin/eslint src/webserver/" }, "dependencies": { "bcrypt-nodejs": "^0.0.3", "body-parser": "~1.0.1", "cross-env": "^3.1.3", - "eslint": "^4.9.0", "express": "~4.0.0", "jsonwebtoken": "^8.0.1", "mongoose": "^3.6.13", @@ -20,11 +21,16 @@ "raven": "^2.2.1", "request": "^2.81.0", "request-promise": "^4.2", - "sqlite": "^2.2.1", - "sqlite3": "^2.5.0" + "sqlite": "^2.9.0" }, "devDependencies": { "eslint-config-airbnb-base": "^12.1.0", - "eslint-plugin-import": "^2.8.0" + "eslint-plugin-import": "^2.8.0", + "eslint": "^4.9.0", + "istanbul": "^0.4.5", + "mocha": "^3.1.0", + "supertest": "^2.0.1", + "supertest-as-promised": "^4.0.1" + } } -- 2.34.1 From f6c27482e4adfe4128a6d8c9e233eec0b0ca9af3 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 21:07:33 +0100 Subject: [PATCH 02/38] Added script for teardown of database. --- seasoned_api/src/database/sqliteDatabase.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/seasoned_api/src/database/sqliteDatabase.js b/seasoned_api/src/database/sqliteDatabase.js index 5ac1330..a68e207 100644 --- a/seasoned_api/src/database/sqliteDatabase.js +++ b/seasoned_api/src/database/sqliteDatabase.js @@ -69,6 +69,15 @@ class SqliteDatabase { return this.execute(setupSchema); } + /** + * Tears down the database by running tearDown.sql file in schemas/. + * @returns {Promise} + */ + tearDown() { + const tearDownSchema = this.readSqlFile('tearDown.sql'); + return this.execute(tearDownSchema); + } + /** * Returns the file contents of a SQL file in schemas/. * @returns {String} -- 2.34.1 From e19b604a78d87aeb9c2ed0e9e252f1d19840a04b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 21:08:35 +0100 Subject: [PATCH 03/38] Our teardown sql script. --- seasoned_api/src/database/schemas/teardown.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 seasoned_api/src/database/schemas/teardown.sql diff --git a/seasoned_api/src/database/schemas/teardown.sql b/seasoned_api/src/database/schemas/teardown.sql new file mode 100644 index 0000000..35fef21 --- /dev/null +++ b/seasoned_api/src/database/schemas/teardown.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS user; +DROP TABLE IF EXISTS search_history; +DROP TABLE IF EXISTS requests; \ No newline at end of file -- 2.34.1 From e4573e6808d2f00ea39507f665d9bc2644ec16a4 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 21:13:24 +0100 Subject: [PATCH 04/38] Forgot to remove path without test scripts yet. --- seasoned_api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/package.json b/seasoned_api/package.json index a93104f..596fd38 100644 --- a/seasoned_api/package.json +++ b/seasoned_api/package.json @@ -3,7 +3,7 @@ "main": "webserver/server.js", "scripts": { "start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js", - "test": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. mocha --recursive test/unit test/system", + "test": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. mocha --recursive test/system", "coverage": "cross-env PLANFLIX_CONFIG=conf/test.json NODE_PATH=. istanbul cover -x script/autogenerate-documentation.js --include-all-sources --dir test/.coverage node_modules/mocha/bin/_mocha --recursive test/**/* -- --report lcovonly && cat test/.coverage/lcov.info | coveralls && rm -rf test/.coverage", "lint": "./node_modules/.bin/eslint src/webserver/" }, -- 2.34.1 From 625717f7adee1a8748c378703c805f24b7d90658 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:16:36 +0100 Subject: [PATCH 05/38] Now when checking config file if the value is blank it checks env variables for a variable defined as uppcase section + option with a separator of _. --- seasoned_api/src/config/configuration.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seasoned_api/src/config/configuration.js b/seasoned_api/src/config/configuration.js index c8aa9a2..bd9d4c3 100644 --- a/seasoned_api/src/config/configuration.js +++ b/seasoned_api/src/config/configuration.js @@ -27,6 +27,11 @@ class Config { const field = new Field(this.fields[section][option]) + if (field.value === '') { + const envField = process.env[[section.toUpperCase(), option.toUpperCase()].join('_')] + if (envField !== undefined && envField.length !== 0) { return envField } + } + if (field.value === undefined) { throw new Error(`${section} => ${option} is empty.`); } -- 2.34.1 From fe64af856e512dea6b9300dd8a08eb0cee86dc01 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:16:57 +0100 Subject: [PATCH 06/38] Fixed a old bug. --- seasoned_api/src/config/field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/src/config/field.js b/seasoned_api/src/config/field.js index 2dbfc58..1a8b79a 100644 --- a/seasoned_api/src/config/field.js +++ b/seasoned_api/src/config/field.js @@ -4,7 +4,7 @@ const EnvironmentVariables = require('./environmentVariables.js'); class Field { constructor(rawValue, environmentVariables) { - this.rawValue, rawValue; + this.rawValue = rawValue; this.filters = new Filters(rawValue); this.valueWithoutFilters = this.filters.removeFiltersFromValue(); this.environmentVariables = new EnvironmentVariables(environmentVariables); -- 2.34.1 From fba38455233dc19d59c11a0e1d62d0686e703a0b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:17:45 +0100 Subject: [PATCH 07/38] Now we check if the values length is 0 not the filters varaible. --- seasoned_api/src/config/filters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/src/config/filters.js b/seasoned_api/src/config/filters.js index ebd813d..fbeab07 100644 --- a/seasoned_api/src/config/filters.js +++ b/seasoned_api/src/config/filters.js @@ -10,7 +10,7 @@ class Filters { } isEmpty() { - return !this.hasValidType() || this.filters.length === 0; + return !this.hasValidType() || this.value.length === 0; } has(filter) { -- 2.34.1 From 93e43d99543174653dcbbd736d5057a5fa5c4677 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:22:25 +0100 Subject: [PATCH 08/38] Added a template development.json file for build purposes. --- seasoned_api/conf/development.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 seasoned_api/conf/development.json diff --git a/seasoned_api/conf/development.json b/seasoned_api/conf/development.json new file mode 100644 index 0000000..abf8399 --- /dev/null +++ b/seasoned_api/conf/development.json @@ -0,0 +1,24 @@ +{ + "database": { + "host": "../shows.db" + }, + "webserver": { + "port": 31459 + }, + "tmdb": { + "apiKey": "" + }, + "raven": { + "DSN": "" + }, + "mail": { + "host": "", + "user": "", + "password": "", + "user_pi": "", + "password_pi": "" + }, + "authentication": { + "secret": "secret" + } +} -- 2.34.1 From 54eca33dfae16869da7a0b082bbb47848d2e53b9 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:27:20 +0100 Subject: [PATCH 09/38] Forgot to update the node version in travis config file. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3140bb8..4f8b8e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ { 'dist': 'trusty', 'language': 'node_js', - 'node_js': '6.9.5', + 'node_js': '8.7.0', 'cache': 'yarn', 'scripts': [ npm run test @@ -11,4 +11,4 @@ ], 'before_script': 'yarn', 'os': 'linux', -} \ No newline at end of file +} -- 2.34.1 From bced4e052d603ed46f49ee74adeda8826075f6bf Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:42:53 +0100 Subject: [PATCH 10/38] There was a weird thing with the file permissions so it would not be read by travis. Now it should be fixed --- seasoned_api/src/database/schemas/teardown.sql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/seasoned_api/src/database/schemas/teardown.sql b/seasoned_api/src/database/schemas/teardown.sql index 35fef21..34d52bd 100644 --- a/seasoned_api/src/database/schemas/teardown.sql +++ b/seasoned_api/src/database/schemas/teardown.sql @@ -1,3 +1 @@ -DROP TABLE IF EXISTS user; -DROP TABLE IF EXISTS search_history; -DROP TABLE IF EXISTS requests; \ No newline at end of file +teardown.sql.bak -- 2.34.1 From fb225767086483b045ee64f8797c0c04f80118c4 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 22:51:08 +0100 Subject: [PATCH 11/38] Travis is still not reading our teardown file. --- seasoned_api/src/database/schemas/teardown.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seasoned_api/src/database/schemas/teardown.sql b/seasoned_api/src/database/schemas/teardown.sql index 34d52bd..ebc1ac9 100644 --- a/seasoned_api/src/database/schemas/teardown.sql +++ b/seasoned_api/src/database/schemas/teardown.sql @@ -1 +1,4 @@ -teardown.sql.bak +DROP TABLE IF EXISTS user; +DROP TABLE IF EXISTS search_history; +DROP TABLE IF EXISTS list; +DROP TABLE IF EXISTS list_content; -- 2.34.1 From 6a725e3921f8824650c8fab5a36c29db71b7c191 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 23:06:02 +0100 Subject: [PATCH 12/38] Added tons of console.logs to try find out where the problem with travis not being able to read file lies. --- seasoned_api/src/database/sqliteDatabase.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seasoned_api/src/database/sqliteDatabase.js b/seasoned_api/src/database/sqliteDatabase.js index a68e207..c30ae26 100644 --- a/seasoned_api/src/database/sqliteDatabase.js +++ b/seasoned_api/src/database/sqliteDatabase.js @@ -75,6 +75,7 @@ class SqliteDatabase { */ tearDown() { const tearDownSchema = this.readSqlFile('tearDown.sql'); + console.log(tearDownSchema); return this.execute(tearDownSchema); } @@ -83,8 +84,11 @@ class SqliteDatabase { * @returns {String} */ readSqlFile(filename) { + console.log(filename) const schemaPath = path.join(this.schemaDirectory, filename); + console.log(schemaPath) const schema = fs.readFileSync(schemaPath).toString('utf-8'); + console.log(schema) return schema; } } -- 2.34.1 From 0459fb93125a01f1298e116626f5289d29a03712 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 23:18:13 +0100 Subject: [PATCH 13/38] More testing for this bug. --- seasoned_api/src/database/sqliteDatabase.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/src/database/sqliteDatabase.js b/seasoned_api/src/database/sqliteDatabase.js index c30ae26..d30f9f1 100644 --- a/seasoned_api/src/database/sqliteDatabase.js +++ b/seasoned_api/src/database/sqliteDatabase.js @@ -88,7 +88,7 @@ class SqliteDatabase { const schemaPath = path.join(this.schemaDirectory, filename); console.log(schemaPath) const schema = fs.readFileSync(schemaPath).toString('utf-8'); - console.log(schema) + console.log('schema: ', schema) return schema; } } -- 2.34.1 From f0e8f84e1262e5b5278d0f4d5329aba377ae0ff2 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 20 Dec 2017 23:20:49 +0100 Subject: [PATCH 14/38] Building with osx now --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4f8b8e0..359cacb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,13 @@ { - 'dist': 'trusty', 'language': 'node_js', 'node_js': '8.7.0', 'cache': 'yarn', 'scripts': [ - npm run test + ' npm run test', ], 'before_install': [ 'cd seasoned_api', ], 'before_script': 'yarn', - 'os': 'linux', + 'os': 'osx', } -- 2.34.1 From e6a34a0503d97869fac1b27b735afdd438b523a6 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 00:38:46 +0100 Subject: [PATCH 15/38] Back to linux. plz fix plzzzzz --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 359cacb..6f664eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,14 @@ { + 'dist': 'trusty', 'language': 'node_js', 'node_js': '8.7.0', 'cache': 'yarn', 'scripts': [ - ' npm run test', + 'npm run test' ], 'before_install': [ 'cd seasoned_api', ], 'before_script': 'yarn', - 'os': 'osx', + 'os': 'linux', } -- 2.34.1 From 908fca6dd0d6c9156f57787b8a2c64f761d2faac Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 00:42:44 +0100 Subject: [PATCH 16/38] Trying to remove teardown between tests. --- seasoned_api/test/helpers/resetDatabase.js | 1 - 1 file changed, 1 deletion(-) diff --git a/seasoned_api/test/helpers/resetDatabase.js b/seasoned_api/test/helpers/resetDatabase.js index 2498852..8fd83a7 100644 --- a/seasoned_api/test/helpers/resetDatabase.js +++ b/seasoned_api/test/helpers/resetDatabase.js @@ -4,7 +4,6 @@ function resetDatabase() { const database = new SqliteDatabase(':memory:'); return Promise.resolve() .then(() => database.connect()) - .then(() => database.tearDown()) .then(() => database.setUp()); } -- 2.34.1 From 2623649c5d426180c59b59847bf47f4cb8040918 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 00:46:52 +0100 Subject: [PATCH 17/38] Tried to re-add teardown and updated it to drop only tables that are in the database. Crossing fingers for a second pass. --- seasoned_api/src/database/schemas/teardown.sql | 3 +-- seasoned_api/test/helpers/resetDatabase.js | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seasoned_api/src/database/schemas/teardown.sql b/seasoned_api/src/database/schemas/teardown.sql index ebc1ac9..750cbab 100644 --- a/seasoned_api/src/database/schemas/teardown.sql +++ b/seasoned_api/src/database/schemas/teardown.sql @@ -1,4 +1,3 @@ DROP TABLE IF EXISTS user; DROP TABLE IF EXISTS search_history; -DROP TABLE IF EXISTS list; -DROP TABLE IF EXISTS list_content; +DROP TABLE IF EXISTS requests; diff --git a/seasoned_api/test/helpers/resetDatabase.js b/seasoned_api/test/helpers/resetDatabase.js index 8fd83a7..06a5152 100644 --- a/seasoned_api/test/helpers/resetDatabase.js +++ b/seasoned_api/test/helpers/resetDatabase.js @@ -3,7 +3,8 @@ const SqliteDatabase = require('src/database/sqliteDatabase'); function resetDatabase() { const database = new SqliteDatabase(':memory:'); return Promise.resolve() - .then(() => database.connect()) + .then(() => database.connect()); + .then(() => database.tearDown()); .then(() => database.setUp()); } -- 2.34.1 From 9e160a7a9150303395944b930ba304c4875790f2 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 21 Dec 2017 00:52:34 +0100 Subject: [PATCH 18/38] Added build status to radme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffa6c70..3a29c52 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 🌶 seasonedShows -Your customly seasoned movie and show requester, downloader and organizer +[![Build Status](https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=testing)](https://travis-ci.org/KevinMidboe/seasonedShows) + +Your customly *seasoned* movie and show requester, downloader and organizer. ## About 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. -- 2.34.1 From b4352ad72176b119e2a60103f3f42c130e0c6693 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 00:53:38 +0100 Subject: [PATCH 19/38] Too many ; bro --- seasoned_api/test/helpers/resetDatabase.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seasoned_api/test/helpers/resetDatabase.js b/seasoned_api/test/helpers/resetDatabase.js index 06a5152..2498852 100644 --- a/seasoned_api/test/helpers/resetDatabase.js +++ b/seasoned_api/test/helpers/resetDatabase.js @@ -3,8 +3,8 @@ const SqliteDatabase = require('src/database/sqliteDatabase'); function resetDatabase() { const database = new SqliteDatabase(':memory:'); return Promise.resolve() - .then(() => database.connect()); - .then(() => database.tearDown()); + .then(() => database.connect()) + .then(() => database.tearDown()) .then(() => database.setUp()); } -- 2.34.1 From 601440d5406564de39c7cd0b1f92fa8cb3df1803 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 01:00:47 +0100 Subject: [PATCH 20/38] Ok. I can explain this bug. It seems commenting out teardown in resetDatabase solves our issue that travis cannot find our teardown script eventough it outputs the exact path it should be at. Will come back to this as it seems it works everywhere else. Good night. --- seasoned_api/test/helpers/resetDatabase.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/test/helpers/resetDatabase.js b/seasoned_api/test/helpers/resetDatabase.js index 2498852..fdb5c0b 100644 --- a/seasoned_api/test/helpers/resetDatabase.js +++ b/seasoned_api/test/helpers/resetDatabase.js @@ -4,7 +4,7 @@ function resetDatabase() { const database = new SqliteDatabase(':memory:'); return Promise.resolve() .then(() => database.connect()) - .then(() => database.tearDown()) + // .then(() => database.tearDown()) .then(() => database.setUp()); } -- 2.34.1 From d92e6d8c7845564f44608af5a31a3d04062b0eaa Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 21 Dec 2017 01:01:43 +0100 Subject: [PATCH 21/38] Psst. Removed console logs from debug hunt. --- seasoned_api/src/database/sqliteDatabase.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/seasoned_api/src/database/sqliteDatabase.js b/seasoned_api/src/database/sqliteDatabase.js index d30f9f1..a68e207 100644 --- a/seasoned_api/src/database/sqliteDatabase.js +++ b/seasoned_api/src/database/sqliteDatabase.js @@ -75,7 +75,6 @@ class SqliteDatabase { */ tearDown() { const tearDownSchema = this.readSqlFile('tearDown.sql'); - console.log(tearDownSchema); return this.execute(tearDownSchema); } @@ -84,11 +83,8 @@ class SqliteDatabase { * @returns {String} */ readSqlFile(filename) { - console.log(filename) const schemaPath = path.join(this.schemaDirectory, filename); - console.log(schemaPath) const schema = fs.readFileSync(schemaPath).toString('utf-8'); - console.log('schema: ', schema) return schema; } } -- 2.34.1 From 76069f4fea6411e9daf9159dfcb7c3016149f5be Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 21 Dec 2017 01:04:00 +0100 Subject: [PATCH 22/38] Added license badfge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3a29c52..cde5c89 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # 🌶 seasonedShows [![Build Status](https://travis-ci.org/KevinMidboe/seasonedShows.svg?branch=testing)](https://travis-ci.org/KevinMidboe/seasonedShows) +[![DUB](https://img.shields.io/dub/l/vibe-d.svg)]() Your customly *seasoned* movie and show requester, downloader and organizer. -- 2.34.1 From 976d5cf69f6f17b49bbcd2333d07b4fd4a723c87 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 2 Jan 2018 15:30:23 +0100 Subject: [PATCH 23/38] TorrentTable gets passed a json list of torrents and this displays it in a table view. --- client/app/components/admin/torrentTable.jsx | 85 ++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 client/app/components/admin/torrentTable.jsx diff --git a/client/app/components/admin/torrentTable.jsx b/client/app/components/admin/torrentTable.jsx new file mode 100644 index 0000000..94d0f43 --- /dev/null +++ b/client/app/components/admin/torrentTable.jsx @@ -0,0 +1,85 @@ +import React, { Component } from 'react'; + +import { fetchJSON } from '../http.jsx'; + +class TorrentTable extends Component { + + constructor() { + super(); + this.state = { + torrentResponse: '', + showResults: false, + } + } + + componentWillReceiveProps(props) { + if (props.response !== undefined) { + this.setState({ + torrentResponse: props.response.map((torrent, index) => { + return ( + + {torrent.name} + {torrent.size} + {torrent.seed} + + + ) + }), + showResults: true, + }) + } + else { + this.setState({ + showResults: false, + }) + } + } + + sendToDownload(torrent) { + let data = {magnet: torrent.magnet} + fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/add', 'POST', data) + .then((response) => { + // TODO: Show a card with response that the item has been sent, and the status of response. + console.log(response) + }) + .catch((error) => { + console.error(error); + }) + } + + generateTable() { + let style = { + table: { + width: '80%', + marginRight: 'auto', + marginLeft: 'auto', + }, + } + + return ( + + + + + + + + + + + { this.state.torrentResponse } + +
NameSizeSeedAdd
+ ); + } + + render() { + return ( +
+ { this.state.showResults ? this.generateTable() : null } +
+ ) + } +} + +export default TorrentTable \ No newline at end of file -- 2.34.1 From 4d99cae74c92d992862acabb097110f4dc231e3c Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 15:52:28 +0100 Subject: [PATCH 24/38] Pulled our info button out to its own component, changed the size of the poster that is fetched from tmdb from width of 300 to 185 pixels. Also some minor changes to the info displayed, no also has type listed in the object. --- client/app/components/SearchObject.jsx | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/client/app/components/SearchObject.jsx b/client/app/components/SearchObject.jsx index 2be95d3..b61f4ea 100644 --- a/client/app/components/SearchObject.jsx +++ b/client/app/components/SearchObject.jsx @@ -5,6 +5,7 @@ import Notifications, {notify} from 'react-notify-toast'; // StyleComponents import searchObjectCSS from './styles/searchObject.jsx'; import buttonsCSS from './styles/buttons.jsx'; +import InfoButton from './buttons/InfoButton.jsx'; var MediaQuery = require('react-responsive'); @@ -48,7 +49,7 @@ class SearchObject { if (this.poster == null || this.poster == undefined) { var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' } else { - var posterPath = 'https://image.tmdb.org/t/p/w300' + this.poster; + var posterPath = 'https://image.tmdb.org/t/p/w185' + this.poster; } var backgroundPath = 'https://image.tmdb.org/t/p/w640_and_h360_bestv2/' + this.background; @@ -75,11 +76,6 @@ class SearchObject { ; } - if (this.type === 'movie') - var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id - else if (this.type === 'show') - var themoviedbLink = 'https://www.themoviedb.org/tv/' + this.id - // TODO go away from using mediaQuery, and create custom resizer return (
@@ -92,7 +88,9 @@ class SearchObject {
{this.title}

- Released: { this.year } | Rating: {this.rating} + + Released: { this.year } | Rating: {this.rating} | Type: {this.type} +

{this.summary}

@@ -108,16 +106,7 @@ class SearchObject {
{foundInPlex} - - - - Info - - +
-- 2.34.1 From 33cb6f5f09c1843d8b7369933133447f409f5e89 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:03:11 +0100 Subject: [PATCH 25/38] In admin we added a updatehandler so that a child can update this parent element. Also added our new sidebar for filtering our requested items by query and category. --- client/app/components/admin/Admin.jsx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/client/app/components/admin/Admin.jsx b/client/app/components/admin/Admin.jsx index 50047b1..d2faeef 100644 --- a/client/app/components/admin/Admin.jsx +++ b/client/app/components/admin/Admin.jsx @@ -20,10 +20,16 @@ class AdminComponent extends React.Component { this.state = { requested_objects: '', } + + this.updateHandler = this.updateHandler.bind(this) } // Fetches all requested elements and updates the state with response componentWillMount() { + this.fetchRequestedItems() + } + + fetchRequestedItems() { fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/requests/all', 'GET') .then(result => { this.setState({ @@ -32,6 +38,10 @@ class AdminComponent extends React.Component { }) } + updateHandler() { + this.fetchRequestedItems() + } + // Displays loginform if not logged in and passes response from // api call to sidebar and infoPanel through props verifyLoggedIn() { @@ -52,18 +62,19 @@ class AdminComponent extends React.Component { return (
+
+ +
-
- -
) } -- 2.34.1 From 279b004aad1344bc7ba5cc831b7a366beb7c1d52 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:19:43 +0100 Subject: [PATCH 26/38] We re-wrote most off the look and feel of this page. No we get a header that is generated with all the info for a requested element. This has a indicator generated by generateStatusIndicator, showing if the element is requested, downloading or downloaded. Then we have a function for generating the type icon, that is movie/show that is displayed in the header. The summary for a movie might be long so if it is over 180 chars than it is cut and a show more button is displayed. After the info on the item we import PirateSearch that takes a name input and displays a search result for that query in a table view. --- .../app/components/admin/AdminRequestInfo.jsx | 165 ++++++++++++++---- 1 file changed, 128 insertions(+), 37 deletions(-) diff --git a/client/app/components/admin/AdminRequestInfo.jsx b/client/app/components/admin/AdminRequestInfo.jsx index 2f51113..c483fc3 100644 --- a/client/app/components/admin/AdminRequestInfo.jsx +++ b/client/app/components/admin/AdminRequestInfo.jsx @@ -3,6 +3,7 @@ import React, { Component } from 'react'; import { fetchJSON } from '../http.jsx'; import PirateSearch from './PirateSearch.jsx' +import InfoButton from '../buttons/InfoButton.jsx'; // Stylesheets import requestInfoCSS from '../styles/adminRequestInfo.jsx' @@ -11,6 +12,12 @@ import buttonsCSS from '../styles/buttons.jsx'; // Interactive button import Interactive from 'react-interactive'; + +String.prototype.capitalize = function() { + return this.charAt(0).toUpperCase() + this.slice(1); +} + + class AdminRequestInfo extends Component { constructor() { @@ -18,6 +25,8 @@ class AdminRequestInfo extends Component { this.state = { statusValue: '', + movieInfo: undefined, + expandedSummary: false, } this.requestInfo = ''; @@ -26,12 +35,14 @@ class AdminRequestInfo extends Component { componentWillReceiveProps(props) { this.requestInfo = props.selectedRequest; this.state.statusValue = this.requestInfo.status; + this.state.expandedSummary = false; + this.fetchIteminfo() } userAgent(agent) { if (agent) { try { - return agent.split(" ")[1].replace(/[\(\;]/g, ''); + return agent.split(" ")[1].replace(/[\(\;]/g, ''); } catch(e) { return agent; @@ -62,60 +73,140 @@ class AdminRequestInfo extends Component { fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + itemID, 'PUT', apiData) .then((response) => { console.log('Response, updateRequestStatus: ', response) - }) - - this.setState({ - statusValue: eventValue + this.props.updateHandler() }) } + generateStatusIndicator(status) { + switch (status) { + case 'requested': + // Yellow + return 'linear-gradient(to right, rgb(63, 195, 243) 0, rgb(63, 195, 243) 10px, #fff 4px, #fff 100%) no-repeat' + case 'downloading': + // Blue + return 'linear-gradient(to right, rgb(255, 225, 77) 0, rgb(255, 225, 77) 10px, #fff 4px, #fff 100%) no-repeat' + case 'downloaded': + // Green + return 'linear-gradient(to right, #39aa56 0, #39aa56 10px, #fff 4px, #fff 100%) no-repeat' + default: + return 'linear-gradient(to right, grey 0, grey 10px, #fff 4px, #fff 100%) no-repeat' + } + } + + generateTypeIcon(type) { + if (type === 'show') + return ( + + ) + else if (type === 'movie') + return ( + + ) + else + return ( + + ) + } + + toggleSummmaryLength() { + this.setState({ + expandedSummary: !this.state.expandedSummary + }) + } + + generateSummary() { + // { this.state.movieInfo != undefined ? this.state.movieInfo.summary : 'Loading...' } + const info = this.state.movieInfo; + if (info !== undefined) { + const summary = this.state.movieInfo.summary + const summary_short = summary.slice(0, 180); + + return ( +
+ Matched: {String(info.matchedInPlex)}
+ Rating: {info.rating}
+ Popularity: {info.popularity}
+ { + (summary.length > 180 && this.state.expandedSummary === false) ? + Summary: { summary_short } this.toggleSummmaryLength()}>... Show more + : + Summary: { summary } this.toggleSummmaryLength()}> Show less + + } +
+ ) + } else { + return Loading... + } + } + requested_by_user(request_user) { if (request_user === 'NULL') return undefined return ( - Requested by: {request_user} + Requested by: {request_user} ) } + fetchIteminfo() { + const itemID = this.requestInfo.id; + const type = this.requestInfo.type; + + fetchJSON('https://apollo.kevinmidboe.com/api/v1/tmdb/' + itemID +'&type='+type, 'GET') + .then((response) => { + console.log('Response, getInfo:', response) + this.setState({ + movieInfo: response + }); + console.log(this.state.movieInfo) + }) + } + displayInfo() { const request = this.props.selectedRequest; if (request) { - return ( -
-
- {request.name} - {request.year} -
+ requestInfoCSS.info.background = this.generateStatusIndicator(request.status); -
- type: {request.type}
+ return ( +
- {this.generateStatusDropdown()}
- - status: {request.status}
- ip: {request.ip}
- user_agent: {this.userAgent(request.user_agent)}
- request_date: {request.requested_date}
- { this.requested_by_user(request.requested_by) } +
+ {request.name} {request.year} + + {this.generateTypeIcon(request.type)} + {/*{request.type.capitalize()}
*/} +
+
+ +
+
+ Movie poster image +
+ +
+

Request info

+ + status:{ request.status }
+ ip:{ request.ip }
+ user_agent:{ this.userAgent(request.user_agent) }
+ request_date:{ request.requested_date}
+ { this.requested_by_user(request.requested_by) }
+ { this.generateStatusDropdown() }
+
+ +
+

Movie info

+ + { this.generateSummary() } +
+
+ + +
- -
- {}} - style={buttonsCSS.edit} - focus={buttonsCSS.edit_hover} - hover={buttonsCSS.edit_hover}> - - Show info - - - -
-
- ) + ) } } -- 2.34.1 From f7c6f6603b9a46a8ddbce43c1219d0b0d270ed42 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:20:18 +0100 Subject: [PATCH 27/38] Renamed style variable to match the imported stylesheet. --- client/app/components/admin/AdminRequestInfo.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/app/components/admin/AdminRequestInfo.jsx b/client/app/components/admin/AdminRequestInfo.jsx index c483fc3..a67e7e1 100644 --- a/client/app/components/admin/AdminRequestInfo.jsx +++ b/client/app/components/admin/AdminRequestInfo.jsx @@ -3,15 +3,13 @@ import React, { Component } from 'react'; import { fetchJSON } from '../http.jsx'; import PirateSearch from './PirateSearch.jsx' +// No in use! import InfoButton from '../buttons/InfoButton.jsx'; // Stylesheets import requestInfoCSS from '../styles/adminRequestInfo.jsx' import buttonsCSS from '../styles/buttons.jsx'; -// Interactive button -import Interactive from 'react-interactive'; - String.prototype.capitalize = function() { return this.charAt(0).toUpperCase() + this.slice(1); @@ -175,7 +173,7 @@ class AdminRequestInfo extends Component {
{request.name} {request.year} - {this.generateTypeIcon(request.type)} + {this.generateTypeIcon(request.type)} {/*{request.type.capitalize()}
*/}
-- 2.34.1 From e64d88bfdf93c44b959833bf13103424d8d2a205 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:20:41 +0100 Subject: [PATCH 28/38] The stylesheet for all of requestInfo page. --- .../components/styles/adminRequestInfo.jsx | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/client/app/components/styles/adminRequestInfo.jsx b/client/app/components/styles/adminRequestInfo.jsx index 0d13af2..d3a525f 100644 --- a/client/app/components/styles/adminRequestInfo.jsx +++ b/client/app/components/styles/adminRequestInfo.jsx @@ -2,10 +2,57 @@ export default { wrapper: { width: '100%', }, - headerWrapper: { - width: '100%', - }, - poster: { - minHeight: '450px', - }, + stick: { + marginBottom: '1em', + }, + + title: { + fontSize: '2em', + }, + image: { + width: '105px', + borderRadius: '4px', + }, + + info: { + paddingTop: '1em', + paddingBottom: '0.5em', + marginRight: '2em', + backgroundColor: 'white', + border: '1px solid #d0d0d0', + borderRadius: '2px', + display: 'flex', + }, + + type_icon: { + marginLeft: '-1.9em', + marginRight: '0.7em', + }, + type_text: { + verticalAlign: 'super', + }, + + + info_poster: { + marginLeft: '2em', + flex: '0 1 10%' + }, + + info_request: { + flex: '0 1 auto' + }, + info_request_header: { + margin: '0', + marginBottom: '0.5em', + }, + + info_movie: { + maxWidth: '70%', + marginLeft: '1em', + flex: '0 1 auto', + }, + info_movie_header: { + margin: '0', + marginBottom: '0.5em', + } } \ No newline at end of file -- 2.34.1 From 262093d196f6bd8d9863411db615d6a3de8ec2d9 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:25:52 +0100 Subject: [PATCH 29/38] Completely re-wrote our sidebar. Now we can filter by search and category on top and a list of all elements are displayed below in a scrollable list. Each element has a indicator showing if the item is requested, downloading or downloaded. There are several generator functions that are to be pulled out in their seperate components. --- client/app/components/admin/Sidebar.jsx | 387 ++++++++++-------- client/app/components/styles/adminSidebar.jsx | 117 +++++- 2 files changed, 321 insertions(+), 183 deletions(-) diff --git a/client/app/components/admin/Sidebar.jsx b/client/app/components/admin/Sidebar.jsx index 7d48397..187e72c 100644 --- a/client/app/components/admin/Sidebar.jsx +++ b/client/app/components/admin/Sidebar.jsx @@ -7,207 +7,242 @@ import sidebarCSS from '../styles/adminSidebar.jsx' class SidebarComponent extends Component { - constructor(props){ - super(props) - // Constructor with states holding the search query and the element of reponse. - this.state = { - filterValue: '', - filterQuery: '', - requestItemsToBeDisplayed: [], - listItemSelected: '', - } - } + constructor(props){ + super(props) + // Constructor with states holding the search query and the element of reponse. + this.state = { + filterValue: '', + filterQuery: '', + requestItemsToBeDisplayed: [], + listItemSelected: '', + height: '0', + } - // Where we wait for api response to be delivered from parent through props - componentWillReceiveProps(props) { - this.state.listItemSelected = props.listItemSelected; - this.displayRequestedElementsInfo(props.requested_objects); - } + this.updateWindowDimensions = this.updateWindowDimensions.bind(this); + } - // Inputs a date and returns a text string that matches how long it was since - convertDateToDaysSince(date) { - var oneDay = 24*60*60*1000; - var firstDate = new Date(date); - var secondDate = new Date(); + // Where we wait for api response to be delivered from parent through props + componentWillReceiveProps(props) { + this.state.listItemSelected = props.listItemSelected; + this.displayRequestedElementsInfo(props.requested_objects); + } - var diffDays = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay)); - - switch (diffDays) { - case 0: - return 'Today'; - case 1: - return '1 day ago' - default: - return diffDays + ' days ago' - } - } + componentDidMount() { + this.updateWindowDimensions(); + window.addEventListener('resize', this.updateWindowDimensions); + } - // Called from our dropdown, receives a filter string and checks it with status field - // of our request objects. - filterItems(filterValue) { - let filteredRequestElements = this.props.requested_objects.map((item, index) => { - if (item.status === filterValue || filterValue === 'all') - return this.generateListElements(index, item); - }) + componentWillUnmount() { + window.removeEventListener('resize', this.updateWindowDimensions); + } - this.setState({ - requestItemsToBeDisplayed: filteredRequestElements, - filterValue: filterValue, - }) - } + updateWindowDimensions() { + this.setState({ height: window.innerHeight }); + } + + // Inputs a date and returns a text string that matches how long it was since + convertDateToDaysSince(date) { + var oneDay = 24*60*60*1000; + var firstDate = new Date(date); + var secondDate = new Date(); + + var diffDays = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay)); + + switch (diffDays) { + case 0: + return 'Today'; + case 1: + return '1 day ago' + default: + return diffDays + ' days ago' + } + } + + // Called from our dropdown, receives a filter string and checks it with status field + // of our request objects. + filterItems(filterValue) { + let filteredRequestElements = this.props.requested_objects.map((item, index) => { + if (item.status === filterValue || filterValue === 'all') + return this.generateListElements(index, item); + }) + + this.setState({ + requestItemsToBeDisplayed: filteredRequestElements, + filterValue: filterValue, + }) + } - // Updates the internal state of the query filter and updates the list to only - // display names matching the query. This is real-time filtering. - updateFilterQuery(event) { - const query = event.target.value; + // Updates the internal state of the query filter and updates the list to only + // display names matching the query. This is real-time filtering. + updateFilterQuery(event) { + const query = event.target.value; - let filteredByQuery = this.props.requested_objects.map((item, index) => { - if (item.name.toLowerCase().indexOf(query.toLowerCase()) != -1) - return this.generateListElements(index, item); - }) + let filteredByQuery = this.props.requested_objects.map((item, index) => { + if (item.name.toLowerCase().indexOf(query.toLowerCase()) != -1) + return this.generateListElements(index, item); + }) - this.setState({ - requestItemsToBeDisplayed: filteredByQuery, - filterQuery: query, - }); - } + console.log(filteredByQuery) + this.setState({ + requestItemsToBeDisplayed: filteredByQuery, + filterQuery: query, + }); + } - generateFilterDropdown() { - return ( - - ) - } + generateFilterSearch() { + return ( +
+
+ this.updateFilterQuery(event)} + value={this.state.filterQuery}/> + + + + + + + + +
+
+ ) + } - generateFilterSearchbar() { - return ( - this.updateFilterQuery(event)} - value={this.state.filterQuery}/> - ) - } + generateNav() { + let filterValue = this.state.filterValue; - // A colored bar indicating the status of a item by color. - generateRequestIndicator(status) { - let statusColor; + return ( + + ) + } + + generateBody(cards) { + let style = sidebarCSS.ulCard; + style.maxHeight = this.state.height - 160; + + return ( +
    + { cards } +
+ ) + } - generateListElements(index, item) { - if (index == this.state.listItemSelected) { - return ( -
-
- {item.name } -
- { this.convertDateToDaysSince(item.requested_date) } -
-
- Status: { item.status } -
- Matches found: 0 - { this.generateRequestIndicator(item.status) } -
- ) - } - else - return ( - - + generateListElements(index, item) { + let statusBar; - {item.name } -
- { this.convertDateToDaysSince(item.requested_date) } -
-
- { this.generateRequestIndicator(item.status) } -
- - ) - } + switch (item.status) { + case 'requested': + // Yellow + statusBar = { background: 'linear-gradient(to right, rgb(63, 195, 243) 0, rgb(63, 195, 243) 4px, #fff 4px, #fff 100%) no-repeat' } + break; + case 'downloading': + // Blue + statusBar = { background: 'linear-gradient(to right, rgb(255, 225, 77) 0, rgb(255, 225, 77) 4px, #fff 4px, #fff 100%) no-repeat' } + break; + case 'downloaded': + // Green + statusBar = { background: 'linear-gradient(to right, #39aa56 0, #39aa56 4px, #fff 4px, #fff 100%) no-repeat' } + break; + default: + statusBar = { background: 'linear-gradient(to right, grey 0, grey 4px, #fff 4px, #fff 100%) no-repeat' } + } - // This is our main loader that gets called when we receive api response through props from parent - displayRequestedElementsInfo(requested_objects) { - let requestedElement = requested_objects.map((item, index) => { - if (['requested', 'downloading', 'downloaded'].indexOf(this.state.filterValue) != -1) { - if (item.status === this.state.filterValue){ - return this.generateListElements(index, item); - } - } + statusBar.listStyleType = 'none'; - else if (this.state.filterQuery !== '') { - if (item.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) != -1) - return this.generateListElements(index, item); - } + return ( + +
  • + - else - return this.generateListElements(index, item); - }) +

    + { item.name } +

    - this.setState({ - requestItemsToBeDisplayed: requestedElement, - }) - } +

    + Requested: + + +

    +
    +
  • + + ) + } - render() { - let bodyCSS = sidebarCSS.body; - if (typeof InstallTrigger !== 'undefined') - bodyCSS.width = '-moz-min-content'; + // This is our main loader that gets called when we receive api response through props from parent + displayRequestedElementsInfo(requested_objects) { + let requestedElement = requested_objects.map((item, index) => { + if (['requested', 'downloading', 'downloaded'].indexOf(this.state.filterValue) != -1) { + if (item.status === this.state.filterValue){ + return this.generateListElements(index, item); + } + } + else if (this.state.filterQuery !== '') { + if (item.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) != -1) + return this.generateListElements(index, item); + } + else + return this.generateListElements(index, item); + }) - return ( -
    -

    Hello from the sidebar:

    - { this.generateFilterDropdown() } - { this.generateFilterSearchbar() } -
    - { this.state.requestItemsToBeDisplayed } -
    -
    - ); - } + this.setState({ + requestItemsToBeDisplayed: this.generateBody(requestedElement) + }) + } + + render() { + // if (typeof InstallTrigger !== 'undefined') + // bodyCSS.width = '-moz-min-content'; + + return ( +
    +

    Requested items

    + { this.generateFilterSearch() } + { this.generateNav() } + +
    + { this.state.requestItemsToBeDisplayed } +
    +
    + ); + } } export default SidebarComponent; \ No newline at end of file diff --git a/client/app/components/styles/adminSidebar.jsx b/client/app/components/styles/adminSidebar.jsx index c10fe49..16f2371 100644 --- a/client/app/components/styles/adminSidebar.jsx +++ b/client/app/components/styles/adminSidebar.jsx @@ -1,13 +1,15 @@ export default { + header: { + textAlign: 'center', + }, body: { backgroundColor: 'white', - width: 'min-content', }, parentElement: { display: 'inline-block', - width: '300px', - border: '1px solid black', + width: '100%', + border: '1px solid grey', borderRadius: '2px', padding: '4px', margin: '4px', @@ -16,7 +18,8 @@ export default { }, parentElement_hover: { - marginLeft: '10px', + backgroundColor: '#f8f8f8', + pointer: 'hand', }, parentElement_active: { @@ -25,8 +28,8 @@ export default { parentElement_selected: { display: 'inline-block', - width: '300px', - border: '1px solid black', + width: '100%', + border: '1px solid grey', borderRadius: '2px', padding: '4px', margin: '4px 0px 4px 4px', @@ -35,7 +38,7 @@ export default { }, title: { - maxWidth: '70%', + maxWidth: '65%', display: 'inline-flex', }, @@ -47,4 +50,104 @@ export default { rightContainer: { float: 'right', }, + + + + searchSidebar: { + height: '4em', + }, + searchInner: { + top: '0', + right: '0', + left: '0', + bottom: '0', + margin: 'auto', + width: '90%', + minWidth: '280px', + height: '30px', + border: '1px solid #d0d0d0', + borderRadius: '4px', + overflow: 'hidden' + }, + searchTextField: { + display: 'inline-block', + width: '90%', + padding: '.3em', + verticalAlign: 'middle', + border: 'none', + background: '#fff', + fontSize: '14px', + marginTop: '-7px', + }, + searchIcon: { + width: '15px', + height: '16px', + marginRight: '4px', + marginTop: '7px', + }, + searchSVGIcon: { + fill: 'none', + stroke: '#9d9d9d', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeMiterlimit: '10', + }, + + + ulFilterSelectors: { + borderBottom: '2px solid #f1f1f1', + display: 'flex', + padding: '0', + margin: '0', + listStyle: 'none', + justifyContent: 'space-evenly', + }, + aFilterSelectors: { + color: '#3eaaaf', + fontSize: '16px', + cursor: 'pointer', + }, + spanFilterSelectors: { + content: '""', + bottom: '-2px', + display: 'block', + width: '100%', + height: '2px', + backgroundColor: '#3eaaaa', + }, + + + ulCard: { + margin: '1em 0 0 0', + padding: '0', + listStyle: 'none', + borderBottom: '.46rem solid #f1f1f', + backgroundColor: '#f1f1f1', + overflow: 'scroll', + }, + + + card: { + padding: '.1em .5em .8em 1.5em', + marginBottom: '.26rem', + height: 'auto', + cursor: 'pointer', + }, + cardSelected: { + padding: '.1em .5em .8em 1.5em', + marginBottom: '.26rem', + height: 'auto', + cursor: 'pointer', + + backgroundColor: '#f9f9f9', + }, + titleCard: { + fontSize: '15px', + fontWeight: '400', + whiteSpace: 'no-wrap', + textDecoration: 'none', + }, + pCard: { + margin: '0', + }, } \ No newline at end of file -- 2.34.1 From c8a2ea9907ea21a4a901b105bb67e7d4140d3188 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:28:34 +0100 Subject: [PATCH 30/38] This now contains the button and request function for getting torrents matching a serach query given by the name of the elment. The result is passed to the child component torrentTable which renders the result in a table view. --- client/app/components/admin/PirateSearch.jsx | 130 ++++++++++--------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/client/app/components/admin/PirateSearch.jsx b/client/app/components/admin/PirateSearch.jsx index 25917fa..fcca252 100644 --- a/client/app/components/admin/PirateSearch.jsx +++ b/client/app/components/admin/PirateSearch.jsx @@ -1,6 +1,9 @@ import React, { Component } from 'react'; import { fetchJSON } from '../http.jsx'; +// Components +import TorrentTable from './torrentTable.jsx' + // Stylesheets import btnStylesheet from '../styles/buttons.jsx'; @@ -10,72 +13,77 @@ import Interactive from 'react-interactive'; import Loading from '../images/loading.jsx' class PirateSearch extends Component { - constructor() { - super(); - this.state = { - response: [], - name: '', - loading: '', - } - } + constructor() { + super(); + this.state = { + torrentResponse: undefined, + name: '', + loading: null, + showButton: true, + } + } - sendToDownload(torrent) { - let data = {magnet: torrent.magnet} - fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/add', 'POST', data) - .then((response) => { - console.log(response) - }) - } + componentWillReceiveProps(props) { + if (props.name != this.state.name) { + this.setState({ + torrentResponse: undefined, + showButton: true, + }) + } + } - searchTheBay() { - const query = this.props.name; - const type = this.props.type; + searchTheBay() { + const query = this.props.name; + const type = this.props.type; - this.setState({ - loading: - }) + this.setState({ + showButton: false, + loading: , + }) - fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/search?query='+query+'&type='+type, 'GET') - .then((response) => { - console.log(response.torrents) - this.setState({ - loading: '', - response: response.torrents.map((torrent, index) => { - return ( -
    - {torrent.name}
    - {torrent.size}
    - {torrent.seed}
    - -

    -
    - ) - }) - }) - }) - } + fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/search?query='+query+'&type='+type, 'GET') + // fetchJSON('http://localhost:31459/api/v1/pirate/search?query='+query+'&type='+type, 'GET') + .then((response) => { + this.setState({ + torrentResponse: response.torrents, + loading: null, + }) + }) + .catch((error) => { + console.error(error); + this.setState({ + showButton: true, + }) + }) + } - render() { - return ( -
    -
    - {this.searchTheBay()}} - style={btnStylesheet.submit} - focus={btnStylesheet.submit_hover} - hover={btnStylesheet.submit_hover}> - - Search for torrents - -
    + render() { + btnStylesheet.submit.top = '50%' + btnStylesheet.submit.position = 'absolute' + btnStylesheet.submit.marginLeft = '-75px' - { this.state.loading } - - {this.state.response} -
    - ) - } + return ( +
    + { this.state.showButton ? +
    + {this.searchTheBay()}} + style={btnStylesheet.submit} + focus={btnStylesheet.submit_hover} + hover={btnStylesheet.submit_hover}> + + Search for torrents + +
    + : null } + + { this.state.loading } + + +
    + ) + } } -export default PirateSearch \ No newline at end of file +export default PirateSearch -- 2.34.1 From da54ad006672f788b3e6ad8186d1d29f1b13ea59 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:30:38 +0100 Subject: [PATCH 31/38] Changed import name of torrentTable to correct TorrentTable --- client/app/components/admin/PirateSearch.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/components/admin/PirateSearch.jsx b/client/app/components/admin/PirateSearch.jsx index fcca252..c60e7c2 100644 --- a/client/app/components/admin/PirateSearch.jsx +++ b/client/app/components/admin/PirateSearch.jsx @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { fetchJSON } from '../http.jsx'; // Components -import TorrentTable from './torrentTable.jsx' +import TorrentTable from './TorrentTable.jsx' // Stylesheets import btnStylesheet from '../styles/buttons.jsx'; -- 2.34.1 From 10ef1bfa697c68ae3ba78c49897b39aa119cc9b8 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:33:46 +0100 Subject: [PATCH 32/38] This receives a response of list of torrents as props and displays them in a table view. From here we can send to download and also filter the results by query. --- .../components/styles/adminTorrentTable.jsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 client/app/components/styles/adminTorrentTable.jsx diff --git a/client/app/components/styles/adminTorrentTable.jsx b/client/app/components/styles/adminTorrentTable.jsx new file mode 100644 index 0000000..f6098bd --- /dev/null +++ b/client/app/components/styles/adminTorrentTable.jsx @@ -0,0 +1,48 @@ +export default { + table: { + width: '80%', + marginRight: 'auto', + marginLeft: 'auto', + }, + + searchSidebar: { + height: '4em', + marginTop: '1em', + }, + searchInner: { + top: '0', + right: '0', + left: '0', + bottom: '0', + margin: 'auto', + width: '50%', + minWidth: '280px', + height: '30px', + border: '1px solid #d0d0d0', + borderRadius: '4px', + overflow: 'hidden' + }, + searchTextField: { + display: 'inline-block', + width: '95%', + padding: '.3em', + verticalAlign: 'middle', + border: 'none', + background: '#fff', + fontSize: '14px', + marginTop: '-7px', + }, + searchIcon: { + width: '15px', + height: '16px', + marginRight: '4px', + marginTop: '7px', + }, + searchSVGIcon: { + fill: 'none', + stroke: '#9d9d9d', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeMiterlimit: '10', + }, +} \ No newline at end of file -- 2.34.1 From 519eba8fda175042846ad7cc216cfdd763e6474e Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:36:24 +0100 Subject: [PATCH 33/38] Added some css for admin component that defines the styles for parent elements of imported components. --- client/app/components/admin/torrentTable.jsx | 85 ------------------- .../app/components/styles/adminComponent.jsx | 12 ++- 2 files changed, 10 insertions(+), 87 deletions(-) delete mode 100644 client/app/components/admin/torrentTable.jsx diff --git a/client/app/components/admin/torrentTable.jsx b/client/app/components/admin/torrentTable.jsx deleted file mode 100644 index 94d0f43..0000000 --- a/client/app/components/admin/torrentTable.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { Component } from 'react'; - -import { fetchJSON } from '../http.jsx'; - -class TorrentTable extends Component { - - constructor() { - super(); - this.state = { - torrentResponse: '', - showResults: false, - } - } - - componentWillReceiveProps(props) { - if (props.response !== undefined) { - this.setState({ - torrentResponse: props.response.map((torrent, index) => { - return ( - - {torrent.name} - {torrent.size} - {torrent.seed} - - - ) - }), - showResults: true, - }) - } - else { - this.setState({ - showResults: false, - }) - } - } - - sendToDownload(torrent) { - let data = {magnet: torrent.magnet} - fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/add', 'POST', data) - .then((response) => { - // TODO: Show a card with response that the item has been sent, and the status of response. - console.log(response) - }) - .catch((error) => { - console.error(error); - }) - } - - generateTable() { - let style = { - table: { - width: '80%', - marginRight: 'auto', - marginLeft: 'auto', - }, - } - - return ( - - - - - - - - - - - { this.state.torrentResponse } - -
    NameSizeSeedAdd
    - ); - } - - render() { - return ( -
    - { this.state.showResults ? this.generateTable() : null } -
    - ) - } -} - -export default TorrentTable \ No newline at end of file diff --git a/client/app/components/styles/adminComponent.jsx b/client/app/components/styles/adminComponent.jsx index 9e131aa..721a740 100644 --- a/client/app/components/styles/adminComponent.jsx +++ b/client/app/components/styles/adminComponent.jsx @@ -1,8 +1,16 @@ export default { sidebar: { - float: 'left', + float: 'left', + width: '18%', + minWidth: '250px', + fontFamily: '"Open Sans", sans-serif', + fontSize: '14px', + borderRight: '2px solid #f2f2f2', }, selectedObjectPanel: { - float: 'left', + width: '80%', + float: 'right', + fontFamily: '"Open Sans", sans-serif', + marginTop: '1em', } } \ No newline at end of file -- 2.34.1 From 21a94d88a9f69b95ebaf72fcc5b7536a0db1b932 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:39:16 +0100 Subject: [PATCH 34/38] Added a seperate component for the info button. --- client/app/components/buttons/InfoButton.jsx | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 client/app/components/buttons/InfoButton.jsx diff --git a/client/app/components/buttons/InfoButton.jsx b/client/app/components/buttons/InfoButton.jsx new file mode 100644 index 0000000..fa8f4f4 --- /dev/null +++ b/client/app/components/buttons/InfoButton.jsx @@ -0,0 +1,52 @@ +import React, { Component } from 'react'; +import Interactive from 'react-interactive'; + +import buttonsCSS from '../styles/buttons.jsx'; + + +class InfoButton extends Component { + constructor(props) { + super(props); + + if (props) { + this.state = { + id: props.id, + type: props.type, + } + } + } + + componentWillReceiveProps(props) { + this.setState({ + id: props.id, + type: props.type, + }) + } + + getTMDBLink() { + const id = this.state.id; + const type = this.state.type; + + if (type === 'movie') + return 'https://www.themoviedb.org/movie/' + id + else if (type === 'show') + return 'https://www.themoviedb.org/tv/' + id + } + + render() { + return ( + + + + More info + + + ); + } +} + +export default InfoButton; \ No newline at end of file -- 2.34.1 From b8fb71854060e321d6547cc8c27ecdb9d73a4589 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:40:49 +0100 Subject: [PATCH 35/38] Added NODE_ENV environmental variable production when building. --- client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/package.json b/client/package.json index f166ff8..3529ce3 100644 --- a/client/package.json +++ b/client/package.json @@ -7,7 +7,7 @@ "license": "MIT", "scripts": { "start": "webpack-dev-server --open --config webpack.dev.js", - "build": "webpack --config webpack.prod.js", + "build": "NODE_ENV=production webpack --config webpack.prod.js", "build_dev": "webpack --config webpack.dev.js" }, "dependencies": { -- 2.34.1 From e9f6f3a656b03ae557ab90a516c964f458efa0ec Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:44:10 +0100 Subject: [PATCH 36/38] We added a new python app for fetching torrents. The api is now changed to use the new project. IMPORTANT that a check for if jackett is set up is done, if else this is will not run correctly. --- seasoned_api/src/pirate/pirateRepository.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/seasoned_api/src/pirate/pirateRepository.js b/seasoned_api/src/pirate/pirateRepository.js index 97bb77c..ab4ca3c 100644 --- a/seasoned_api/src/pirate/pirateRepository.js +++ b/seasoned_api/src/pirate/pirateRepository.js @@ -6,12 +6,12 @@ var PythonShell = require('python-shell'); async function find(searchterm, callback) { var options = { - pythonPath: '/usr/bin/python3', - // pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3', - args: [searchterm] + // pythonPath: '/usr/bin/python3', + pythonPath: '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3', + args: [searchterm, '-s', 'jackett', '--print'] } - PythonShell.run('../app/pirateSearch.py', options, callback); + PythonShell.run('../app/torrent_search/torrentSearch/search.py', options, callback); // PythonShell does not support return }; @@ -29,6 +29,7 @@ async function callPythonAddMagnet(magnet, callback) { async function SearchPiratebay(query) { return await new Promise((resolve) => { return find(query, function(err, results) { + console.log('err', err, '. result', results); resolve(JSON.parse(results, null, '\t')); }) }) -- 2.34.1 From 65bce27a2b21629a09106a7befa566a7378a2295 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:49:24 +0100 Subject: [PATCH 37/38] Added our torrent_search repo as a submodule in our app folder. This will need to be set up automatically with build setting when the project is to be downloaded. --- app/torrent_search | 1 + 1 file changed, 1 insertion(+) create mode 160000 app/torrent_search diff --git a/app/torrent_search b/app/torrent_search new file mode 160000 index 0000000..3deaed4 --- /dev/null +++ b/app/torrent_search @@ -0,0 +1 @@ +Subproject commit 3deaed48b71adf97172e7bf899af87972cc7f4e2 -- 2.34.1 From e0da166406fe72e75fa69af6fe456d22953a1f2a Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 9 Jan 2018 16:53:41 +0100 Subject: [PATCH 38/38] Our request page is redesigned with a new header and search bar. It also has a better way of redrawing when the page size is changed to a mobile device. This is not very reactonic, but this file contains the intire search and request logic of the page, but this means we still have things todo :) --- client/app/components/SearchRequest.jsx | 43 ++++++--- .../components/styles/searchRequestStyle.jsx | 87 ++++++++++++++----- 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index 39cdce2..bdb5098 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -47,7 +47,7 @@ class SearchRequest extends React.Component { // this.setState({responseMovieList: null}) this.resetPageNumber(); this.state.loadResults = true; - this.fetchTmdbList('upcoming'); + this.fetchTmdbList('discover'); } // Handles all errors of the response of a fetch call @@ -384,25 +384,28 @@ class SearchRequest extends React.Component {
    - Request new content + Request new content for plex
    -
    -
    - +
    + - this._handleQueryKeyPress(event)} - onChange={event => this.updateQueryState(event)} - value={this.state.searchQuery}/> + this._handleQueryKeyPress(event)} + onChange={event => this.updateQueryState(event)} + value={this.state.searchQuery}/> -
    - {this.state.resultHeader} -



    +
    +
    {this.state.resultHeader}
    + + +
    + +

    {this.state.responseMovieList}
    @@ -441,7 +444,21 @@ class SearchRequest extends React.Component { ) } - + //
    + // + //
    + + //
    + // + //
    } export default SearchRequest; diff --git a/client/app/components/styles/searchRequestStyle.jsx b/client/app/components/styles/searchRequestStyle.jsx index 17b5b11..0700a4e 100644 --- a/client/app/components/styles/searchRequestStyle.jsx +++ b/client/app/components/styles/searchRequestStyle.jsx @@ -10,19 +10,19 @@ export default { backgroundLargeHeader: { width: '100%', - minHeight: '400px', + minHeight: '180px', backgroundColor: 'rgb(1, 28, 35)', // backgroundImage: 'radial-gradient(circle, #004c67 0, #005771 120%)', zIndex: 1, - marginBottom: '-100px' + marginBottom: '80px' }, backgroundSmallHeader: { width: '100%', - minHeight: '300px', + minHeight: '120px', backgroundColor: '#011c23', zIndex: 1, - marginBottom: '-100px' + marginBottom: '40px' }, requestWrapper: { @@ -44,7 +44,7 @@ export default { pageTitleLargeSpan: { color: 'white', - fontSize: '4em', + fontSize: '3em', marginTop: '4vh', marginBottom: '6vh' }, @@ -54,39 +54,35 @@ export default { fontSize: '2em', marginTop: '3vh', marginBottom: '3vh' - }, - - box: { - height: '50px', }, searchLargeContainer: { - margin: '0 25%', + height: '52px', + width: '77%', + paddingLeft: '23%', + backgroundColor: 'white', }, searchSmallContainer: { - margin: '0 10%', }, searchIcon: { position: 'absolute', - fontSize: '1.2em', - marginTop: '12px', - marginLeft: '-13px', - color: '#4f5b66' + fontSize: '1.6em', + marginTop: '7px', + color: '#4f5b66', + display: 'block', }, searchLargeBar: { - width: '100%', + width: '50%', height: '50px', background: '#ffffff', border: 'none', - fontSize: '10pt', + fontSize: '12pt', float: 'left', color: '#63717f', - paddingLeft: '45px', - marginLeft: '-25px', - borderRadius: '5px', + paddingLeft: '40px', }, searchSmallBar: { @@ -94,14 +90,58 @@ export default { height: '50px', background: '#ffffff', border: 'none', - fontSize: '13pt', + fontSize: '11pt', float: 'left', color: '#63717f', - paddingLeft: '45px', + paddingLeft: '65px', marginLeft: '-25px', borderRadius: '5px', }, + + // Dropdown for selecting tmdb lists + controls: { + textAlign: 'left', + paddingTop: '8px', + width: '33.3333%', + marginLeft: '0', + marginRight: '0', + }, + + withData: { + boxSizing: 'border-box', + marginBottom: '0', + display: 'block', + padding: '0', + verticalAlign: 'baseline', + font: 'inherit', + textAlign: 'left', + boxSizing: 'border-box', + }, + + sortOptions: { + border: '1px solid #000', + maxWidth: '100%', + overflow: 'hidden', + lineHeight: 'normal', + textAlign: 'left', + padding: '4px 12px', + paddingRight: '2rem', + backgroundImage: 'url("data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOCAxOCI+CiAgPHRpdGxlPmFycm93LWRvd24tbWljcm88L3RpdGxlPgogIDxwb2x5bGluZSBwb2ludHM9IjE0IDQuNjcgOSAxMy4zMyA0IDQuNjciIHN0eWxlPSJmaWxsOiBub25lO3N0cm9rZTogIzAwMDtzdHJva2UtbWl0ZXJsaW1pdDogMTA7c3Ryb2tlLXdpZHRoOiAycHgiLz4KPC9zdmc+Cg==")', + backgroundSize: '18px 18px', + backgroundPosition: 'right 8px center', + backgroundRepeat: 'no-repeat', + width: 'auto', + display: 'inline-block', + outline: 'none', + boxSizing: 'border-box', + fontSize: '15px', + WebkitAppearance: 'none', + MozAppearance: 'none', + appearance: 'none', + }, + + searchFilterActive: { color: '#00d17c', fontSize: '1em', @@ -116,7 +156,6 @@ export default { cursor: 'pointer' }, - filter: { color: 'white', paddingLeft: '40px', @@ -124,9 +163,9 @@ export default { }, resultLargeHeader: { - paddingLeft: '30px', color: 'black', fontSize: '1.6em', + width: '20%', }, resultSmallHeader: { -- 2.34.1