From 265049798642380e4e31ff0eddc22a331152496d Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Mon, 4 Nov 2019 23:07:25 +0100 Subject: [PATCH] Tautulli will serve all the tautulli api calls. Mostly this will be used by user requests for getting stats related to the logged in user. WIP but currently watch time stats, plays by number of days, and view history has been implemented. TODO: - Error handling if tautulli request fails. - Filter the responses and/or limit them from tautulli - Handle parsing variable parameters from request --- seasoned_api/src/tautulli/tautulli.js | 48 +++++++++++ seasoned_api/src/webserver/app.js | 5 ++ .../webserver/controllers/user/viewHistory.js | 80 +++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 seasoned_api/src/tautulli/tautulli.js create mode 100644 seasoned_api/src/webserver/controllers/user/viewHistory.js diff --git a/seasoned_api/src/tautulli/tautulli.js b/seasoned_api/src/tautulli/tautulli.js new file mode 100644 index 0000000..311dfca --- /dev/null +++ b/seasoned_api/src/tautulli/tautulli.js @@ -0,0 +1,48 @@ +const fetch = require('node-fetch'); + +class Tautulli { + constructor(apiKey, ip, port) { + this.apiKey = apiKey; + this.ip = ip; + this.port = port; + } + + buildUrlWithCmdAndUserid(cmd, user_id) { + const url = new URL('api/v2', `http://${this.ip}:${this.port}`) + url.searchParams.append('apikey', this.apiKey) + url.searchParams.append('cmd', cmd) + url.searchParams.append('user_id', user_id) + + return url + } + + getPlaysByDays(plex_userid, days) { + const url = this.buildUrlWithCmdAndUserid('get_plays_by_date', plex_userid) + url.searchParams.append('time_range', days) + + return fetch(url.href) + .then(resp => resp.json()) + } + + watchTimeStats(plex_userid) { + const url = this.buildUrlWithCmdAndUserid('get_user_watch_time_stats', plex_userid) + url.searchParams.append('grouping', 0) + + return fetch(url.href) + .then(resp => resp.json()) +} + + viewHistory(plex_userid) { + const url = this.buildUrlWithCmdAndUserid('get_history', plex_userid) + + url.searchParams.append('start', 0) + url.searchParams.append('length', 50) + + console.log('fetching url', url.href) + + return fetch(url.href) + .then(resp => resp.json()) + } +} + +module.exports = Tautulli; diff --git a/seasoned_api/src/webserver/app.js b/seasoned_api/src/webserver/app.js index 10597f7..de38044 100644 --- a/seasoned_api/src/webserver/app.js +++ b/seasoned_api/src/webserver/app.js @@ -8,6 +8,7 @@ const mustHaveAccountLinkedToPlex = require('./middleware/mustHaveAccountLinkedT const configuration = require('src/config/configuration').getInstance(); const listController = require('./controllers/list/listController'); +const tautulli = require('./controllers/user/viewHistory.js'); // TODO: Have our raven router check if there is a value, if not don't enable raven. Raven.config(configuration.get('raven', 'DSN')).install(); @@ -58,6 +59,10 @@ router.get('/v1/user/search_history', mustBeAuthenticated, require('./controller router.get('/v1/user/requests', mustBeAuthenticated, require('./controllers/user/requests.js')); router.post('/v1/user/authenticate', mustBeAuthenticated, require('./controllers/user/authenticatePlexAccount.js')); +router.get('/v1/user/view_history', mustHaveAccountLinkedToPlex, tautulli.userViewHistoryController); +router.get('/v1/user/watch_time', mustHaveAccountLinkedToPlex, tautulli.watchTimeStatsController); +router.get('/v1/user/plays_by_day', mustHaveAccountLinkedToPlex, tautulli.getPlaysByDaysController); + /** * Seasoned */ diff --git a/seasoned_api/src/webserver/controllers/user/viewHistory.js b/seasoned_api/src/webserver/controllers/user/viewHistory.js new file mode 100644 index 0000000..f129cbe --- /dev/null +++ b/seasoned_api/src/webserver/controllers/user/viewHistory.js @@ -0,0 +1,80 @@ +const configuration = require('src/config/configuration').getInstance(); +const Tautulli = require('src/tautulli/tautulli'); +const tautulli = new Tautulli('', '', ); + +function handleError(error, res) { + const { status, message } = error; + + if (status && message) { + res.status(status).send({ success: false, message }) + } else { + console.log('caught view history controller error', error) + res.status(500).send({ message: 'An unexpected error occured while fetching view history'}) + } +} + +function watchTimeStatsController(req, res) { + const user = req.loggedInUser; + + tautulli.watchTimeStats(user.plex_userid) + .then(data => { + console.log('data', data, JSON.stringify(data.response.data)) + + return res.send({ + success: true, + data: data.response.data, + message: 'watch time successfully fetched from tautulli' + }) + }) +} + +function getPlaysByDaysController(req, res) { + const user = req.loggedInUser; + const days = req.query.days || undefined; + + if (days === undefined) { + return res.status(422).send({ + success: false, + message: "Missing parameter: days (number)" + }) + } + + tautulli.getPlaysByDays(user.plex_userid, days) + .then(data => res.send({ + success: true, + data: data.response.data + })) +} + + +function userViewHistoryController(req, res) { + const user = req.loggedInUser; + + console.log('user', user) + + + // TODO here we should check if we can init tau + // and then return 501 Not implemented + + tautulli.viewHistory(user.plex_userid) + .then(data => { + console.log('data', data, JSON.stringify(data.response.data.data)) + + + return res.send({ + success: true, + data: data.response.data.data, + message: 'view history successfully fetched from tautulli' + }) + }) + .catch(error => handleError(error)) + + + // const username = user.username; +} + +module.exports = { + watchTimeStatsController, + getPlaysByDaysController, + userViewHistoryController +};