From e33e7d8dc787e6482440f3db55a27cae2ca68c6b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Sep 2017 23:15:30 +0200 Subject: [PATCH 01/23] Removed unsued imports. --- client/app/components/MovieObject.jsx | 6 +-- client/app/components/SearchRequest.jsx | 63 +++++++++++++------------ 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/client/app/components/MovieObject.jsx b/client/app/components/MovieObject.jsx index e2c87aa..e1be5d9 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/MovieObject.jsx @@ -1,8 +1,6 @@ import React from 'react'; -import glamorous from 'glamorous'; // StyleComponents -import mediaResultItem from './styledComponents/mediaResultItem.jsx'; class MovieObject { constructor(object) { @@ -12,7 +10,7 @@ class MovieObject { // Check if object.poster != undefined this.poster = object.poster; this.matchedInPlex = object.matchedInPlex; - this.overview = object.overview; + this.summary = object.summary; } requestExisting(movie) { @@ -124,7 +122,7 @@ class MovieObject {
{this.title} ({this.year})

- {this.overview} + {this.summary}

diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index 32c3be9..588b53f 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -5,34 +5,35 @@ import MovieObject from './MovieObject.jsx'; // TODO add option for searching multi, movies or tv shows class SearchRequest extends React.Component { - constructor(props){ + constructor(props){ super(props) // Constructor with states holding the search query and the element of reponse. this.state = { searchQuery: '', responseMovieList: null, movieFilter: true, - tvshowFilter: false + tvshowFilter: false, + page: 1 } this.URLs = { - request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=', - sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=' + request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?page='+this.props.page+'&query=', + sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=' } } componentDidMount(){ - var that = this; - this.setState({responseMovieList: null}) + var that = this; + this.setState({responseMovieList: null}) } // Handles all errors of the response of a fetch call handleErrors(response) { - if (!response.ok) { - throw Error(response.status); - } - return response; + if (!response.ok) { + throw Error(response.status); + } + return response; } fetchQuery() { @@ -42,27 +43,27 @@ class SearchRequest extends React.Component { } fetch(url) - // Check if the response is ok - .then(response => this.handleErrors(response)) - .then(response => response.json()) // Convert to json object and pass to next then - .then(data => { // Parse the data of the JSON response - // If it is something here it updates the state variable with the HTML list of all - // movie objects that where returned by the search request - if (data.length > 0) { - this.setState({ - responseMovieList: data.map(item => this.createMovieObjects(item)) - }) - } - }) - // If the -------- - .catch(error => { - console.log(error) - this.setState({ - responseMovieList:

Not Found

- }) + // Check if the response is ok + .then(response => this.handleErrors(response)) + .then(response => response.json()) // Convert to json object and pass to next then + .then(data => { // Parse the data of the JSON response + // If it is something here it updates the state variable with the HTML list of all + // movie objects that where returned by the search request + if (data.length > 0) { + this.setState({ + responseMovieList: data.map(item => this.createMovieObjects(item)) + }) + } + }) + // If the -------- + .catch(error => { + console.log(error) + this.setState({ + responseMovieList:

Not Found

