Merge pull request #22 from KevinMidboe/client_features

Client features
This commit was merged in pull request #22.
This commit is contained in:
2017-09-07 23:56:02 +02:00
committed by GitHub
16 changed files with 686 additions and 292 deletions

View File

@@ -1,102 +1,32 @@
import React from 'react'; import React from 'react';
import glamorous from 'glamorous';
// StyleComponents // StyleComponents
import mediaResultItem from './styledComponents/mediaResultItem.jsx'; import movieStyle from './styles/movieObjectStyle.jsx';
class MovieObject { class MovieObject {
constructor(object) { constructor(object) {
this.id = object.id; this.id = object.id;
this.title = object.title; this.title = object.title;
this.year = object.year; this.year = object.year;
this.type = object.type;
// Check if object.poster != undefined // Check if object.poster != undefined
this.poster = object.poster; this.poster = object.poster;
this.matchedInPlex = object.matchedInPlex; this.matchedInPlex = object.matchedInPlex;
this.overview = object.overview; this.summary = object.summary;
} }
requestExisting(movie) { requestExisting(movie) {
console.log('Exists', movie); console.log('Exists', movie);
} }
requestMovie(id) { requestMovie() {
fetch('https://apollo.kevinmidboe.com/api/v1/plex/request/' + id, { // 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' method: 'POST'
}); });
} }
getElement() { 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 // TODO set the poster image async by updating the dom after this is returned
if (this.poster == null || this.poster == undefined) { if (this.poster == null || this.poster == undefined) {
var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' 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; var foundInPlex;
if (this.matchedInPlex) { if (this.matchedInPlex) {
foundInPlex = <button onClick={() => {this.requestExisting(this)}} foundInPlex = <button onClick={() => {this.requestExisting(this)}}
style={requestButton}><span>Request Anyway</span></button>; style={movieStyle.requestButton}><span>Request Anyway</span></button>;
} else { } else {
foundInPlex = <button onClick={() => {this.requestMovie(this.id)}} foundInPlex = <button onClick={() => {this.requestMovie()}}
style={requestButton}><span>&#x0002B; Request</span></button>; style={movieStyle.requestButton}><span>&#x0002B; Request</span></button>;
} }
var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id
@@ -117,27 +47,29 @@ class MovieObject {
return ( return (
<div> <div>
<div style={resultItem} key={this.id}> <div style={movieStyle.resultItem} key={this.id}>
<div style={resultPoster}> <div style={movieStyle.resultPoster}>
<img style={resultPosterImg} id='poster' src={posterPath}></img> <img style={movieStyle.resultPosterImg} id='poster' src={posterPath}></img>
</div> </div>
<div> <div>
<span style={resultTitle}>{this.title} ({this.year})</span> <span style={movieStyle.resultTitle}>{this.title} ({this.year})</span>
<br></br> <br></br>
<span>{this.overview}</span> <span>{this.summary}</span>
<br></br> <br></br>
<span className='imdbLogo'> <span className='imdbLogo'>
</span> </span>
<div style={buttons}> <div style={movieStyle.buttons}>
{foundInPlex} {foundInPlex}
<a href={themoviedbLink}><button style={tmdbButton}><span>Info</span></button></a> <a href={themoviedbLink}>
<button style={movieStyle.tmdbButton}><span>Info</span></button>
</a>
</div> </div>
</div> </div>
</div> </div>
<div style={row}> <div style={movieStyle.row}>
<div style={itemDivider}></div> <div style={movieStyle.itemDivider}></div>
</div> </div>
</div>) </div>)

View File

@@ -2,37 +2,67 @@ import React from 'react';
import MovieObject from './MovieObject.jsx'; 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 { class SearchRequest extends React.Component {
constructor(props){ constructor(props){
super(props) super(props)
// Constructor with states holding the search query and the element of reponse. // Constructor with states holding the search query and the element of reponse.
this.state = { this.state = {
searchQuery: '', searchQuery: '',
responseMovieList: null, responseMovieList: null,
movieFilter: true, movieFilter: true,
tvshowFilter: false tvshowFilter: false,
page: 1
} }
this.URLs = { this.URLs = {
request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?query=', // request: 'https://apollo.kevinmidboe.com/api/v1/plex/request?page='+this.state.page+'&query=',
sendRequest: 'https://apollo.kevinmidboe.com/api/v1/plex/request?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(){ componentDidMount(){
var that = this; var that = this;
this.setState({responseMovieList: null}) // this.setState({responseMovieList: null})
this.getUpcoming();
} }
// Handles all errors of the response of a fetch call // Handles all errors of the response of a fetch call
handleErrors(response) { handleErrors(response) {
if (!response.ok) { if (!response.ok) {
throw Error(response.status); throw Error(response.status);
} }
return response; 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: <h1>Not found (upcoming)</h1>
})
})
} }
fetchQuery() { fetchQuery() {
@@ -42,27 +72,27 @@ class SearchRequest extends React.Component {
} }
fetch(url) fetch(url)
// Check if the response is ok // Check if the response is ok
.then(response => this.handleErrors(response)) .then(response => this.handleErrors(response))
.then(response => response.json()) // Convert to json object and pass to next then .then(response => response.json()) // Convert to json object and pass to next then
.then(data => { // Parse the data of the JSON response .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 // 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 // movie objects that where returned by the search request
if (data.length > 0) { if (data.length > 0) {
this.setState({ this.setState({
responseMovieList: data.map(item => this.createMovieObjects(item)) responseMovieList: data.map(item => this.createMovieObjects(item))
}) })
} }
}) })
// If the -------- // If the --------
.catch(error => { .catch(error => {
console.log(error) console.log(error)
this.setState({ this.setState({
responseMovieList: <h1>Not Found</h1> responseMovieList: <h1>Not Found</h1>
}) })
console.log('Error submit: ', error.toString()); console.log('Error submit: ', error.toString());
}); });
} }
// Updates the internal state of the query search field. // Updates the internal state of the query search field.
@@ -101,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(){ 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( return(
<div style={body}> <div style={searchStyle.body}>
<div className='backgroundHeader' style={backgroundHeader}> <div className='backgroundHeader' style={searchStyle.backgroundHeader}>
<div className='pageTitle' style={pageTitle}> <div className='pageTitle' style={searchStyle.pageTitle}>
<span style={pageTitleSpan}>Request new movies or tv shows</span> <span style={searchStyle.pageTitleSpan}>Request new movies or tv shows</span>
</div> </div>
<div className='box' style={box}> <div className='box' style={searchStyle.box}>
<div style={container}> <div style={searchStyle.container}>
<span style={searchIcon}><i className="fa fa-search"></i></span> <span style={searchStyle.searchIcon}><i className="fa fa-search"></i></span>
<input style={searchBar} type="text" id="search" placeholder="Search for new content..." <input style={searchStyle.searchBar} type="text" id="search" placeholder="Search for new content..."
onKeyPress={(event) => this._handleQueryKeyPress(event)} onKeyPress={(event) => this._handleQueryKeyPress(event)}
onChange={event => this.updateQueryState(event)} onChange={event => this.updateQueryState(event)}
value={this.state.searchQuery}/> value={this.state.searchQuery}/>
<span style={searchFilter} <span style={searchStyle.searchFilter}
className="search_category hvrUnderlineFromCenter" className="search_category hvrUnderlineFromCenter"
onClick={() => {this.toggleFilter('movies')}} onClick={() => {this.toggleFilter('movies')}}
id="category_active">Movies</span> id="category_active">Movies</span>
<span style={searchFilter} <span style={searchStyle.searchFilter}
className="search_category hvrUnderlineFromCenter" className="search_category hvrUnderlineFromCenter"
onClick={() => {this.toggleFilter('tvshows')}} onClick={() => {this.toggleFilter('tvshows')}}
id="category_inactive">TV Shows</span> id="category_inactive">TV Shows</span>
@@ -265,14 +177,18 @@ class SearchRequest extends React.Component {
</div> </div>
</div> </div>
<div id='requestMovieList' ref='requestMovieList' style={requestWrapper}> <div id='requestMovieList' ref='requestMovieList' style={searchStyle.requestWrapper}>
{this.state.responseMovieList} {this.state.responseMovieList}
</div> </div>
<div>
<button onClick={() => {this.pageBackwards()}}>Back</button>
<button onClick={() => {this.pageForwards()}}>Forward</button>
</div>
</div> </div>
) )
} }
} }
export default SearchRequest; export default SearchRequest;

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
"cross-env": "^3.1.3", "cross-env": "^3.1.3",
"express": "~4.0.0", "express": "~4.0.0",
"mongoose": "^3.6.13", "mongoose": "^3.6.13",
"moviedb": "^0.2.7", "moviedb": "^0.2.10",
"node-cache": "^4.1.1", "node-cache": "^4.1.1",
"nodemailer": "^4.0.1", "nodemailer": "^4.0.1",
"python-shell": "^0.4.0", "python-shell": "^0.4.0",

