diff --git a/client/src/components/App.js b/client/src/components/App.js index 6f5964c..c5013cc 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -7,6 +7,7 @@ import Header from './Header'; import Hero from './Hero'; import Plex from './plex/Plex'; import SimilarList from './SimilarList'; +import PopularList from './PopularList'; class App extends Component { componentDidMount() { @@ -26,6 +27,7 @@ class App extends Component { path="/similar/:show" render={props => } /> + diff --git a/client/src/components/Header.js b/client/src/components/Header.js index 896dcd5..8660e1c 100644 --- a/client/src/components/Header.js +++ b/client/src/components/Header.js @@ -1,9 +1,10 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {Link} from 'react-router-dom'; - +import '../css/materialize.css'; class Header extends Component { renderContent() { + const isMobile = window.innerWidth < 480; switch (this.props.auth) { case null: return; @@ -14,16 +15,21 @@ class Header extends Component { ); default: - return ( -
-
  • - Most Watched -
  • -
  • - Logout -
  • -
    - ); + if (!isMobile) { + return ( +
    +
  • + Most Watched +
  • +
  • + Popular +
  • +
  • + Logout +
  • +
    + ); + } } } render() { diff --git a/client/src/components/MediaCard.js b/client/src/components/MediaCard.js index 0d4d2eb..d83ef52 100644 --- a/client/src/components/MediaCard.js +++ b/client/src/components/MediaCard.js @@ -5,8 +5,8 @@ import {connect} from 'react-redux'; import Header from './helpers/Header'; import styles from '../css/materialize.css'; import '../css/materialize.css'; - import {Link} from 'react-router-dom'; + class MediaCard extends Component { render() { const show = this.props.media; diff --git a/client/src/components/PopularCard.js b/client/src/components/PopularCard.js new file mode 100644 index 0000000..d27bc03 --- /dev/null +++ b/client/src/components/PopularCard.js @@ -0,0 +1,143 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {withStyles} from '@material-ui/core/styles'; +import {connect} from 'react-redux'; +import Header from './helpers/Header'; +import styles from '../css/materialize.css'; +import {ToastContainer} from 'react-toastify'; +import * as actions from '../actions'; +import {Link} from 'react-router-dom'; +import 'react-toastify/dist/ReactToastify.css'; + +class PopularCard extends Component { + renderContent() { + if ( + this.props.loading && + this.props.currentShow === this.props.media.name + ) { + return ( +
    +
    +
    + ); + } + } + renderToast() { + if (this.props.currentShow === this.props.media.name) { + return ; + } + } + + render() { + const show = this.props.media; + const isMobile = window.innerWidth < 480; + if (!isMobile) { + return ( +
    + {this.renderToast()} + {this.renderContent()} +
    +
    +
    +
    + pic +
    +
    +
    +
    +
    +
    +

    {show.overview}

    +
    +
    +
    + Rating: {` ${show.vote_average} `}| Popularity:{' '} + {` ${show.popularity}`} +
    + + +
    +
    +
    +
    +
    +
    + ); + } + return ( +
    + +
    +
    +
    + pic +
    +
    +

    {show.overview}

    +
    +
    + this.props.addSeries({showName: show.name})} + > + sendAdd to Sonarr + +
    +
    +
    +
    + ); + } +} + +PopularCard.propTypes = { + classes: PropTypes.object.isRequired, +}; + +function mapStateToProps(state) { + return { + loading: state.sonarr.loading, + sonarrAddSeries: state.sonarr.sonarrAddSeries, + currentShow: state.plex.currentShow, + }; +} + +export default connect( + mapStateToProps, + actions, +)(withStyles(styles)(PopularCard)); diff --git a/client/src/components/PopularList.js b/client/src/components/PopularList.js new file mode 100644 index 0000000..b743842 --- /dev/null +++ b/client/src/components/PopularList.js @@ -0,0 +1,50 @@ +import React, {Component} from 'react'; +import {Redirect} from 'react-router-dom'; +import {withStyles} from '@material-ui/core/styles'; +import {connect} from 'react-redux'; +import styles from '../css/materialize.css.js'; +import axios from 'axios'; +import PopularCard from './PopularCard'; +import * as actions from '../actions'; + +class PopularList extends Component { + state = { + shows: [], + }; + componentDidMount() { + this.getSimilar(); + } + + getSimilar = async () => { + const res = await axios.get('/api/moviedb/tv/popular'); + const shows = res.data; + this.setState({shows: shows}); + }; + + render() { + if (!this.props.auth) { + return ; + } + if (this.state.shows.length > 0) { + const mediaList = this.state.shows.map((show, index) => { + return ; + }); + return
    {mediaList}
    ; + } + + return ( +
    +
    +
    + ); + } +} + +function mapStateToProps({plex, auth, sonarr}) { + return {loading: plex.loading, auth, sonarrAddSeries: sonarr.sonarrAddSeries}; +} + +export default connect( + mapStateToProps, + actions, +)(withStyles(styles)(PopularList)); diff --git a/client/src/components/PrivateRoute.js b/client/src/components/PrivateRoute.js new file mode 100644 index 0000000..c6eaa97 --- /dev/null +++ b/client/src/components/PrivateRoute.js @@ -0,0 +1,24 @@ +import React from 'react'; +import {Redirect, Route} from 'react-router-dom'; +import {connect} from 'react-redux'; +// Utils + +const PrivateRoute = ({component: Component, ...rest}) => ( + + props.auth !== null ? ( + + ) : ( + + ) + } + /> +); + +export default PrivateRoute; diff --git a/client/src/components/SimilarCard.js b/client/src/components/SimilarCard.js index 48bea16..d2a2fb3 100644 --- a/client/src/components/SimilarCard.js +++ b/client/src/components/SimilarCard.js @@ -7,6 +7,7 @@ import styles from '../css/materialize.css'; import {ToastContainer} from 'react-toastify'; import * as actions from '../actions'; import 'react-toastify/dist/ReactToastify.css'; +import {Link} from 'react-router-dom'; class MediaCard extends Component { renderContent() { @@ -106,23 +107,16 @@ class MediaCard extends Component {

    {show.overview}

    -
    - {' '} -
    - live_tvRating: - {` ${show.vote_average}`} Popularity: {` ${show.popularity}`} - -
    +
    + this.props.addSeries({showName: show.name})} + > + sendAdd to Sonarr +
    diff --git a/client/src/components/plex/Plex.js b/client/src/components/plex/Plex.js index adb832a..8af2fbd 100644 --- a/client/src/components/plex/Plex.js +++ b/client/src/components/plex/Plex.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PlexTokenForm from './PlexTokenForm'; +import {Link} from 'react-router-dom'; import ImportPlexLibrary from './ImportPlexLibrary'; import MediaList from '../MediaList'; @@ -20,6 +21,14 @@ class Plex extends Component {
    +
    + + sendGet Started + +
    ); } diff --git a/client/src/components/plex/PlexTokenForm.js b/client/src/components/plex/PlexTokenForm.js index 689bb84..5da446f 100644 --- a/client/src/components/plex/PlexTokenForm.js +++ b/client/src/components/plex/PlexTokenForm.js @@ -10,7 +10,14 @@ import TextHeader from '../helpers/Header'; import styles from '../../css/materialize.css'; class PlexTokenForm extends React.Component { - state = {email: '', password: '', sonarrUrl: '', sonarrApiKey: ''}; + state = { + email: '', + password: '', + sonarrUrl: '', + sonarrApiKey: '', + errorMessage: '', + redirect: false, + }; onFormSubmit = event => { event.preventDefault(); @@ -18,8 +25,13 @@ class PlexTokenForm extends React.Component { }; getPlexToken = async params => { - await axios.get('/api/plex/token', {params}); - window.location.reload(); + const res = await axios.get('/api/plex/token', {params}); + console.log('response', res.data); + if (res.data.includes('Invalid')) { + this.setState({errorMessage: res.data}); + } else { + window.location.reload(); + } }; render() { @@ -37,6 +49,9 @@ class PlexTokenForm extends React.Component {

    +
    +
    {this.state.errorMessage}
    +
    diff --git a/client/src/reducers/authReducer.js b/client/src/reducers/authReducer.js index 4e65b2c..f93f6c3 100644 --- a/client/src/reducers/authReducer.js +++ b/client/src/reducers/authReducer.js @@ -3,6 +3,7 @@ import {types} from '../actions/index'; export default function(state = {}, action) { switch (action.type) { case types.FETCH_USER: + console.log('actionpayload', action.payload); return action.payload || false; default: return state; diff --git a/server/controllers/movieDb.controller.js b/server/controllers/movieDb.controller.js index ab66bc8..471c065 100644 --- a/server/controllers/movieDb.controller.js +++ b/server/controllers/movieDb.controller.js @@ -5,5 +5,6 @@ const router = Router(); router.get('/tv/search', movieDbService.searchTv); router.get('/tv/similar', movieDbService.similarTv); +router.get('/tv/popular', movieDbService.popularTv); export default router; diff --git a/server/services/moviedb/index.js b/server/services/moviedb/index.js index ac13d00..eeabf50 100644 --- a/server/services/moviedb/index.js +++ b/server/services/moviedb/index.js @@ -10,6 +10,17 @@ const searchTv = async (req, res) => { res.json(response); }; +const popularTv = async (req, res) => { + const response = await movieDbApi.popularTv(); + const library = await sonarrService.getSeries(req.user); + const jsonLibrary = JSON.parse(library); + const libraryTitles = jsonLibrary.map(show => show.title.toLowerCase()); + const filteredResponse = response.results.filter( + show => !libraryTitles.includes(show.name.toLowerCase()), + ); + res.json(filteredResponse); +}; + const similarTv = async (req, res) => { const {showName} = req.query; const searchResponse = await movieDbApi.searchTv(showName); @@ -22,7 +33,6 @@ const similarTv = async (req, res) => { // }); // Use Sonarr list instead const libraryTitles = jsonLibrary.map(show => show.title.toLowerCase()); - console.log('titles', libraryTitles); const filteredResponse = similarResponse.results.filter( show => !libraryTitles.includes(show.name.toLowerCase()), ); @@ -32,4 +42,5 @@ const similarTv = async (req, res) => { export default { searchTv, similarTv, + popularTv, }; diff --git a/server/services/moviedb/movieDbApi.js b/server/services/moviedb/movieDbApi.js index 6b47d55..31554fc 100644 --- a/server/services/moviedb/movieDbApi.js +++ b/server/services/moviedb/movieDbApi.js @@ -4,6 +4,15 @@ import models from '../../db/models'; import MovieDb from 'moviedb-promise'; const mdb = new MovieDb(config.server.movieApiKey); +const popularTv = async () => { + try { + const response = await mdb.miscPopularTvs(); + return response; + } catch (error) { + helpers.handleError(error, 'popularTv'); + } +}; + const searchTv = async showName => { try { const response = await mdb.searchTv({ @@ -29,4 +38,4 @@ const similarTV = async showId => { } }; -export default {searchTv, similarTV}; +export default {searchTv, similarTV, popularTv}; diff --git a/server/services/plex/index.js b/server/services/plex/index.js index 96d91b6..b56a850 100644 --- a/server/services/plex/index.js +++ b/server/services/plex/index.js @@ -8,6 +8,10 @@ const getAuthToken = async (req, res) => { try { const {email, password, sonarrUrl, sonarrApiKey} = req.query; const plexToken = await auth.fetchToken(email, password); + console.log('plex token', plexToken.includes('401')); + if (plexToken.includes('401')) { + return res.json('Invalid username or password.'); + } const plexUrl = await auth.plexUrl(plexToken); const [rowsUpdate, updatedUser] = await models.User.update( {plexUrl, plexToken, sonarrUrl, sonarrApiKey}, @@ -16,6 +20,7 @@ const getAuthToken = async (req, res) => { return res.json(updatedUser); } catch (error) { + console.log('error in auth', error); return res.status(201).json(error.message); } };