+ }) - console.log('Error submit: ', error.toString()); - }); + console.log('Error submit: ', error.toString()); + }); } // Updates the internal state of the query search field. @@ -272,7 +273,7 @@ class SearchRequest extends React.Component { ) } - + } export default SearchRequest; \ No newline at end of file From 3e12cf10fbd8733157f800102b1680efd365b3de Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 16:59:52 +0200 Subject: [PATCH 02/23] Updated tmdb to use the new endpoints --- 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 52fe956..94cd8bd 100644 --- a/seasoned_api/package.json +++ b/seasoned_api/package.json @@ -9,7 +9,7 @@ "cross-env": "^3.1.3", "express": "~4.0.0", "mongoose": "^3.6.13", - "moviedb": "^0.2.7", + "moviedb": "^0.2.10", "node-cache": "^4.1.1", "nodemailer": "^4.0.1", "python-shell": "^0.4.0", From 2dc22b386d60aa17d1cc03f14e1f2f52d733085b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:02:02 +0200 Subject: [PATCH 03/23] Added endpoints for discover media (either movie or show), getUpcoming movies, nowPlaying (movies or shows), popular (movie or show) and search similar where the type movie/show must be defined --- seasoned_api/src/webserver/app.js | 6 +++++ .../controllers/tmdb/discoverMedia.js | 21 ++++++++++++++++++ .../webserver/controllers/tmdb/getUpcoming.js | 21 ++++++++++++++++++ .../controllers/tmdb/nowPlayingMedia.js | 21 ++++++++++++++++++ .../controllers/tmdb/popularMedia.js | 21 ++++++++++++++++++ .../controllers/tmdb/searchSimilar.js | 22 +++++++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 seasoned_api/src/webserver/controllers/tmdb/discoverMedia.js create mode 100644 seasoned_api/src/webserver/controllers/tmdb/getUpcoming.js create mode 100644 seasoned_api/src/webserver/controllers/tmdb/nowPlayingMedia.js create mode 100644 seasoned_api/src/webserver/controllers/tmdb/popularMedia.js create mode 100644 seasoned_api/src/webserver/controllers/tmdb/searchSimilar.js diff --git a/seasoned_api/src/webserver/app.js b/seasoned_api/src/webserver/app.js index 63dde87..5d56fab 100644 --- a/seasoned_api/src/webserver/app.js +++ b/seasoned_api/src/webserver/app.js @@ -40,6 +40,12 @@ router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitReque router.get('/v1/plex/hook', require('./controllers/plex/hookDump.js')); router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.js')); +router.get('/v1/tmdb/discover', require('./controllers/tmdb/discoverMedia.js')); +router.get('/v1/tmdb/popular', require('./controllers/tmdb/popularMedia.js')); +router.get('/v1/tmdb/nowplaying', require('./controllers/tmdb/nowPlayingMedia.js')); +router.get('/v1/tmdb/upcoming', require('./controllers/tmdb/getUpcoming.js')); + +router.get('/v1/tmdb/similar/:mediaId', require('./controllers/tmdb/searchSimilar.js')); router.get('/v1/tmdb/:mediaId', require('./controllers/tmdb/readMedia.js')); router.post('/v1/git/dump', require('./controllers/git/dumpHook.js')); diff --git a/seasoned_api/src/webserver/controllers/tmdb/discoverMedia.js b/seasoned_api/src/webserver/controllers/tmdb/discoverMedia.js new file mode 100644 index 0000000..3669035 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/tmdb/discoverMedia.js @@ -0,0 +1,21 @@ +const configuration = require('src/config/configuration').getInstance(); +const TMDB = require('src/tmdb/tmdb'); +const tmdb = new TMDB(configuration.get('tmdb', 'apiKey')); + +/** + * Controller: Retrieve a list of movies or shows in discover section in TMDB + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +function discoverMediaController(req, res) { + const { page, type } = req.query; + tmdb.discover(page, type) + .then((results) => { + res.send(results); + }).catch((error) => { + res.status(404).send({ success: false, error: error.message }); + }); +} + +module.exports = discoverMediaController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/getUpcoming.js b/seasoned_api/src/webserver/controllers/tmdb/getUpcoming.js new file mode 100644 index 0000000..9c9e049 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/tmdb/getUpcoming.js @@ -0,0 +1,21 @@ +const configuration = require('src/config/configuration').getInstance(); +const TMDB = require('src/tmdb/tmdb'); +const tmdb = new TMDB(configuration.get('tmdb', 'apiKey')); + +/** + * Controller: Retrieve upcoming movies + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +function getUpcomingController(req, res) { + const { page } = req.query; + tmdb.upcoming(page) + .then((results) => { + res.send(results); + }).catch((error) => { + res.status(404).send({ success: false, error: error.message }); + }); +} + +module.exports = getUpcomingController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/nowPlayingMedia.js b/seasoned_api/src/webserver/controllers/tmdb/nowPlayingMedia.js new file mode 100644 index 0000000..5e67db8 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/tmdb/nowPlayingMedia.js @@ -0,0 +1,21 @@ +const configuration = require('src/config/configuration').getInstance(); +const TMDB = require('src/tmdb/tmdb'); +const tmdb = new TMDB(configuration.get('tmdb', 'apiKey')); + +/** + * Controller: Retrieve nowplaying movies / now airing shows + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +function nowPlayingMediaController(req, res) { + const { page, type } = req.query; + tmdb.nowplaying(page, type) + .then((results) => { + res.send(results); + }).catch((error) => { + res.status(404).send({ success: false, error: error.message }); + }); +} + +module.exports = nowPlayingMediaController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/popularMedia.js b/seasoned_api/src/webserver/controllers/tmdb/popularMedia.js new file mode 100644 index 0000000..963d553 --- /dev/null +++ b/seasoned_api/src/webserver/controllers/tmdb/popularMedia.js @@ -0,0 +1,21 @@ +const configuration = require('src/config/configuration').getInstance(); +const TMDB = require('src/tmdb/tmdb'); +const tmdb = new TMDB(configuration.get('tmdb', 'apiKey')); + +/** + * Controller: Retrieve information for a movie + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +function popularMediaController(req, res) { + const { page, type } = req.query; + tmdb.popular(page, type) + .then((results) => { + res.send(results); + }).catch((error) => { + res.status(404).send({ success: false, error: error.message }); + }); +} + +module.exports = popularMediaController; diff --git a/seasoned_api/src/webserver/controllers/tmdb/searchSimilar.js b/seasoned_api/src/webserver/controllers/tmdb/searchSimilar.js new file mode 100644 index 0000000..bc2383f --- /dev/null +++ b/seasoned_api/src/webserver/controllers/tmdb/searchSimilar.js @@ -0,0 +1,22 @@ +const configuration = require('src/config/configuration').getInstance(); +const TMDB = require('src/tmdb/tmdb'); +const tmdb = new TMDB(configuration.get('tmdb', 'apiKey')); + +/** + * Controller: Retrieve similar movies or shows + * @param {Request} req http request variable + * @param {Response} res + * @returns {Callback} + */ +function similarMediaController(req, res) { + const mediaId = req.params.mediaId; + const { type } = req.query; + tmdb.similar(mediaId, type) + .then((results) => { + res.send(results); + }).catch((error) => { + res.status(404).send({ success: false, error: error.message }); + }); +} + +module.exports = similarMediaController; From 789ed77ab6447be9a59e180264af66aea66699d0 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:03:31 +0200 Subject: [PATCH 04/23] Now the type of object being passed can be set by strictType variable. The type is then set before creating a movie or show object item. --- seasoned_api/src/tmdb/convertTmdbToSeasoned.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/seasoned_api/src/tmdb/convertTmdbToSeasoned.js b/seasoned_api/src/tmdb/convertTmdbToSeasoned.js index 642b7ea..7379610 100644 --- a/seasoned_api/src/tmdb/convertTmdbToSeasoned.js +++ b/seasoned_api/src/tmdb/convertTmdbToSeasoned.js @@ -1,8 +1,8 @@ const Movie = require('src/media_classes/movie'); const Show = require('src/media_classes/show'); -function convertTmdbToSeasoned(tmdbObject) { - const mediaType = tmdbObject.media_type; +function convertTmdbToSeasoned(tmdbObject, strictType=undefined) { + const mediaType = strictType || tmdbObject.media_type; // There are many diff types of content, we only want to look at movies and tv shows if (mediaType === 'movie') { @@ -16,6 +16,7 @@ function convertTmdbToSeasoned(tmdbObject) { const movie = new Movie(title, year, mediaType); + movie.id = tmdbObject.id; movie.summary = tmdbObject.overview; movie.rating = tmdbObject.vote_average; movie.poster = tmdbObject.poster_path; @@ -27,11 +28,12 @@ function convertTmdbToSeasoned(tmdbObject) { return movie; } - else if (mediaType === 'tv') { + else if (mediaType === 'tv' || mediaType === 'show') { const year = new Date(tmdbObject.first_air_date).getFullYear(); - const show = new Show(tmdbObject.title, year, mediaType); + const show = new Show(tmdbObject.name, year, 'show'); + show.id = tmdbObject.id; show.summary = tmdbObject.overview; show.rating = tmdbObject.vote_average; show.poster = tmdbObject.poster_path; From e81f5c80575148e093546b60c78c4db5313d5d30 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:04:59 +0200 Subject: [PATCH 05/23] Added functions for discover, upcoming, nowplaying, popular and similar. Also documented better and some finer error handling with more logical responses. --- seasoned_api/src/tmdb/tmdb.js | 242 +++++++++++++++++++++++++++++++--- 1 file changed, 227 insertions(+), 15 deletions(-) diff --git a/seasoned_api/src/tmdb/tmdb.js b/seasoned_api/src/tmdb/tmdb.js index a536381..c9481b7 100644 --- a/seasoned_api/src/tmdb/tmdb.js +++ b/seasoned_api/src/tmdb/tmdb.js @@ -1,7 +1,10 @@ const moviedb = require('moviedb'); const convertTmdbToSeasoned = require('src/tmdb/convertTmdbToSeasoned'); -var methodTypes = { 'movie': 'searchMovie', 'tv': 'searchTv', 'multi': 'searchMulti', 'movieInfo': 'movieInfo', - 'tvInfo': 'tvInfo' }; +var methodTypes = { 'movie': 'searchMovie', 'show': 'searchTv', 'multi': 'searchMulti', 'movieInfo': 'movieInfo', + 'tvInfo': 'tvInfo', 'upcomingMovies': 'miscUpcomingMovies', 'discoverMovie': 'discoverMovie', + 'discoverShow': 'discoverTv', 'popularMovies': 'miscPopularMovies', 'popularShows': 'miscPopularTvs', + 'nowPlayingMovies': 'miscNowPlayingMovies', 'nowAiringShows': 'tvOnTheAir', 'movieSimilar': 'movieSimilar', + 'showSimilar': 'tvSimilar' }; class TMDB { constructor(apiKey, tmdbLibrary) { @@ -9,7 +12,7 @@ class TMDB { } search(text, page = 1, type = 'multi') { - const query = { query: text, page }; + const query = { 'query': text, 'page': page }; return Promise.resolve() .then(() => this.tmdb(type, query)) .catch(() => { throw new Error('Could not search for movies.'); }) @@ -26,27 +29,235 @@ class TMDB { } + /** + * Retrive list of discover section of movies from TMDB. + * @param {Page, type} the page number to specify in the request for discover, + * and type for movie or show + * @returns {Promise} dict with query results, current page and total_pages + */ + discover(page, type='movie') { + // Sets the tmdb function type to the corresponding type from query + var tmdbType; + if (type === 'movie') { + tmdbType = 'discoverMovie'; + } else if (type === 'show') { + tmdbType = 'discoverShow'; + } else { + // Throw error if invalid type from query + return Promise.resolve() + .then(() => { + throw new Error('Invalid type declaration.') + }) + } + + // Build a query for tmdb with pagenumber + const query = { 'page': page } + return Promise.resolve() + .then(() => this.tmdb(tmdbType, query)) + .catch(() => { throw new Error('Could not fetch discover.'); }) + .then((response) => { + try { + // Return a object that has the results and a variable for page, total_pages + // and seasonedResponse + var seasonedResponse = response.results.map((result) => { + return convertTmdbToSeasoned(result, type); } + ); + return { 'results': seasonedResponse, + 'page': response.page, 'total_pages': response.total_pages }; + } catch (error) { + console.log(error) + throw new Error('Error while parsing discover list.') + } + }); + } + + + /** + * Retrive list of popular section of movies or shows from TMDB. + * @param {Page, type} the page number to specify in the request for popular, + * and type for movie or show + * @returns {Promise} dict with query results, current page and total_pages + */ + // TODO add filter for language + popular(page, type='movie') { + // Sets the tmdb function type to the corresponding type from query + var tmdbType; + if (type === 'movie') { + tmdbType = 'popularMovies'; + } else if (type === 'show') { + tmdbType = 'popularShows'; + } else { + // Throw error if invalid type from query + return Promise.resolve() + .then(() => { + throw new Error('Invalid type declaration.') + }) + } + + // Build a query for tmdb with pagenumber + const query = { 'page': page } + return Promise.resolve() + .then(() => this.tmdb(tmdbType, query)) + .catch(() => { throw new Error('Could not fetch popular.'); }) + .then((response) => { + try { + var seasonedResponse = response.results.map((result) => { + return convertTmdbToSeasoned(result, type); } + ); + // Return a object that has the results and a variable for page, total_pages + // and seasonedResponse + return { 'results': seasonedResponse, + 'page': response.page, 'total_pages': response.total_pages }; + } catch (error) { + console.log(error) + throw new Error('Error while parsing discover list.') + } + }); + } + + + + /** + * Retrive list of now playing/airing section of movies or shows from TMDB. + * @param {Page, type} the page number to specify in the request for now playing/airing, + * and type for movie or show + * @returns {Promise} dict with query results, current page and total_pages + */ + // TODO add filter for language + nowplaying(page, type='movie') { + // Sets the tmdb function type to the corresponding type from query + var tmdbType; + if (type === 'movie') { + tmdbType = 'nowPlayingMovies'; + } else if (type === 'show') { + tmdbType = 'nowAiringShows'; + } else { + // Throw error if invalid type from query + return Promise.resolve() + .then(() => { + throw new Error('Invalid type declaration.') + }) + } + + // Build a query for tmdb with pagenumber + const query = { 'page': page } + return Promise.resolve() + .then(() => this.tmdb(tmdbType, query)) + .catch(() => { throw new Error('Could not fetch popular.'); }) + .then((response) => { + try { + var seasonedResponse = response.results.map((result) => { + return convertTmdbToSeasoned(result, type); } + ); + // Return a object that has the results and a variable for page, total_pages + // and seasonedResponse + return { 'results': seasonedResponse, + 'page': response.page, 'total_pages': response.total_pages }; + } catch (error) { + console.log(error) + throw new Error('Error while parsing discover list.') + } + }); + } + + /** + * Retrive list of upcmoing movies from TMDB. + * @param {Page} the page number to specify in the request for upcoming movies + * @returns {Promise} dict with query results, current page and total_pages + */ + // TODO add filter for language + upcoming(page) { + const query = { 'page': page } + return Promise.resolve() + .then(() => this.tmdb('upcomingMovies', query)) + .catch(() => { throw new Error('Could not fetch upcoming movies.'); }) + .then((response) => { + try { + var seasonedResponse = response.results.map((result) => { + return convertTmdbToSeasoned(result, 'movie'); } + ); + // Return a object that has the results and a variable for page, total_pages + // and seasonedResponse + return { 'results': seasonedResponse, + 'page': response.page, 'total_pages': response.total_pages }; + } catch (parseError) { + throw new Error('Error while parsing upcoming movies list.') + } + }); + } + + + /** + * Retrive list of upcmoing movies from TMDB. + * @param {Page} the page number to specify in the request for upcoming movies + * @returns {Promise} dict with query results, current page and total_pages + */ + // TODO add filter for language + similar(identifier, type) { + var tmdbType; + if (type === 'movie') { + tmdbType = 'movieSimilar'; + } else if (type === 'show') { + tmdbType = 'showSimilar'; + } else { + // Throw error if invalid type from query + return Promise.resolve() + .then(() => { + throw new Error('Invalid type declaration.') + }) + } + + const query = { id: identifier } + return Promise.resolve() + .then(() => this.tmdb(tmdbType, query)) + .catch(() => { throw new Error('Could not fetch upcoming movies.'); }) + .then((response) => { + try { + var seasonedResponse = response.results.map((result) => { + return convertTmdbToSeasoned(result, type); } + ); + // Return a object that has the results and a variable for page, total_pages + // and seasonedResponse + return { 'results': seasonedResponse, + 'page': response.page, 'total_pages': response.total_pages }; + } catch (parseError) { + throw new Error('Error while parsing silimar media list.') + } + }); + } + + /** * Retrieve a specific movie by id from TMDB. * @param {Number} identifier of the movie you want to retrieve * @returns {Promise} succeeds if movie was found */ - lookup(identifier, type = 'movie') { - if (type === 'movie') { type = 'movieInfo'} - else if (type === 'tv') { type = 'tvInfo'} + lookup(identifier, queryType = 'movie') { + var type; + if (queryType === 'movie') { type = 'movieInfo'} + else if (queryType === 'show') { type = 'tvInfo'} + else { + return Promise.resolve() + .then(() => { + throw new Error('Invalid type declaration.') + }) + } const query = { id: identifier }; return Promise.resolve() - .then(() => this.tmdb(type, query)) - .catch(() => { throw new Error('Could not find a movie with that id.'); }) - .then((response) => { - try { - return convertTmdbToSeasoned(response); - } catch (parseError) { - throw new Error('Could not parse movie.'); - } - }); + .then(() => this.tmdb(type, query)) + .catch(() => { throw new Error('Could not find a movie with that id.'); }) + .then((response) => { + try { + var car = convertTmdbToSeasoned(response, queryType); + console.log(car); + return car; + } catch (parseError) { + throw new Error('Could not parse movie.'); + } + }); } + // TODO ADD CACHE LOOKUP tmdb(method, argument) { return new Promise((resolve, reject) => { const callback = (error, reponse) => { @@ -58,6 +269,7 @@ class TMDB { if (!argument) { this.tmdbLibrary[methodTypes[method]](callback); + // this.tmdbLibrary['miscUpcomingMovies'] } else { this.tmdbLibrary[methodTypes[method]](argument, callback); } From 7a1f709d90da1669b64461926050fcead979107b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:06:19 +0200 Subject: [PATCH 06/23] Small changed that first checks if the variable is undefined or not and then length --- seasoned_api/src/webserver/controllers/tmdb/searchMedia.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js b/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js index f09b859..90f5cda 100644 --- a/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js +++ b/seasoned_api/src/webserver/controllers/tmdb/searchMedia.js @@ -14,7 +14,7 @@ function searchMoviesController(req, res) { Promise.resolve() .then(() => tmdb.search(query, page, type)) .then((movies) => { - if (movies.length > 0) { + if (movies !== undefined || movies.length > 0) { res.send(movies); } else { res.status(404).send({ success: false, error: 'Search query did not return any results.'}) From 850452db78e8d2bf2b83147549876bb4708bb57d Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:07:53 +0200 Subject: [PATCH 07/23] Removed unnesessary this.id when calling function, this is already a object, don't need to pass itself. --- client/app/components/MovieObject.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/app/components/MovieObject.jsx b/client/app/components/MovieObject.jsx index e1be5d9..09da015 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/MovieObject.jsx @@ -7,6 +7,7 @@ class MovieObject { this.id = object.id; this.title = object.title; this.year = object.year; + this.type = object.type; // Check if object.poster != undefined this.poster = object.poster; this.matchedInPlex = object.matchedInPlex; @@ -17,8 +18,9 @@ class MovieObject { console.log('Exists', movie); } - requestMovie(id) { - fetch('https://apollo.kevinmidboe.com/api/v1/plex/request/' + id, { + requestMovie() { + // fetch('https://apollo.kevinmidboe.com/api/v1/plex/request/' + id, { + fetch('http://localhost:31459/api/v1/plex/request/' + this.id + '?type='+this.type, { method: 'POST' }); } @@ -106,7 +108,7 @@ class MovieObject { foundInPlex = ; } else { - foundInPlex = ; } From 6a4e2bc2ab53559efba356a085872f96535fb23a Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:29:51 +0200 Subject: [PATCH 08/23] Moved all styling to a separate file in ./style --- client/app/components/MovieObject.jsx | 94 +++---------------- .../components/styles/movieObjectStyle.jsx | 80 ++++++++++++++++ 2 files changed, 93 insertions(+), 81 deletions(-) create mode 100644 client/app/components/styles/movieObjectStyle.jsx diff --git a/client/app/components/MovieObject.jsx b/client/app/components/MovieObject.jsx index 09da015..1349f68 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/MovieObject.jsx @@ -1,6 +1,7 @@ import React from 'react'; // StyleComponents +import movieStyle from './styles/movieObjectStyle.jsx'; class MovieObject { constructor(object) { @@ -26,77 +27,6 @@ class MovieObject { } getElement() { - // TODO move this to separate files. - var resultItem = { - maxWidth: '95%', - margin: '0 auto', - minHeight: '230px' - } - var movie_content = { - marginLeft: '15px' - } - var resultTitle = { - color: 'black', - fontSize: '2em', - } - - var resultPoster = { - float: 'left', - zIndex: '3', - position: 'relative', - marginRight: '30px' - } - - var resultPosterImg = { - border: '2px none', - borderRadius: '2px', - width: '150px' - } - - var buttons = { - paddingTop: '20px' - } - - var requestButton = { - color: '#e9a131', - marginRight: '10px', - background: 'white', - border: '#e9a131 2px solid', - borderRadius: '4px', - textAlign: 'center', - padding: '10px', - minWidth: '100px', - float: 'left', - fontSize: '13px', - fontWeight: '800', - cursor: 'pointer' - } - - var tmdbButton = { - color: '#00d17c', - marginRight: '10px', - background: 'white', - border: '#00d17c 2px solid', - borderRadius: '4px', - textAlign: 'center', - padding: '10px', - minWidth: '100px', - float: 'left', - fontSize: '13px', - fontWeight: '800', - cursor: 'pointer' - } - - var row = { - width: '100%' - } - - var itemDivider = { - width: '90%', - borderBottom: '1px solid grey', - margin: '2rem auto' - } - // TODO set the poster image async by updating the dom after this is returned if (this.poster == null || this.poster == undefined) { var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' @@ -106,10 +36,10 @@ class MovieObject { var foundInPlex; if (this.matchedInPlex) { foundInPlex = ; + style={movieStyle.requestButton}>Request Anyway; } else { foundInPlex = ; + style={movieStyle.requestButton}>+ Request; } var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id @@ -117,12 +47,12 @@ class MovieObject { return (
-
-
- +
+
+
- {this.title} ({this.year}) + {this.title} ({this.year})