View File

@@ -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 add to DB so can have a admin page
// TODO try a cache hit on the movie item // TODO try a cache hit on the movie item
tmdb.lookup(identifier).then(movie => { tmdb.lookup(identifier, type).then(movie => {
console.log(movie.title)
// create reusable transporter object using the default SMTP transport // create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({ let transporter = nodemailer.createTransport({
@@ -110,6 +113,7 @@ class RequestRepository {
}) })
// TODO add better response when done.
return Promise.resolve(); return Promise.resolve();
} }

View File

@@ -1,8 +1,8 @@
const Movie = require('src/media_classes/movie'); const Movie = require('src/media_classes/movie');
const Show = require('src/media_classes/show'); const Show = require('src/media_classes/show');
function convertTmdbToSeasoned(tmdbObject) { function convertTmdbToSeasoned(tmdbObject, strictType=undefined) {
const mediaType = tmdbObject.media_type; const mediaType = strictType || tmdbObject.media_type;
// There are many diff types of content, we only want to look at movies and tv shows // There are many diff types of content, we only want to look at movies and tv shows
if (mediaType === 'movie') { if (mediaType === 'movie') {
@@ -16,6 +16,7 @@ function convertTmdbToSeasoned(tmdbObject) {
const movie = new Movie(title, year, mediaType); const movie = new Movie(title, year, mediaType);
movie.id = tmdbObject.id;
movie.summary = tmdbObject.overview; movie.summary = tmdbObject.overview;
movie.rating = tmdbObject.vote_average; movie.rating = tmdbObject.vote_average;
movie.poster = tmdbObject.poster_path; movie.poster = tmdbObject.poster_path;
@@ -27,11 +28,12 @@ function convertTmdbToSeasoned(tmdbObject) {
return movie; return movie;
} }
else if (mediaType === 'tv') { else if (mediaType === 'tv' || mediaType === 'show') {
const year = new Date(tmdbObject.first_air_date).getFullYear(); 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.summary = tmdbObject.overview;
show.rating = tmdbObject.vote_average; show.rating = tmdbObject.vote_average;
show.poster = tmdbObject.poster_path; show.poster = tmdbObject.poster_path;

View File

@@ -1,7 +1,10 @@
const moviedb = require('moviedb'); const moviedb = require('moviedb');
const convertTmdbToSeasoned = require('src/tmdb/convertTmdbToSeasoned'); const convertTmdbToSeasoned = require('src/tmdb/convertTmdbToSeasoned');
var methodTypes = { 'movie': 'searchMovie', 'tv': 'searchTv', 'multi': 'searchMulti', 'movieInfo': 'movieInfo', var methodTypes = { 'movie': 'searchMovie', 'show': 'searchTv', 'multi': 'searchMulti', 'movieInfo': 'movieInfo',
'tvInfo': 'tvInfo' }; 'tvInfo': 'tvInfo', 'upcomingMovies': 'miscUpcomingMovies', 'discoverMovie': 'discoverMovie',
'discoverShow': 'discoverTv', 'popularMovies': 'miscPopularMovies', 'popularShows': 'miscPopularTvs',
'nowPlayingMovies': 'miscNowPlayingMovies', 'nowAiringShows': 'tvOnTheAir', 'movieSimilar': 'movieSimilar',
'showSimilar': 'tvSimilar' };
class TMDB { class TMDB {
constructor(apiKey, tmdbLibrary) { constructor(apiKey, tmdbLibrary) {
@@ -9,7 +12,7 @@ class TMDB {
} }
search(text, page = 1, type = 'multi') { search(text, page = 1, type = 'multi') {
const query = { query: text, page }; const query = { 'query': text, 'page': page };
return Promise.resolve() return Promise.resolve()
.then(() => this.tmdb(type, query)) .then(() => this.tmdb(type, query))
.catch(() => { throw new Error('Could not search for movies.'); }) .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. * Retrieve a specific movie by id from TMDB.
* @param {Number} identifier of the movie you want to retrieve * @param {Number} identifier of the movie you want to retrieve
* @returns {Promise} succeeds if movie was found * @returns {Promise} succeeds if movie was found
*/ */
lookup(identifier, type = 'movie') { lookup(identifier, queryType = 'movie') {
if (type === 'movie') { type = 'movieInfo'} var type;
else if (type === 'tv') { type = 'tvInfo'} 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 }; const query = { id: identifier };
return Promise.resolve() return Promise.resolve()
.then(() => this.tmdb(type, query)) .then(() => this.tmdb(type, query))
.catch(() => { throw new Error('Could not find a movie with that id.'); }) .catch(() => { throw new Error('Could not find a movie with that id.'); })
.then((response) => { .then((response) => {
try { try {
return convertTmdbToSeasoned(response); var car = convertTmdbToSeasoned(response, queryType);
} catch (parseError) { console.log(car);
throw new Error('Could not parse movie.'); return car;
} } catch (parseError) {
}); throw new Error('Could not parse movie.');
}
});
} }
// TODO ADD CACHE LOOKUP
tmdb(method, argument) { tmdb(method, argument) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callback = (error, reponse) => { const callback = (error, reponse) => {
@@ -58,6 +269,7 @@ class TMDB {
if (!argument) { if (!argument) {
this.tmdbLibrary[methodTypes[method]](callback); this.tmdbLibrary[methodTypes[method]](callback);
// this.tmdbLibrary['miscUpcomingMovies']
} else { } else {
this.tmdbLibrary[methodTypes[method]](argument, callback); this.tmdbLibrary[methodTypes[method]](argument, callback);
} }

View File

@@ -15,7 +15,8 @@ var allowedOrigins = ['https://kevinmidboe.com', 'http://localhost:8080']
router.use(function(req, res, next) { 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; var origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) { if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader('Access-Control-Allow-Origin', origin); res.setHeader('Access-Control-Allow-Origin', origin);
@@ -40,6 +41,12 @@ router.post('/v1/plex/request/:mediaId', require('./controllers/plex/submitReque
router.get('/v1/plex/hook', require('./controllers/plex/hookDump.js')); router.get('/v1/plex/hook', require('./controllers/plex/hookDump.js'));
router.get('/v1/tmdb/search', require('./controllers/tmdb/searchMedia.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.get('/v1/tmdb/:mediaId', require('./controllers/tmdb/readMedia.js'));
router.post('/v1/git/dump', require('./controllers/git/dumpHook.js')); router.post('/v1/git/dump', require('./controllers/git/dumpHook.js'));

View File

@@ -11,8 +11,9 @@ const requestRepository = new RequestRepository();
function submitRequestController(req, res) { function submitRequestController(req, res) {
// This is the id that is the param of the url // This is the id that is the param of the url
const id = req.params.mediaId; const id = req.params.mediaId;
const type = req.query.type;
requestRepository.sendRequest(id) requestRepository.sendRequest(id, type)
.then(() => { .then(() => {
res.send({ success: true, message: 'Media item sucessfully requested!' }); res.send({ success: true, message: 'Media item sucessfully requested!' });
}) })

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ function searchMoviesController(req, res) {
Promise.resolve() Promise.resolve()
.then(() => tmdb.search(query, page, type)) .then(() => tmdb.search(query, page, type))
.then((movies) => { .then((movies) => {
if (movies.length > 0) { if (movies !== undefined || movies.length > 0) {
res.send(movies); res.send(movies);
} else { } else {
res.status(404).send({ success: false, error: 'Search query did not return any results.'}) res.status(404).send({ success: false, error: 'Search query did not return any results.'})

View File

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