diff --git a/index.js b/index.js index 5805ef7..9f614e6 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,8 @@ const ProgrammingService = require("./src/services/programming-service"); const ActiveChannelService = require('./src/services/active-channel-service') const ProgramPlayTimeDB = require('./src/dao/program-play-time-db') const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service') +const PlexProxyService = require('./src/services/plex-proxy-service') +const PlexServerDB = require('./src/dao/plex-server-db'); const onShutdown = require("node-graceful-shutdown").onShutdown; @@ -107,6 +109,9 @@ fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelServ customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') ); let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') ); let ffmpegSettingsService = new FfmpegSettingsService(db, unlockPath); +let plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db); +let plexProxyService = new PlexProxyService(plexServerDB); + async function initializeProgramPlayTimeDB() { try { @@ -251,6 +256,35 @@ channelService.on("channel-update", (data) => { let hdhr = HDHR(db, channelDB) let app = express() + +const responseInterceptor = ( + req, + res, + next +) => { + + let t0 = new Date().getTime(); + + const originalSend = res.send; + let responseSent = false; + console.log(`${req.method} ${req.url} ...`); + + res.send = function (body) { + + if (!responseSent) { + let t1 = new Date().getTime(); + let dt = t1 - t0; + console.log(`${req.method} ${req.url} ${res.statusCode} in ${dt}ms`); + responseSent = true; + } + + return originalSend.call(this, body); + }; + + next(); +}; +app.use(responseInterceptor); + eventService.setup(app); app.use( @@ -290,7 +324,7 @@ app.use('/favicon.svg', express.static( app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css'))) // API Routers -app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService)) +app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService, plexServerDB, plexProxyService)) app.use('/api/cache/images', cacheImageService.apiRouters()) app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome))) app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap))) diff --git a/src/api.js b/src/api.js index 7d21f4d..904574a 100644 --- a/src/api.js +++ b/src/api.js @@ -24,10 +24,9 @@ function safeString(object) { } module.exports = { router: api } -function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService ) { +function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService, plexServerDB, plexProxyService ) { let m3uService = _m3uService; const router = express.Router() - const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db); router.get('/api/version', async (req, res) => { try { @@ -216,7 +215,15 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe ); } }) - + router.get('/api/plex-server/:serverName64/:path(*)', async (req, res) => { + try { + let result = await plexProxyService.get(req.params.serverName64, req.params.path); + res.status(200).send(result); + } catch (err) { + console.error("Could not use plex proxy.", err); + res.status(404).send("Could not call plex server."); + } + }); // Channels router.get('/api/channels', async (req, res) => { diff --git a/src/dao/plex-server-db.js b/src/dao/plex-server-db.js index cd35d4c..a3a7000 100644 --- a/src/dao/plex-server-db.js +++ b/src/dao/plex-server-db.js @@ -15,6 +15,17 @@ class PlexServerDB this.showDB = showDB; } + async getPlexServerByName(name) { + let servers = this.db['plex-servers'].find() + let server = servers.sort( (a,b) => { return a.index - b.index } ) + .filter( (server) => name === server.name ) + [0]; + if (typeof(server) === "undefined") { + return null; + } + return server; + } + async fixupAllChannels(name, newServer) { let channelNumbers = await this.channelService.getAllChannelNumbers(); let report = await Promise.all( channelNumbers.map( async (i) => { diff --git a/src/services/plex-proxy-service.js b/src/services/plex-proxy-service.js new file mode 100644 index 0000000..8a14ba1 --- /dev/null +++ b/src/services/plex-proxy-service.js @@ -0,0 +1,30 @@ +const Plex = require('../plex.js') +const events = require('events') + +class PlexProxyService extends events.EventEmitter { + + constructor(plexServerDB) { + super(); + this.plexServerDB = plexServerDB; + } + + async get(serverName64, path) { + let plexServer = await getPlexServer(this.plexServerDB, serverName64); + let client = new Plex(plexServer); + return { MediaContainer: await client.Get("/" + path) }; + } +} + + + +async function getPlexServer(plexServerDB, serverName64) { + let serverKey = Buffer.from(serverName64, 'base64').toString('utf-8'); + let server = await plexServerDB.getPlexServerByName(serverKey); + if (server == null) { + throw Error("server not found"); + } + return server; + +} + +module.exports = PlexProxyService \ No newline at end of file diff --git a/web/directives/plex-library.js b/web/directives/plex-library.js index 153b2e2..0406d89 100644 --- a/web/directives/plex-library.js +++ b/web/directives/plex-library.js @@ -31,13 +31,7 @@ module.exports = function (plex, dizquetv, $timeout, commonProgramTools) { }); } scope.selectOrigin = function (origin) { - if ( origin.type === 'plex' ) { - scope.plexServer = origin.server; - updateLibrary(scope.plexServer); - } else { - scope.plexServer = undefined; - updateCustomShows(); - } + updateLibrary(origin); } scope._onFinish = (s, insertPoint) => { if (s.length > scope.limit) { @@ -99,20 +93,31 @@ module.exports = function (plex, dizquetv, $timeout, commonProgramTools) { "type" : "plex", "name" : `Plex - ${s.name}`, "server": s, + "loaded" : false, } } ); - scope.currentOrigin = scope.origins[0]; - scope.plexServer = scope.currentOrigin.server; scope.origins.push( { "type": "dizquetv", "name" : "dizqueTV - Custom Shows", + "loaded" : false, } ); - updateLibrary(scope.plexServer) + updateLibrary(scope.origins[0]) }) - let updateLibrary = async(server) => { + let updateLibrary = async(origin) => { + scope.currentOrigin = origin; + origin.loaded = false; + if ( origin.type !== 'plex' ) { + scope.plexServer = undefined; + await updateCustomShows(); + origin.loaded = true; + return; + } + let server = scope.currentOrigin.server; let lib = await plex.getLibrary(server); let play = await plex.getPlaylists(server); + scope.currentOrigin.loaded = true; + scope.plexServer = server; play.forEach( p => { p.type = "playlist"; diff --git a/web/directives/plex-settings.js b/web/directives/plex-settings.js index 53a51a2..83994fd 100644 --- a/web/directives/plex-settings.js +++ b/web/directives/plex-settings.js @@ -20,12 +20,9 @@ module.exports = function (plex, dizquetv, $timeout) { scope.servers = servers; if(servers) { for (let i = 0; i < scope.servers.length; i++) { - scope.servers[i].uiStatus = 0; scope.servers[i].backendStatus = 0; let t = (new Date()).getTime(); - scope.servers[i].uiPending = t; scope.servers[i].backendPending = t; - scope.refreshUIStatus(t, i); scope.refreshBackendStatus(t, i); } } @@ -51,22 +48,6 @@ module.exports = function (plex, dizquetv, $timeout) { scope.refreshServerList(); } - scope.isAnyUIBad = () => { - let t = (new Date()).getTime(); - if(scope.servers) { - for (let i = 0; i < scope.servers.length; i++) { - let s = scope.servers[i]; - if ( - (s.uiStatus == -1) - || ( (s.uiStatus == 0) && (s.uiPending + 30000 < t) ) - ) { - return true; - } - } - } - return false; - }; - scope.isAnyBackendBad = () => { let t = (new Date()).getTime(); if(scope.servers) { @@ -84,15 +65,6 @@ module.exports = function (plex, dizquetv, $timeout) { }; - scope.refreshUIStatus = async (t, i) => { - let s = await plex.check(scope.servers[i]); - if (scope.servers[i].uiPending == t) { - // avoid updating for a previous instance of the row - scope.servers[i].uiStatus = s; - } - scope.$apply(); - }; - scope.refreshBackendStatus = async (t, i) => { let s = await dizquetv.checkExistingPlexServer(scope.servers[i].name); if (scope.servers[i].backendPending == t) { diff --git a/web/public/locales/en/main.json b/web/public/locales/en/main.json index 3bf946d..a067645 100644 --- a/web/public/locales/en/main.json +++ b/web/public/locales/en/main.json @@ -19,12 +19,10 @@ "minutes_to_sign_plex": "You have 2 minutes to sign into your Plex Account.", "name": "Name", "uri": "URI", - "ui_route": "UI Route", - "backend_route": "Backend Route", + "routeStatus": "Route Status", "ok": "ok", "error": "error", - "ui_bad": "If a Plex server configuration has problems with the UI route, the channel editor won't be able to access its content.", - "backend_bad": "If a Plex server configuration has problems with the backend route, dizqueTV won't be able to play its content.", + "backend_bad": "A route problem means the dizqueTV server can't establish a connection with the configured Plex server.", "plex_transcoder_settings": "Plex Transcoder Settings", "update": "Update", "reset_options": "Reset Options", diff --git a/web/public/templates/plex-library.html b/web/public/templates/plex-library.html index adc2f58..74d0a78 100644 --- a/web/public/templates/plex-library.html +++ b/web/public/templates/plex-library.html @@ -43,7 +43,13 @@ Content: -
{{serverError}}
{{'settings_server.ui_bad' | i18next}}
-{{'settings_server.server_bad' | i18next}}
diff --git a/web/services/dizquetv.js b/web/services/dizquetv.js index 4e8b8f6..396ece5 100644 --- a/web/services/dizquetv.js +++ b/web/services/dizquetv.js @@ -49,6 +49,15 @@ module.exports = function ($http, $q) { }); return d.data; }, + getFromPlexProxy: async (serverName, path) => { + let serverName64 = Buffer.from(serverName, 'utf-8').toString('base64'); + let tmp = await ($http({ + method: 'GET', + url : `api/plex-server/${serverName64}${path}`, + headers: { "Cache-Control": "no-cache"}, + })) + return tmp.data.MediaContainer; + }, getPlexSettings: () => { return $http.get('/api/plex-settings').then((d) => { return d.data }) }, diff --git a/web/services/plex.js b/web/services/plex.js index 18913bc..f36734f 100644 --- a/web/services/plex.js +++ b/web/services/plex.js @@ -1,6 +1,6 @@ const Plex = require('../../src/plex'); -module.exports = function ($http, $window, $interval) { +module.exports = function (dizquetv, $http, $window, $interval) { let exported = { login: async () => { const headers = { @@ -108,14 +108,13 @@ module.exports = function ($http, $window, $interval) { }, getLibrary: async (server) => { - var client = new Plex(server) - const res = await client.Get('/library/sections') + const res = await dizquetv.getFromPlexProxy(server.name, '/library/sections') var sections = [] for (let i = 0, l = typeof res.Directory !== 'undefined' ? res.Directory.length : 0; i < l; i++) if (res.Directory[i].type === 'movie' || res.Directory[i].type === 'show' || res.Directory[i].type === 'artist' ) { var genres = [] if (res.Directory[i].type === 'movie') { - const genresRes = await client.Get(`/library/sections/${res.Directory[i].key}/genre`) + const genresRes = await dizquetv.getFromPlexProxy(server.name,`/library/sections/${res.Directory[i].key}/genre`) for (let q = 0, k = typeof genresRes.Directory !== 'undefined' ? genresRes.Directory.length : 0; q < k; q++) { if (genresRes.Directory[q].type === 'genre') { genres.push({ @@ -138,8 +137,7 @@ module.exports = function ($http, $window, $interval) { return sections }, getPlaylists: async (server) => { - var client = new Plex(server) - const res = await client.Get('/playlists') + const res = await dizquetv.getFromPlexProxy(server.name, '/playlists'); var playlists = [] for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++) if ( @@ -157,8 +155,7 @@ module.exports = function ($http, $window, $interval) { return playlists }, getStreams: async (server, key) => { - var client = new Plex(server) - return client.Get(key).then((res) => { + return dizquetv.getFromPlexProxy(server.name, key).then((res) => { let streams = res.Metadata[0].Media[0].Part[0].Stream for (let i = 0, l = streams.length; i < l; i++) { if (typeof streams[i].key !== 'undefined') { @@ -169,9 +166,9 @@ module.exports = function ($http, $window, $interval) { }) }, getNested: async (server, lib, includeCollections, errors) => { - var client = new Plex(server) + const key = lib.key - const res = await client.Get(key) + const res = await dizquetv.getFromPlexProxy(server.name, key) const size = (typeof(res.Metadata) !== 'undefined') ? res.Metadata.length : 0; var nested = [] @@ -191,7 +188,8 @@ module.exports = function ($http, $window, $interval) { albumKeys = Object.keys( albumKeys ); await Promise.all( albumKeys.map( async(albumKey) => { try { - let album = await client.Get(albumKey); + let album = await dizquetv.getFromPlexProxy( + server.name, albumKey); if ( (typeof(album)!=='undefined') && album.size == 1) { album = album.Metadata[0]; } @@ -287,7 +285,7 @@ module.exports = function ($http, $window, $interval) { let k = res.librarySectionID; k = `/library/sections/${k}/collections`; - let collections = await client.Get(k); + let collections = await dizquetv.getFromPlexProxy(server.name, k); if ( typeof(collections.Metadata) === 'undefined') { collections.Metadata = []; }