{this.summary}

@@ -130,14 +60,16 @@ class MovieObject { -
+
{foundInPlex} - + + +
-
-
+
+
) diff --git a/client/app/components/styles/movieObjectStyle.jsx b/client/app/components/styles/movieObjectStyle.jsx new file mode 100644 index 0000000..a87c726 --- /dev/null +++ b/client/app/components/styles/movieObjectStyle.jsx @@ -0,0 +1,80 @@ + +export default { + resultItem: { + maxWidth: '95%', + margin: '0 auto', + minHeight: '230px' + }, + + resultItem: { + maxWidth: '95%', + margin: '0 auto', + minHeight: '230px' + }, + + movie_content: { + marginLeft: '15px' + }, + + resultTitle: { + color: 'black', + fontSize: '2em', + }, + + resultPoster: { + float: 'left', + zIndex: '3', + position: 'relative', + marginRight: '30px' + }, + + resultPosterImg: { + border: '2px none', + borderRadius: '2px', + width: '150px' + }, + + buttons: { + paddingTop: '20px' + }, + + requestButton: { + color: '#e9a131', + marginRight: '10px', + background: 'white', + border: '#e9a131 2px solid', + borderRadius: '4px', + textAlign: 'center', + padding: '10px', + minWidth: '100px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer' + }, + + tmdbButton: { + color: '#00d17c', + marginRight: '10px', + background: 'white', + border: '#00d17c 2px solid', + borderRadius: '4px', + textAlign: 'center', + padding: '10px', + minWidth: '100px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer' + }, + + row: { + width: '100%' + }, + + itemDivider: { + width: '90%', + borderBottom: '1px solid grey', + margin: '2rem auto' + } +} \ No newline at end of file From 2b7f9551bf8b33b89d712536ce26f91a2d82c6d1 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 17:34:45 +0200 Subject: [PATCH 09/23] Moved styles from serachRequest to a seapate file in ./styles --- client/app/components/SearchRequest.jsx | 213 ++++++------------ .../components/styles/searchRequestStyle.jsx | 134 +++++++++++ 2 files changed, 198 insertions(+), 149 deletions(-) create mode 100644 client/app/components/styles/searchRequestStyle.jsx diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index 588b53f..e32d4da 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -2,8 +2,10 @@ import React from 'react'; import MovieObject from './MovieObject.jsx'; -// TODO add option for searching multi, movies or tv shows +// StyleComponents +import searchStyle from './styles/searchRequestStyle.jsx'; +// TODO add option for searching multi, movies or tv shows class SearchRequest extends React.Component { constructor(props){ super(props) @@ -17,15 +19,20 @@ class SearchRequest extends React.Component { } this.URLs = { - request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?page='+this.props.page+'&query=', - sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=' + // request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?page='+this.state.page+'&query=', + request: 'http://localhost:31459/api/v1/plex/request?page='+this.state.page+'&query=', + // upcoming: 'https://apollo.kevinmidboe.com/api/v1/tmdb/upcoming', + upcoming: 'http://localhost:31459/api/v1/tmdb/upcoming', + // sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=' + sendRequest: 'http://localhost:31459/api/v1/plex/request?query=' } } componentDidMount(){ var that = this; - this.setState({responseMovieList: null}) + // this.setState({responseMovieList: null}) + this.getUpcoming(); } // Handles all errors of the response of a fetch call @@ -36,6 +43,28 @@ class SearchRequest extends React.Component { return response; } + getUpcoming() { + let url = this.URLs.upcoming + '?page=' + this.state.page; + + fetch(url) + .then(response => this.handleErrors(response)) + .then(response => response.json()) + .then(data => { + console.log(data.total_pages) + if (data.results.length > 0) { + this.setState({ + responseMovieList: data.results.map(item => this.createMovieObjects(item)) + }) + } + }) + .catch(error => { + console.log(error); + this.setState({ + reposemovieList:

