diff --git a/client/src/components/Hero.js b/client/src/components/Hero.js index a1cd4c8..2afa15b 100644 --- a/client/src/components/Hero.js +++ b/client/src/components/Hero.js @@ -37,7 +37,7 @@ class Hero extends Component {
live_tvGet Started diff --git a/client/src/components/ImportLibrary.js b/client/src/components/ImportLibrary.js index 0f81b46..20b3be5 100644 --- a/client/src/components/ImportLibrary.js +++ b/client/src/components/ImportLibrary.js @@ -1,20 +1,70 @@ import React, {Component} from 'react'; -import {BrowserRouter, Route} from 'react-router-dom'; +import PropTypes from 'prop-types'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Typography from '@material-ui/core/Typography'; +import {withStyles} from '@material-ui/core/styles'; import {connect} from 'react-redux'; -import * as actions from '../actions'; +import styles from './css'; +import axios from 'axios'; class ImportLibrary extends Component { + importPlexLibrary = async params => { + const res = await axios.get('/api/plex/import/all'); + console.log(res); + return res; + }; + render() { + const {classes} = this.props; return (
-

Hello

+ + +
+
+
+ + PlexRex + + + Media recommendations based on your most watched Plex TV and + Movies. + +
+ +
+
+
+
+
); } } +ImportLibrary.propTypes = { + classes: PropTypes.object.isRequired, +}; + function mapStateToProps({auth}) { return {auth}; } -export default connect(mapStateToProps)(ImportLibrary); +export default connect(mapStateToProps)(withStyles(styles)(ImportLibrary)); diff --git a/client/src/components/MediaList.js b/client/src/components/MediaList.js new file mode 100644 index 0000000..4a47e8d --- /dev/null +++ b/client/src/components/MediaList.js @@ -0,0 +1,46 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {withStyles} from '@material-ui/core/styles'; +import {connect} from 'react-redux'; +import styles from './css'; +import axios from 'axios'; + +class MediaList extends Component { + componentDidMount() { + this.getMostWatched(); + } + getMostWatched = async params => { + const res = await axios.get('/api/recommend/most-watched'); + console.log(res); + return res; + }; + + render() { + const {classes} = this.props; + return ( + + ); + } +} + +MediaList.propTypes = { + classes: PropTypes.object.isRequired, +}; + +function mapStateToProps({auth}) { + return {auth}; +} + +export default connect(mapStateToProps)(withStyles(styles)(MediaList)); diff --git a/client/src/components/Plex.js b/client/src/components/Plex.js index a09ee74..f85b00c 100644 --- a/client/src/components/Plex.js +++ b/client/src/components/Plex.js @@ -1,9 +1,8 @@ import React, {Component} from 'react'; -import {BrowserRouter, Route} from 'react-router-dom'; import {connect} from 'react-redux'; -import * as actions from '../actions'; import PlexTokenForm from './PlexTokenForm'; import ImportLibrary from './ImportLibrary'; +import MediaList from './MediaList'; class Plex extends Component { render() { @@ -17,6 +16,7 @@ class Plex extends Component { return (
+
); } diff --git a/client/src/components/PlexTokenForm.js b/client/src/components/PlexTokenForm.js index ae15bf3..83cac1a 100644 --- a/client/src/components/PlexTokenForm.js +++ b/client/src/components/PlexTokenForm.js @@ -22,12 +22,6 @@ class PlexTokenForm extends React.Component { return res; }; - fetchSections = async params => { - const res = await axios.get('/plex/library/sections'); - console.log(res); - this.setState({section_data: res.data}); - }; - render() { const {classes} = this.props; if (!this.props.auth) { diff --git a/config/local.js b/config/local.js index ff57cf9..9e1f03d 100644 --- a/config/local.js +++ b/config/local.js @@ -4,6 +4,7 @@ const localConfig = { googleClientID: process.env.GOOGLE_CLIENT_ID, googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, cookieKey: process.env.COOKIE_KEY, + movieApiKey: process.env.MOVIE_API_KEY }; export default localConfig; diff --git a/config/production.js b/config/production.js index 51e8346..4d5b328 100644 --- a/config/production.js +++ b/config/production.js @@ -4,6 +4,7 @@ const prodConfig = { cookieKey: process.env.COOKIE_KEY, port: process.env.PORT, cookieKey: process.env.COOKIE_KEY, + movieApiKey: process.env.MOVIE_API_KEY, }; export default prodConfig; diff --git a/config/test.js b/config/test.js index fab6e66..1013f74 100644 --- a/config/test.js +++ b/config/test.js @@ -4,6 +4,7 @@ const testConfig = { googleClientID: process.env.GOOGLE_CLIENT_ID, googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, cookieKey: process.env.COOKIE_KEY, + movieApiKey: process.env.MOVIE_API_KEY, }; export default testConfig; diff --git a/package-lock.json b/package-lock.json index 8a950cc..5827c4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -599,12 +599,14 @@ "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -643,6 +645,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, "requires": { "remove-trailing-separator": "^1.0.1" } @@ -1686,8 +1689,7 @@ "cookiejar": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", - "dev": true + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" }, "cookies": { "version": "0.7.3", @@ -2512,8 +2514,7 @@ "formidable": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", - "dev": true + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" }, "forwarded": { "version": "0.1.2", @@ -2570,7 +2571,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2588,11 +2590,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2605,15 +2609,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2716,7 +2723,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2726,6 +2734,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2738,17 +2747,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2765,6 +2777,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2837,7 +2850,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2847,6 +2861,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2922,7 +2937,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2952,6 +2968,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2969,6 +2986,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3007,11 +3025,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -3110,12 +3130,14 @@ "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "optional": true }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -3920,6 +3942,11 @@ "invert-kv": "^2.0.0" } }, + "limits.js": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/limits.js/-/limits.js-0.1.4.tgz", + "integrity": "sha1-2rIe8ioXDPoO8eoBpKe+yWRrXoI=" + }, "lint-staged": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz", @@ -4457,6 +4484,15 @@ } } }, + "moviedb-promise": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/moviedb-promise/-/moviedb-promise-1.3.2.tgz", + "integrity": "sha512-391/dwm9OI6ufHUC9w5B2JN+etPIFRG07Y9CWNHAYskmtGbGNR1n352DOTXQF9BYdsOFYL3H4Kh4M1N5e1PSoQ==", + "requires": { + "limits.js": "^0.1.4", + "superagent": "^3.8.3" + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -9408,7 +9444,8 @@ "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "optional": true }, "is-glob": { "version": "2.0.1", @@ -10690,7 +10727,6 @@ "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, "requires": { "component-emitter": "^1.2.0", "cookiejar": "^2.1.0", diff --git a/package.json b/package.json index 7aca494..a34d603 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "http-proxy-middleware": "^0.19.1", "lodash": "^4.17.11", "morgan": "^1.9.1", + "moviedb-promise": "^1.3.2", "onchange": "^5.2.0", "passport": "^0.4.0", "passport-google-oauth20": "^2.0.0", diff --git a/server/controllers/recommend.controller.js b/server/controllers/recommend.controller.js new file mode 100644 index 0000000..5bec42a --- /dev/null +++ b/server/controllers/recommend.controller.js @@ -0,0 +1,8 @@ +import {Router} from 'express'; +import recommend from '../services/recommend'; + +const router = Router(); + +router.get('/most-watched', recommend.getMostWatched); + +export default router; diff --git a/server/db/migrations/20190224045315-create-plex-library.js b/server/db/migrations/20190224045315-create-plex-library.js index 3d4fb57..c4734cd 100644 --- a/server/db/migrations/20190224045315-create-plex-library.js +++ b/server/db/migrations/20190224045315-create-plex-library.js @@ -20,7 +20,7 @@ module.exports = { rating_key: { type: Sequelize.INTEGER, }, - metadata_path: { + poster_path: { type: Sequelize.STRING, }, summary: { diff --git a/server/db/models/plexlibrary.js b/server/db/models/plexlibrary.js index 307440d..d45feb1 100644 --- a/server/db/models/plexlibrary.js +++ b/server/db/models/plexlibrary.js @@ -6,7 +6,7 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING, views: DataTypes.INTEGER, rating_key: DataTypes.INTEGER, - metadata_path: DataTypes.STRING, + poster_path: DataTypes.STRING, summary: DataTypes.TEXT, rating: DataTypes.FLOAT, year: DataTypes.INTEGER, diff --git a/server/index.js b/server/index.js index c8e7d62..9c05c27 100644 --- a/server/index.js +++ b/server/index.js @@ -8,6 +8,7 @@ import keys from '../config'; import plex from './routes/plex.route'; import tdaw from './routes/tdaw.route'; import auth from './routes/auth.route'; +import recommend from './routes/recommend.route'; require('./services/auth/passport'); export default () => { @@ -36,6 +37,7 @@ export default () => { // Set up routes server.use('/api/plex', plex); server.use('/api/tdaw', tdaw); + server.use('/api/recommend', recommend); server.use('/auth', auth); server.use('/api/auth', auth); diff --git a/server/routes/recommend.route.js b/server/routes/recommend.route.js new file mode 100644 index 0000000..e34027c --- /dev/null +++ b/server/routes/recommend.route.js @@ -0,0 +1,9 @@ +import recommendController from '../controllers/recommend.controller'; + +const express = require('express'); + +const router = express.Router(); + +router.use(recommendController); + +export default router; diff --git a/server/services/plex/importData.js b/server/services/plex/importData.js index ef5e7b7..86520b8 100644 --- a/server/services/plex/importData.js +++ b/server/services/plex/importData.js @@ -1,20 +1,54 @@ import Promise from 'bluebird'; import plexApi from './plexApi'; import models from '../../db/models'; +import config from '../../../config'; +import MovieDb from 'moviedb-promise'; +import {Op} from 'sequelize'; +const mdb = new MovieDb(config.server.movieApiKey); -const importSections = async () => { +const importSections = async user => { const sections = await plexApi.getSections(); - const dbSections = await createSections(sections); + const dbSections = await createSections(sections, user); return dbSections; }; -const createSections = sections => { +const getTvPosters = async user => { + try { + console.log('Called Get Posters'); + const mostWatched = await models.PlexLibrary.findAll({ + where: {UserId: user.id, type: 'show', views: {[Op.gt]: 0}}, + }); + + console.log(mostWatched); + const imageUrls = await mostWatched.map(async show => { + console.log('title', show.title); + const res = await mdb.searchTv({query: show.title}); + console.log('request-movie', res.results[0].poster_path); + return models.PlexLibrary.update( + { + poster_path: res.results[0].poster_path, + }, + { + where: {title: show.title}, + }, + ); + }); + + console.log('mike--', imageUrls); + } catch (error) { + console.log(error); + return error.message; + } +}; + +const createSections = (sections, user) => { return Promise.map(sections, section => { return models.PlexSection.upsert( { title: section.title, type: section.type, key: section.key, + UserId: user.id, }, { where: { @@ -30,10 +64,10 @@ const createSections = sections => { }); }; -const importLibraries = async () => { +const importLibraries = async user => { const sections = await plexApi.getSections(); return Promise.map(sections, section => { - return importLibrary(section.key); + return importLibrary(section.key, user); }); }; @@ -41,7 +75,7 @@ const importMostWatched = async () => { // const sectionKeys = await models.PlexSection.findAll().then(sections => { // return sections.map(section => section.key.toString()); // }); - const sectionKeys = [1, 2, 3]; + const sectionKeys = [1, 2]; return Promise.map(sectionKeys, sectionKey => { return importMostWatchedData(sectionKey); }).catch(err => { @@ -55,12 +89,12 @@ const importMostWatchedData = async sectionKey => { return mostWatchedDbData; }; -const importLibrary = async sectionKey => { - console.log('section-key', sectionKey); +const importLibrary = async (sectionKey, user) => { + console.log('user--', user); const libraryData = await plexApi.getLibraryDataBySection({ sectionKey, }); - const dbLibraryData = await createLibrary(libraryData); + const dbLibraryData = await createLibrary(libraryData, user); return dbLibraryData; }; @@ -72,7 +106,7 @@ const updateLibrary = libraryData => { type: data.type, views: data.globalViewCount, rating_key: data.ratingKey, - metadata_path: data.key, + poster_path: data.key, summary: data.summary, rating: data.rating, year: data.year, @@ -89,11 +123,13 @@ const updateLibrary = libraryData => { }); }; -const createLibrary = libraryData => { +const createLibrary = (libraryData, user) => { + const userId = user.id; return Promise.map(libraryData, sectionLibraryData => { return models.PlexLibrary.upsert( { title: sectionLibraryData.title, + UserId: userId, type: sectionLibraryData.type, views: sectionLibraryData.views, rating_key: sectionLibraryData.ratingKey, @@ -112,4 +148,9 @@ const createLibrary = libraryData => { }).catch(err => console.log(err)); }; -export default {importSections, importLibraries, importMostWatched}; +export default { + importSections, + importLibraries, + importMostWatched, + getTvPosters, +}; diff --git a/server/services/plex/index.js b/server/services/plex/index.js index bf34582..417cd7f 100644 --- a/server/services/plex/index.js +++ b/server/services/plex/index.js @@ -73,10 +73,12 @@ const importMostWatched = async (req, res) => { }; const importAll = async (req, res) => { - await importData.importSections(); - await importData.importLibraries(); - const data = await importData.importMostWatched(); - res.json(data); + await importData.importSections(req.user); + await importData.importLibraries(req.user); + await importData.importMostWatched(req.user); + const images = await importData.getTvPosters(req.user); + + res.json(images); }; export default { diff --git a/server/services/recommend/index.js b/server/services/recommend/index.js index e69de29..b5c68ae 100644 --- a/server/services/recommend/index.js +++ b/server/services/recommend/index.js @@ -0,0 +1,20 @@ +import models from '../../db/models'; +import helpers from '../helpers'; +import {Op} from 'sequelize'; + +const getMostWatched = async (req, res) => { + try { + console.log('mike'); + const mostWatched = await models.PlexLibrary.findAll({ + where: {UserId: req.user.id, views: {[Op.gt]: 0}}, + }); + console.log('mostwatched', mostWatched); + res.json(mostWatched); + } catch (error) { + res.json(error.message); + } +}; + +export default { + getMostWatched, +}; diff --git a/test/server/services/plex/importData.test.js b/test/server/services/plex/importData.test.js index 77ad4f2..cb93571 100644 --- a/test/server/services/plex/importData.test.js +++ b/test/server/services/plex/importData.test.js @@ -3,7 +3,7 @@ import nock from 'nock'; import app from '../../../../index'; import importData from '../../../../server/services/plex/importData'; import models from '../../../../server/db/models'; -import { seed, truncate } from '../../../../server/db/scripts'; +import {seed, truncate} from '../../../../server/db/scripts'; import * as nocks from '../../../nocks'; describe('ImportData', () => { @@ -71,7 +71,7 @@ describe('ImportData', () => { data => data.dataValues.title === 'New Girl', ); newGirl[0].dataValues.views.should.eq(74); - newGirl[0].dataValues.metadata_path.should.eq( + newGirl[0].dataValues.poster_path.should.eq( '/library/metadata/5485/children', ); libraryMostWatched.should.be.length(56);