diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index ccf8b8a..67771c7 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -7,21 +7,28 @@ export const types = {
GET_MOST_WATCHED: 'get_most_watched',
ADD_SERIES: 'add_series',
CURRENT_SHOW: 'current_show',
+ FETCH_PIN: 'fetch_pin',
+ CHECK_PLEX_PIN: 'check_plex_pin',
};
export const setLoading = loading => dispatch => {
dispatch({type: types.SET_LOADING, payload: {loading: loading}});
};
-// Action Creators
export const fetchUser = () => async dispatch => {
const res = await axios.get('/api/auth/current_user');
dispatch({type: types.FETCH_USER, payload: res.data});
};
+export const fetchPin = () => async dispatch => {
+ const res = await axios.get('/api/plex/plex-pin');
+ dispatch({type: types.FETCH_PIN, payload: res.data});
+};
+
export const fetchMedia = () => async dispatch => {
dispatch({type: types.SET_LOADING, payload: true});
const res = await axios.get('/api/plex/import/all');
+ console.log('fetchMedia', res);
dispatch({type: types.SET_LOADING, payload: false});
dispatch({type: types.FETCH_MEDIA_RESPONSE, payload: res.data});
};
@@ -43,3 +50,37 @@ export const addSeries = params => async dispatch => {
: toast(res.data);
dispatch({type: types.ADD_SERIES, payload: res.data});
};
+
+const createPoller = (interval, initialDelay) => {
+ let timeoutId = null;
+ let poller = () => {};
+ return fn => {
+ window.clearTimeout(timeoutId);
+ poller = () => {
+ timeoutId = window.setTimeout(poller, 2000);
+ return fn();
+ };
+ if (initialDelay) {
+ return (timeoutId = window.setTimeout(poller, 2000));
+ }
+ return poller();
+ };
+};
+
+export const createPollingAction = (action, interval, initialDelay) => {
+ const poll = createPoller(action, initialDelay);
+ return () => (dispatch, getState) => poll(() => action(dispatch, getState));
+};
+
+export const checkPlexPin = createPollingAction(dispatch => {
+ axios.get('/api/plex/check-plex-pin').then(res => {
+ if (res.data) {
+ var highestTimeoutId = setTimeout(';');
+ for (var i = 0; i < highestTimeoutId; i++) {
+ clearTimeout(i);
+ }
+ }
+ console.log('action res', res);
+ dispatch({type: types.CHECK_PLEX_PIN, payload: res.data});
+ });
+}, 15000);
diff --git a/client/src/components/App.js b/client/src/components/App.js
index 659f2d9..6f58255 100644
--- a/client/src/components/App.js
+++ b/client/src/components/App.js
@@ -7,6 +7,7 @@ import ReactGA from 'react-ga';
import Header from './Header';
import Hero from './Hero';
import Plex from './plex/Plex';
+import PlexPin from './plex/PlexPin';
import SimilarList from './SimilarList';
import PopularList from './PopularList';
@@ -26,6 +27,7 @@ class App extends Component {
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+HeroSimple.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+function mapStateToProps({auth}) {
+ return {auth};
+}
+
+export default connect(mapStateToProps)(withStyles(styles)(HeroSimple));
diff --git a/client/src/components/plex/PlexPin.js b/client/src/components/plex/PlexPin.js
new file mode 100644
index 0000000..6173ef8
--- /dev/null
+++ b/client/src/components/plex/PlexPin.js
@@ -0,0 +1,76 @@
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+import {Link, Redirect} from 'react-router-dom';
+import HeroSimple from '../HeroSimple';
+import * as actions from '../../actions';
+import Typography from '@material-ui/core/Typography';
+
+class PlexPin extends Component {
+ async componentDidMount() {
+ await this.props.fetchPin();
+ const check = await this.props.checkPlexPin();
+ }
+ render() {
+ if (!this.props) {
+ return;
+ }
+ if (this.props.auth.plexToken) {
+ return ;
+ }
+ if (!this.props.auth.plexToken) {
+ console.log(this.props.auth);
+ return (
+
+
+
+
+
+
+ {this.props.auth.plexPin}
+
+
+
+ );
+ }
+ return (
+
+
+
+ sendGet Started
+
+
+
+ );
+ }
+}
+
+function mapStateToProps({auth}) {
+ console.log('auth state to prop', auth);
+ return {auth};
+}
+
+export default connect(
+ mapStateToProps,
+ actions,
+)(PlexPin);
diff --git a/client/src/css/materialize.css b/client/src/css/materialize.css
index ce3c612..aee7f97 100644
--- a/client/src/css/materialize.css
+++ b/client/src/css/materialize.css
@@ -25,6 +25,11 @@
justify-content: center;
}
+.code {
+ padding: 1rem;
+ border-radius: 1rem;
+}
+
.margin-bottom-button {
margin-bottom: 2em
}
diff --git a/client/src/reducers/authReducer.js b/client/src/reducers/authReducer.js
index f93f6c3..2402bed 100644
--- a/client/src/reducers/authReducer.js
+++ b/client/src/reducers/authReducer.js
@@ -1,10 +1,20 @@
import {types} from '../actions/index';
+export const initialState = {
+ loading: false,
+ plexPin: '',
+ user: '',
+};
+
export default function(state = {}, action) {
+ console.log('action - payload', action.payload);
switch (action.type) {
case types.FETCH_USER:
- console.log('actionpayload', action.payload);
return action.payload || false;
+ case types.FETCH_PIN:
+ return {...state, plexPin: action.payload};
+ case types.CHECK_PLEX_PIN:
+ return {...state, plexToken: action.payload};
default:
return state;
}
diff --git a/server/controllers/auth.controller.js b/server/controllers/auth.controller.js
index 2d298ff..341408e 100644
--- a/server/controllers/auth.controller.js
+++ b/server/controllers/auth.controller.js
@@ -14,7 +14,7 @@ router.get(
);
router.get('/google/callback', passport.authenticate('google'), (req, res) => {
- res.redirect('/most-watched');
+ res.redirect('/plex-pin');
});
router.get('/current_user', (req, res) => {
diff --git a/server/controllers/plex.controller.js b/server/controllers/plex.controller.js
index 146cc23..3eb5751 100644
--- a/server/controllers/plex.controller.js
+++ b/server/controllers/plex.controller.js
@@ -4,6 +4,8 @@ import plexService from '../services/plex';
const router = Router();
router.get('/token', plexService.getAuthToken);
+router.get('/plex-pin', plexService.getPlexPin);
+router.get('/check-plex-pin', plexService.checkPlexPin);
router.get('/users', plexService.getUsers);
diff --git a/server/db/migrations/20190224043920-create-user.js b/server/db/migrations/20190224043920-create-user.js
index 3e8d031..7976d9e 100644
--- a/server/db/migrations/20190224043920-create-user.js
+++ b/server/db/migrations/20190224043920-create-user.js
@@ -26,6 +26,9 @@ module.exports = {
plexToken: {
type: Sequelize.STRING,
},
+ plexPinId: {
+ type: Sequelize.STRING,
+ },
sonarrUrl: {
type: Sequelize.STRING,
},
diff --git a/server/db/models/user.js b/server/db/models/user.js
index a963dfd..9a02673 100644
--- a/server/db/models/user.js
+++ b/server/db/models/user.js
@@ -7,6 +7,7 @@ module.exports = (sequelize, DataTypes) => {
googleId: DataTypes.STRING,
email: {type: DataTypes.STRING, unique: true},
plexUrl: DataTypes.STRING,
+ plexPinId: DataTypes.STRING,
plexToken: DataTypes.STRING,
sonarrUrl: DataTypes.STRING,
sonarrApiKey: DataTypes.STRING,
diff --git a/server/services/moviedb/index.js b/server/services/moviedb/index.js
index eeabf50..2443499 100644
--- a/server/services/moviedb/index.js
+++ b/server/services/moviedb/index.js
@@ -12,8 +12,12 @@ const searchTv = async (req, res) => {
const popularTv = async (req, res) => {
const response = await movieDbApi.popularTv();
- const library = await sonarrService.getSeries(req.user);
- const jsonLibrary = JSON.parse(library);
+ // const library = await sonarrService.getSeries(req.user);
+ // const jsonLibrary = JSON.parse(library);
+ const jsonLibrary = await models.PlexLibrary.findAll({
+ userId: req.user.id,
+ type: 'show',
+ });
const libraryTitles = jsonLibrary.map(show => show.title.toLowerCase());
const filteredResponse = response.results.filter(
show => !libraryTitles.includes(show.name.toLowerCase()),
@@ -25,12 +29,13 @@ const similarTv = async (req, res) => {
const {showName} = req.query;
const searchResponse = await movieDbApi.searchTv(showName);
const similarResponse = await movieDbApi.similarTV(searchResponse.id);
- const library = await sonarrService.getSeries(req.user);
- const jsonLibrary = JSON.parse(library);
- // const library = await models.PlexLibrary.findAll({
- // userId: req.user.id,
- // type: 'show',
- // });
+ console.log('TCL: similarTv -> similarResponse', similarResponse);
+ // const library = await sonarrService.getSeries(req.user);
+ // const jsonLibrary = JSON.parse(library);
+ const jsonLibrary = await models.PlexLibrary.findAll({
+ userId: req.user.id,
+ type: 'show',
+ });
// Use Sonarr list instead
const libraryTitles = jsonLibrary.map(show => show.title.toLowerCase());
const filteredResponse = similarResponse.results.filter(
diff --git a/server/services/plex/auth.js b/server/services/plex/auth.js
index 144e86a..a31f249 100644
--- a/server/services/plex/auth.js
+++ b/server/services/plex/auth.js
@@ -2,6 +2,7 @@ import parser from 'xml2json';
import uuid from 'uuid';
import btoa from 'btoa';
import request from 'request-promise';
+import models from '../../db/models';
const rxAuthToken = /authenticationToken="([^"]+)"/;
@@ -36,19 +37,85 @@ const plexUrlParams = plexToken => ({
},
});
-const plexUrl = async plexToken => {
+const getPlexPin = async user => {
try {
- const res = await request.get(plexUrlParams(plexToken));
+ const params = {
+ url: 'https://plex.tv/pins.xml',
+ headers: {
+ 'X-Plex-Client-Identifier': user.googleId,
+ },
+ };
+ const res = await request.post(params);
const formattedResponse = JSON.parse(parser.toJson(res));
- const server = formattedResponse.MediaContainer.Server.filter(
- server => server.port === '32400',
- );
- console.log(server);
- return `http://${server[0].address}:${server[0].port}`;
+ return formattedResponse;
} catch (error) {
+ console.log(error);
return error.message;
}
};
-export default {fetchToken, plexUrl};
+const checkPlexPin = async (pinId, user) => {
+ try {
+ const params = {
+ url: `https://plex.tv/pins/${pinId}.xml`,
+ headers: {
+ 'X-Plex-Client-Identifier': user.googleId,
+ },
+ };
+ const res = await request.get(params);
+ const formattedResponse = JSON.parse(parser.toJson(res));
+ console.log(
+ 'TCL: checkPlexPin -> formattedResponse',
+ formattedResponse.pin.auth_token,
+ );
+ return formattedResponse.pin.auth_token;
+ } catch (error) {
+ console.log(error);
+ return error.message;
+ }
+};
+
+const getPlexUrl = async (user, token) => {
+ const params = {
+ url: `https://plex.tv/pms/:/ip`,
+ headers: {
+ 'X-Plex-Client-Identifier': user.googleId,
+ },
+ };
+ const plexUrl = await request.get(params);
+
+ const fullPlexUrl = await plexPort(token, plexUrl, user);
+ return fullPlexUrl;
+};
+
+const plexPort = async (plexToken, plexUrl, user) => {
+ try {
+ const res = await request.get(plexUrlParams(plexToken));
+ let formattedResponse = JSON.parse(parser.toJson(res)).MediaContainer
+ .Server;
+ if (!Array.isArray(formattedResponse)) {
+ formattedResponse = [formattedResponse];
+ }
+ console.log('formatted response', formattedResponse);
+ console.log('url--', plexUrl);
+ const server = formattedResponse.filter(
+ server => server.address.toString().trim() === plexUrl.toString().trim(),
+ );
+ console.log('server', server);
+ await models.User.update(
+ {
+ plexToken: plexToken.trim(),
+ plexUrl: `http://${server[0].address}:${server[0].port}`.trim(),
+ },
+ {where: {googleId: user.googleId}},
+ );
+ console.log('server--', server);
+ return `http://${server[0].address}:${server[0].port}`;
+ } catch (error) {
+ console.log(error.message);
+ return error.message;
+ }
+};
+
+export default {fetchToken, plexPort, getPlexPin, checkPlexPin, getPlexUrl};
diff --git a/server/services/plex/index.js b/server/services/plex/index.js
index b56a850..6c69498 100644
--- a/server/services/plex/index.js
+++ b/server/services/plex/index.js
@@ -3,6 +3,7 @@ import importData from './importData';
import auth from './auth';
import models from '../../db/models';
import helpers from '../helpers';
+import request from 'request-promise';
const getAuthToken = async (req, res) => {
try {
@@ -25,6 +26,36 @@ const getAuthToken = async (req, res) => {
}
};
+const getPlexPin = async (req, res) => {
+ try {
+ const pinRes = await auth.getPlexPin(req.user);
+ const plexPinId = pinRes.pin.id['$t'];
+ await models.User.update(
+ {plexPinId},
+ {where: {googleId: req.user.googleId}},
+ );
+ const pinCode = pinRes.pin.code;
+ return res.json(pinCode);
+ } catch (error) {
+ console.log('error in auth', error);
+ return res.status(201).json(error.message);
+ }
+};
+
+const checkPlexPin = async (req, res) => {
+ try {
+ const token = await auth.checkPlexPin(req.user.plexPinId, req.user);
+ if (token.nil) {
+ return res.json(null);
+ }
+ await auth.getPlexUrl(req.user, token);
+ return res.json(token);
+ } catch (error) {
+ console.log('error in auth', error);
+ return res.status(201).json(error.message);
+ }
+};
+
const getUsers = (req, res) => {
plexApi
.getUsers(req.user)
@@ -101,4 +132,6 @@ export default {
importLibraries,
importMostWatched,
importAll,
+ getPlexPin,
+ checkPlexPin,
};
diff --git a/server/services/plex/plexApi.js b/server/services/plex/plexApi.js
index 31196a1..5ce5434 100644
--- a/server/services/plex/plexApi.js
+++ b/server/services/plex/plexApi.js
@@ -78,6 +78,7 @@ const getSections = async function(user) {
try {
const urlParams = getSectionsUrlParams(user);
const getSectionsUrl = helpers.buildUrl(urlParams);
+ console.log('sec -url', getSectionsUrl);
const response = await helpers.request(getSectionsUrl);
console.log('mike-', response);
return response.MediaContainer.Directory;