Not found (upcoming)

+ }) + }) + } + fetchQuery() { let url = this.URLs.request + this.state.searchQuery if (this.state.tvshowFilter) { @@ -102,163 +131,45 @@ class SearchRequest extends React.Component { } } + pageBackwards() { + if (this.state.page > 1) { + console.log('backwards'); + this.state.page--; + this.getUpcoming(); + } + console.log(this.state.page) + } + + pageForwards() { + this.state.page++; + this.getUpcoming(); + console.log('forwards'); + console.log(this.state.page) + } + render(){ - - var body = { - fontFamily: "'Open Sans', sans-serif", - backgroundColor: '#f7f7f7', - margin: 0, - padding: 0, - minHeight: '100%', - position: 'relative' - } - - var backgroundHeader = { - width: '100%', - minHeight: '400px', - backgroundColor: '#011c23', - zIndex: 1, - position: 'absolute' - } - - - var requestWrapper = { - top: '300px', - width: '90%', - maxWidth: '1200px', - margin: 'auto', - paddingTop: '20px', - backgroundColor: 'white', - position: 'relative', - zIndex: '10', - boxShadow: '0 2px 10px grey' - } - - var pageTitle = { - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - } - - var pageTitleSpan = { - color: 'white', - fontSize: '3em', - marginTop: '4vh', - marginBottom: '6vh' - } - - var box = { - width: '90%', - height: '50px', - maxWidth: '1200px', - margin: '0 auto' - } - - var container = { - verticalAlign: 'middle', - whiteSpace: 'nowrap', - position: 'relative', - display: 'flex', - justifyContent: 'center' - } - - var searchIcon = { - position: 'absolute', - marginLeft: '17px', - marginTop: '17px', - zIndex: '1', - color: '#4f5b66' - } - - var searchBar = { - width: '60%', - minWidth: '120px', - height: '50px', - background: '#ffffff', - border: 'none', - fontSize: '10pt', - float: 'left', - color: '#63717f', - paddingLeft: '45px', - borderRadius: '5px', - marginRight: '15px' - } - - var searchFilter = { - color: 'white', - fontSize: '1em', - paddingTop: '12px', - marginBottom: '12px', - marginLeft: '10px', - cursor: 'pointer' - } - - var hvrUnderlineFromCenter = { - color: 'white', - fontSize: '1em', - paddingTop: '12px', - marginBottom: '12px', - marginLeft: '10px', - cursor: 'pointer', - display: 'inline-block', - verticalAlign: 'middle', - WebkitTransform: 'perspective(1px) translateZ(0)', - transform: 'perspective(1px) translateZ(0)', - boxShadow: '0 0 1px transparent', - position: 'relative', - overflow: 'hidden', - ':before': { - content: "", - position: 'absolute', - zIndex: '-1', - left: '50%', - right: '50%', - bottom: '0', - background: '#00d17c', - height: '2px', - WebkitTransitionProperty: 'left, right', - transitionProperty: 'left, right', - WebkitTransitionDuration: '0.3s', - transitionDuration: '0.3s', - WebkitTransitionTimingFunction: 'ease-out', - transitionTimingFunction: 'ease-out' - }, - ':hover:before': { - left: 0, - right: 0 - }, - 'focus:before': { - left: 0, - right: 0 - }, - 'active:before': { - left: 0, - right: 0 - } - } - return( -
-
-
- Request new movies or tv shows +
+
+
+ Request new movies or tv shows
-
-
- +
+
+ - this._handleQueryKeyPress(event)} onChange={event => this.updateQueryState(event)} value={this.state.searchQuery}/> - {this.toggleFilter('movies')}} id="category_active">Movies - {this.toggleFilter('tvshows')}} id="category_inactive">TV Shows @@ -266,9 +177,13 @@ class SearchRequest extends React.Component {
-
+
{this.state.responseMovieList}
+
+ + +
) } diff --git a/client/app/components/styles/searchRequestStyle.jsx b/client/app/components/styles/searchRequestStyle.jsx new file mode 100644 index 0000000..d02eb5e --- /dev/null +++ b/client/app/components/styles/searchRequestStyle.jsx @@ -0,0 +1,134 @@ + +export default { + body: { + fontFamily: "'Open Sans', sans-serif", + backgroundColor: '#f7f7f7', + margin: 0, + padding: 0, + minHeight: '100%', + position: 'relative' + }, + + backgroundHeader: { + width: '100%', + minHeight: '400px', + backgroundColor: '#011c23', + zIndex: 1, + position: 'absolute' + }, + + requestWrapper: { + top: '300px', + width: '90%', + maxWidth: '1200px', + margin: 'auto', + paddingTop: '20px', + backgroundColor: 'white', + position: 'relative', + zIndex: '10', + boxShadow: '0 2px 10px grey' + }, + + pageTitle: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }, + + pageTitleSpan: { + color: 'white', + fontSize: '3em', + marginTop: '4vh', + marginBottom: '6vh' + }, + + box: { + width: '90%', + height: '50px', + maxWidth: '1200px', + margin: '0 auto' + }, + + container: { + verticalAlign: 'middle', + whiteSpace: 'nowrap', + position: 'relative', + display: 'flex', + justifyContent: 'center' + }, + + searchIcon: { + position: 'absolute', + marginLeft: '17px', + marginTop: '17px', + zIndex: '1', + color: '#4f5b66' + }, + + searchBar: { + width: '60%', + minWidth: '120px', + height: '50px', + background: '#ffffff', + border: 'none', + fontSize: '10pt', + float: 'left', + color: '#63717f', + paddingLeft: '45px', + borderRadius: '5px', + marginRight: '15px' + }, + + searchFilter: { + color: 'white', + fontSize: '1em', + paddingTop: '12px', + marginBottom: '12px', + marginLeft: '10px', + cursor: 'pointer' + }, + + hvrUnderlineFromCenter: { + color: 'white', + fontSize: '1em', + paddingTop: '12px', + marginBottom: '12px', + marginLeft: '10px', + cursor: 'pointer', + display: 'inline-block', + verticalAlign: 'middle', + WebkitTransform: 'perspective(1px) translateZ(0)', + transform: 'perspective(1px) translateZ(0)', + boxShadow: '0 0 1px transparent', + position: 'relative', + overflow: 'hidden', + ':before': { + content: "", + position: 'absolute', + zIndex: '-1', + left: '50%', + right: '50%', + bottom: '0', + background: '#00d17c', + height: '2px', + WebkitTransitionProperty: 'left, right', + transitionProperty: 'left, right', + WebkitTransitionDuration: '0.3s', + transitionDuration: '0.3s', + WebkitTransitionTimingFunction: 'ease-out', + transitionTimingFunction: 'ease-out' + }, + ':hover:before': { + left: 0, + right: 0 + }, + 'focus:before': { + left: 0, + right: 0 + }, + 'active:before': { + left: 0, + right: 0 + } + } +} \ No newline at end of file From 9eb31ea43322b97047b659b1db24d4ec87736ba7 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 18:16:26 +0200 Subject: [PATCH 10/23] Added type input to the sendRequest function. --- seasoned_api/src/plex/requestRepository.js | 12 ++++++++---- .../src/webserver/controllers/plex/submitRequest.js | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/seasoned_api/src/plex/requestRepository.js b/seasoned_api/src/plex/requestRepository.js index 8aef0ca..e72dc72 100644 --- a/seasoned_api/src/plex/requestRepository.js +++ b/seasoned_api/src/plex/requestRepository.js @@ -67,13 +67,16 @@ class RequestRepository { }); } - sendRequest(identifier) { + /** + * Send request for given media id. + * @param {identifier, type} the id of the media object and type of media must be defined + * @returns {Promise} If nothing has gone wrong. + */ + sendRequest(identifier, type) { // TODO add to DB so can have a admin page // TODO try a cache hit on the movie item - tmdb.lookup(identifier).then(movie => { - console.log(movie.title) - + tmdb.lookup(identifier, type).then(movie => { // create reusable transporter object using the default SMTP transport let transporter = nodemailer.createTransport({ @@ -110,6 +113,7 @@ class RequestRepository { }) + // TODO add better response when done. return Promise.resolve(); } diff --git a/seasoned_api/src/webserver/controllers/plex/submitRequest.js b/seasoned_api/src/webserver/controllers/plex/submitRequest.js index 80e1355..fd6345f 100644 --- a/seasoned_api/src/webserver/controllers/plex/submitRequest.js +++ b/seasoned_api/src/webserver/controllers/plex/submitRequest.js @@ -11,8 +11,9 @@ const requestRepository = new RequestRepository(); function submitRequestController(req, res) { // This is the id that is the param of the url const id = req.params.mediaId; + const type = req.query.type; - requestRepository.sendRequest(id) + requestRepository.sendRequest(id, type) .then(() => { res.send({ success: true, message: 'Media item sucessfully requested!' }); }) From 28d0b6396067bd28ecc8bdf11335013778c0a595 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sun, 3 Sep 2017 18:17:07 +0200 Subject: [PATCH 11/23] Added TODO and a output of what is requested in the router --- seasoned_api/src/webserver/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seasoned_api/src/webserver/app.js b/seasoned_api/src/webserver/app.js index 5d56fab..9bd47fe 100644 --- a/seasoned_api/src/webserver/app.js +++ b/seasoned_api/src/webserver/app.js @@ -15,7 +15,8 @@ var allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080'] router.use(function(req, res, next) { - console.log('Something is happening.'); + // TODO add logging of all incoming + console.log('Request: ', req.originalUrl); var origin = req.headers.origin; if (allowedOrigins.indexOf(origin) > -1) { res.setHeader('Access-Control-Allow-Origin', origin); From 231b70c226f5b69378cbfd04003c26eab9ae87b9 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Mon, 4 Sep 2017 13:43:31 +0200 Subject: [PATCH 12/23] Added what the different parts of seasoned does, that is external, api and services. --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6af9825..dd6b359 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# *Seasoned*: an intelligent organizer for your shows +# 🌶 seasonedShows : Your customly seasoned movie and show requester, downloader and organizer -*Seasoned* 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. +*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. ## 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. @@ -8,3 +8,21 @@ The flow of the system will first check for new folders in your tv shows directo 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. After approval by user the files are modified and moved to folders in resptected area. If error occours, pasteee link if log is sent to user. + +#### External + + Seasoned: request, discover and manage. + + Stray: Overview of downloaded episodes before they are organized. + + (+) Admin Panel: Overview of all stray episodes/movies. + +#### Api + + All communication between public website to server. + + Plex: All querying to what is localy available in your plex library. + + Stray (seasoned) -> also calls services (moveStray) through api. + + Tmdb: Requesting information from tmdb. + + (+) Admin Panel: Use secure login and session tokens to handle logged in viewer. + +#### Services + + Parse directories for new content. + + Extract and save in db information about stray item. + + Move a confirmed stray item. + + (+) Search for torrents matching new content. \ No newline at end of file From fcf6b324c9a16dcfe36c8ce1bfdc6266c4f6b864 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Mon, 4 Sep 2017 13:44:34 +0200 Subject: [PATCH 13/23] Moved the pitch sentence down under the header --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd6b359..e07e08a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# 🌶 seasonedShows : Your customly seasoned movie and show requester, downloader and organizer +# 🌶 seasonedShows -*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. +Your customly seasoned movie and show requester, downloader and organizer + +## About +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. ## 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. From e9c0e5bfa6d8678e5466677de5685c7d501ffb64 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Mon, 4 Sep 2017 13:45:18 +0200 Subject: [PATCH 14/23] Removed extra space under header --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e07e08a..8b34d0b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # 🌶 seasonedShows - Your customly seasoned movie and show requester, downloader and organizer ## About From cd52d295b060b22a40342fe6c46fe8c3f90d0ff1 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 7 Sep 2017 23:47:11 +0200 Subject: [PATCH 15/23] Now both Media and Media.Part in the JSON object about a stream is a list and need to index the first element of the stream to get this info. --- seasoned_api/src/plex/convertPlexToStream.js | 8 +++++--- seasoned_api/src/plex/convertStreamToMediaInfo.js | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/seasoned_api/src/plex/convertPlexToStream.js b/seasoned_api/src/plex/convertPlexToStream.js index 46ddfcd..1e82173 100644 --- a/seasoned_api/src/plex/convertPlexToStream.js +++ b/seasoned_api/src/plex/convertPlexToStream.js @@ -5,11 +5,13 @@ const convertStreamToUser = require('src/plex/stream/convertStreamToUser'); const ConvertStreamToPlayback = require('src/plex/stream/convertStreamToPlayback'); function convertPlexToStream(plexStream) { - const stream = convertPlexToSeasoned(plexStream); - stream.mediaInfo = convertStreamToMediaInfo(plexStream.Media); + const stream = convertPlexToSeasoned(plexStream) + const plexStreamMedia = plexStream.Media[0] + stream.mediaInfo = convertStreamToMediaInfo(plexStreamMedia); stream.player = convertStreamToPlayer(plexStream.Player); + stream.user = convertStreamToUser(plexStream.User); - stream.playback = new ConvertStreamToPlayback(plexStream.Media.Part); + stream.playback = new ConvertStreamToPlayback(plexStreamMedia.Part[0]); return stream; } diff --git a/seasoned_api/src/plex/convertStreamToMediaInfo.js b/seasoned_api/src/plex/convertStreamToMediaInfo.js index cd55477..3844fa9 100644 --- a/seasoned_api/src/plex/convertStreamToMediaInfo.js +++ b/seasoned_api/src/plex/convertStreamToMediaInfo.js @@ -15,6 +15,8 @@ function convertStreamToMediaInfo(plexStream) { mediaInfo.container = plexStream.container; mediaInfo.audioCodec = plexStream.audioCodec; + console.log(mediaInfo) + return mediaInfo; } From a8c6850863a00cd83218495524f098f8648b4819 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 7 Sep 2017 23:48:06 +0200 Subject: [PATCH 16/23] For some reason this was player info, but it is now been replaced with all the variables specific for mediaInfo --- seasoned_api/src/media_classes/mediaInfo.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/seasoned_api/src/media_classes/mediaInfo.js b/seasoned_api/src/media_classes/mediaInfo.js index c3cb52f..3019f25 100644 --- a/seasoned_api/src/media_classes/mediaInfo.js +++ b/seasoned_api/src/media_classes/mediaInfo.js @@ -1,12 +1,14 @@ class MediaInfo { - constructor(device, platform) { - this.device = undefined; - this.platform = undefined; - this.ip = undefined; - this.product = undefined; - this.title = undefined; - this.state = undefined; - + constructor() { + this.duration = undefined; + this.height = undefined; + this.width = undefined; + this.bitrate = undefined; + this.resolution = undefined; + this.framerate = undefined; + this.protocol = undefined; + this.container = undefined; + this.audioCodec = undefined; } } From 3eb2bbe54e4c20f68128858657c06a5154c1579a Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 7 Sep 2017 23:49:21 +0200 Subject: [PATCH 17/23] Removed a console log --- seasoned_api/src/plex/convertStreamToMediaInfo.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seasoned_api/src/plex/convertStreamToMediaInfo.js b/seasoned_api/src/plex/convertStreamToMediaInfo.js index 3844fa9..60d6c1d 100644 --- a/seasoned_api/src/plex/convertStreamToMediaInfo.js +++ b/seasoned_api/src/plex/convertStreamToMediaInfo.js @@ -6,6 +6,7 @@ function convertStreamToMediaInfo(plexStream) { mediaInfo.duration = plexStream.duration; mediaInfo.height = plexStream.height; mediaInfo.width = plexStream.width; + if (plexStream.bitrate) { mediaInfo.bitrate = plexStream.bitrate; } @@ -15,8 +16,6 @@ function convertStreamToMediaInfo(plexStream) { mediaInfo.container = plexStream.container; mediaInfo.audioCodec = plexStream.audioCodec; - console.log(mediaInfo) - return mediaInfo; } From 0370f051bcaddd2e257005fa5de4652d2f8ef69f Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 7 Sep 2017 23:51:22 +0200 Subject: [PATCH 18/23] Changed the error message returned if something goes wrong when fetching playing --- seasoned_api/src/plex/plexRepository.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/src/plex/plexRepository.js b/seasoned_api/src/plex/plexRepository.js index 0de89c6..25645a8 100644 --- a/seasoned_api/src/plex/plexRepository.js +++ b/seasoned_api/src/plex/plexRepository.js @@ -51,7 +51,7 @@ class PlexRepository { } }) .catch((err) => { - throw new Error(err); + throw new Error('Error handling plex playing. Error: ' + err); }) } } From eb2de340a3bc833b403c263f0c1bd98c4c1969d8 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Thu, 7 Sep 2017 23:52:05 +0200 Subject: [PATCH 19/23] Added variables for audioProfile, videoProfile, duration and container for a playback. --- seasoned_api/src/plex/stream/convertStreamToPlayback.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/seasoned_api/src/plex/stream/convertStreamToPlayback.js b/seasoned_api/src/plex/stream/convertStreamToPlayback.js index 555a07e..f4fae2d 100644 --- a/seasoned_api/src/plex/stream/convertStreamToPlayback.js +++ b/seasoned_api/src/plex/stream/convertStreamToPlayback.js @@ -4,6 +4,11 @@ class convertStreamToPlayback { this.width = plexStream.width; this.height = plexStream.height; this.decision = plexStream.decision; + this.audioProfile = plexStream.audioProfile; + this.videoProfile = plexStream.videoProfile; + this.duration = plexStream.duration; + this.container = plexStream.container; + } } From d26f4b053175b4dd7033df70cc47593775f7c3cf Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 8 Sep 2017 20:21:56 +0200 Subject: [PATCH 20/23] Set theme jekyll-theme-cayman --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file From 4fa32440afea39a50f32ee5dc53948db44c107dd Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 12 Sep 2017 14:39:32 +0200 Subject: [PATCH 23/23] Now we can fetch lists for discover, popular, now playing and upcoming with optional tv_show and movie filter options. --- client/app/components/SearchRequest.jsx | 122 +++++++++++++++--------- 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index e32d4da..5ee5d20 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -5,6 +5,8 @@ import MovieObject from './MovieObject.jsx'; // StyleComponents import searchStyle from './styles/searchRequestStyle.jsx'; +import URI from 'urijs'; + // TODO add option for searching multi, movies or tv shows class SearchRequest extends React.Component { constructor(props){ @@ -14,10 +16,18 @@ class SearchRequest extends React.Component { searchQuery: '', responseMovieList: null, movieFilter: true, - tvshowFilter: false, + showFilter: false, + discoverType: '', page: 1 } + this.allowedDiscoverTypes = [ + 'discover', 'popular', 'nowplaying', 'upcoming' + ] + + // this.baseUrl = 'https://apollo.kevinmidboe.com/api/v1/'; + this.baseUrl = 'http://localhost:31459/api/v1/tmdb/'; + this.URLs = { // request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?page='+this.state.page+'&query=', request: 'http://localhost:31459/api/v1/plex/request?page='+this.state.page+'&query=', @@ -32,7 +42,7 @@ class SearchRequest extends React.Component { componentDidMount(){ var that = this; // this.setState({responseMovieList: null}) - this.getUpcoming(); + this.fetchDiscover('upcoming'); } // Handles all errors of the response of a fetch call @@ -43,56 +53,77 @@ class SearchRequest extends React.Component { return response; } - getUpcoming() { - let url = this.URLs.upcoming + '?page=' + this.state.page; - fetch(url) - .then(response => this.handleErrors(response)) - .then(response => response.json()) - .then(data => { - console.log(data.total_pages) - if (data.results.length > 0) { - this.setState({ - responseMovieList: data.results.map(item => this.createMovieObjects(item)) - }) - } - }) - .catch(error => { - console.log(error); + fetchDiscover(queryDiscoverType) { + if (this.allowedDiscoverTypes.indexOf(queryDiscoverType) === -1) + throw Error('Invalid discover type: ' + queryDiscoverType); + + var uri = new URI(this.baseUrl); + uri.segment(queryDiscoverType) + uri = uri.setSearch('page', this.state.page); + if (this.state.showFilter) + uri = uri.addSearch('type', 'show'); + + console.log(uri) + + this.setState({ + responseMovieList: 'Loading...' + }); + + fetch(uri) + // Check if the response is ok + .then(response => this.handleErrors(response)) + .then(response => response.json()) // Convert to json object and pass to next then + .then(data => { // Parse the data of the JSON response + // If it is something here it updates the state variable with the HTML list of all + // movie objects that where returned by the search request + if (data.results.length > 0) { this.setState({ - reposemovieList:

Not found (upcoming)

+ responseMovieList: data.results.map(item => this.createMovieObjects(item)) }) + } + }) + // If the -------- + .catch(error => { + console.log(error) + this.setState({ + responseMovieList:

Not Found

}) + + console.log('Error submit: ', error.toString()); + }); } + fetchQuery() { let url = this.URLs.request + this.state.searchQuery - if (this.state.tvshowFilter) { + if (this.state.showFilter) { url = url + '&type=tv' } - fetch(url) - // Check if the response is ok - .then(response => this.handleErrors(response)) - .then(response => response.json()) // Convert to json object and pass to next then - .then(data => { // Parse the data of the JSON response - // If it is something here it updates the state variable with the HTML list of all - // movie objects that where returned by the search request - if (data.length > 0) { - this.setState({ - responseMovieList: data.map(item => this.createMovieObjects(item)) - }) - } + fetch(url) + // Check if the response is ok + .then(response => this.handleErrors(response)) + .then(response => response.json()) // Convert to json object and pass to next then + .then(data => { // Parse the data of the JSON response + // If it is something here it updates the state variable with the HTML list of all + // movie objects that where returned by the search request + console.log(data) + if (data.length > 0) { + this.setState({ + responseMovieList: data.map(item => this.createMovieObjects(item)) }) - // If the -------- - .catch(error => { - console.log(error) - this.setState({ - responseMovieList:

Not Found

- }) + } + }) + // If the -------- + .catch(error => { + console.log(error) + this.setState({ + responseMovieList:

Not Found

+ }) - console.log('Error submit: ', error.toString()); - }); + console.log('Error submit: ', error.toString()); + }); } // Updates the internal state of the query search field. @@ -123,11 +154,11 @@ class SearchRequest extends React.Component { }) console.log(this.state.movieFilter); } - else if (filterType == 'tvshows') { + else if (filterType == 'shows') { this.setState({ - tvshowFilter: !this.state.tvshowFilter + showFilter: !this.state.showFilter }) - console.log(this.state.tvshowFilter); + console.log(this.state.showFilter); } } @@ -151,6 +182,11 @@ class SearchRequest extends React.Component { render(){ return(
+ + + + +
Request new movies or tv shows @@ -171,7 +207,7 @@ class SearchRequest extends React.Component { id="category_active">Movies {this.toggleFilter('tvshows')}} + onClick={() => {this.toggleFilter('shows')}} id="category_inactive">TV Shows