From 78e7b8d1a97780d60360a6278b4b3cf693e795f9 Mon Sep 17 00:00:00 2001 From: vexorian Date: Sat, 22 Aug 2020 18:53:57 -0400 Subject: [PATCH 1/6] Prepare 0.0.65 development --- README.md | 2 +- src/constants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70cce27..688407e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# dizqueTV 0.0.64-prerelease +# dizqueTV 0.0.65-prerelease ![Discord](https://img.shields.io/discord/711313431457693727?logo=discord&logoColor=fff&style=flat-square) ![GitHub top language](https://img.shields.io/github/languages/top/vexorian/dizquetv?logo=github&style=flat-square) ![Docker Pulls](https://img.shields.io/docker/pulls/vexorian/dizquetv?logo=docker&logoColor=fff&style=flat-square) Create live TV channel streams from media on your Plex servers. diff --git a/src/constants.js b/src/constants.js index 1b3f932..03ce000 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,5 +2,5 @@ module.exports = { SLACK: 9999, TVGUIDE_MAXIMUM_PADDING_LENGTH_MS: 30*60*1000, - VERSION_NAME: "0.0.64-prerelease" + VERSION_NAME: "0.0.65-prerelease" } From 2859c90b2c4a8f1a69269db7f430757c6e7071e1 Mon Sep 17 00:00:00 2001 From: Jason Baker Date: Sat, 22 Aug 2020 15:22:38 -0700 Subject: [PATCH 2/6] Implement Remove Show(s) Channel Config UI --- web/app.js | 1 + web/directives/channel-config.js | 17 ++++++++++- web/directives/remove-shows.js | 29 ++++++++++++++++++ web/public/templates/channel-config.html | 15 +++++++--- web/public/templates/remove-shows.html | 38 ++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 web/directives/remove-shows.js create mode 100644 web/public/templates/remove-shows.html diff --git a/web/app.js b/web/app.js index 131c284..57904f9 100644 --- a/web/app.js +++ b/web/app.js @@ -17,6 +17,7 @@ app.directive('plexLibrary', require('./directives/plex-library')) app.directive('programConfig', require('./directives/program-config')) app.directive('offlineConfig', require('./directives/offline-config')) app.directive('frequencyTweak', require('./directives/frequency-tweak')) +app.directive('removeShows', require('./directives/remove-shows')) app.directive('plexServerEdit', require('./directives/plex-server-edit')) app.directive('channelConfig', require('./directives/channel-config')) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index d068789..a8a0f30 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -278,7 +278,22 @@ module.exports = function ($timeout, $location) { updateChannelDuration() } - + scope.startRemoveShows = () => { + scope._removablePrograms = scope.channel.programs + .map(program => program.showTitle) + .reduce((dedupedArr, showTitle) => { + if (!dedupedArr.includes(showTitle)) { + dedupedArr.push(showTitle) + } + return dedupedArr + }, []) + .filter(showTitle => !!showTitle); + scope._deletedProgramNames = []; + } + scope.removeShows = (deletedShowNames) => { + const p = scope.channel.programs; + scope.channel.programs = p.filter(program => deletedShowNames.indexOf(program.showTitle) === -1); + } scope.describeFallback = () => { if (scope.channel.offlineMode === 'pic') { diff --git a/web/directives/remove-shows.js b/web/directives/remove-shows.js new file mode 100644 index 0000000..33efcd5 --- /dev/null +++ b/web/directives/remove-shows.js @@ -0,0 +1,29 @@ +module.exports = function ($timeout) { + return { + restrict: 'E', + templateUrl: 'templates/remove-shows.html', + replace: true, + scope: { + programTitles: "=programTitles", + visible: "=visible", + onDone: "=onDone", + deleted: "=deleted" + }, + link: function (scope, element, attrs) { + scope.toggleShowDeletion = (programTitle) => { + const deletedIdx = scope.deleted.indexOf(programTitle); + if (deletedIdx === -1) { + scope.deleted.push(programTitle); + } else { + scope.deleted.splice(deletedIdx, 1); + } + } + scope.finished = () => { + const d = scope.deleted; + scope.programTitles = null; + scope.deleted = null; + scope.onDone(d); + } + } + }; +} diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index 83cfd0c..17f50f6 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -159,6 +159,9 @@
Remove Specials

Removes any specials from the schedule. Specials are episodes with season "00".

+
Remove Show(s)
+

Allows you to pick specific shows to remove from your channel.

+
Remove All

Wipes out the schedule so that you can start over.

@@ -250,16 +253,19 @@
-
+
-
+
-
+
-
+
+ +
+
@@ -347,6 +353,7 @@ +
diff --git a/web/public/templates/remove-shows.html b/web/public/templates/remove-shows.html new file mode 100644 index 0000000..0dc7dbf --- /dev/null +++ b/web/public/templates/remove-shows.html @@ -0,0 +1,38 @@ +
+ +
\ No newline at end of file From 21381766894cdaec71141d6d4dcfbf1a2aa024e9 Mon Sep 17 00:00:00 2001 From: vexorian Date: Mon, 24 Aug 2020 22:38:22 -0400 Subject: [PATCH 3/6] Channel redirects + 'Channel At night' --- index.js | 2 +- src/channel-cache.js | 8 +- src/dao/channel-db.js | 29 +++-- src/helperFuncs.js | 14 +++ src/plex-player.js | 7 +- src/program-player.js | 2 +- src/video.js | 92 ++++++++++++-- web/app.js | 1 + web/directives/channel-config.js | 138 +++++++++++++++++---- web/directives/channel-redirect.js | 85 +++++++++++++ web/public/templates/channel-config.html | 38 +++++- web/public/templates/channel-redirect.html | 35 ++++++ web/public/templates/plex-server-edit.html | 2 +- 13 files changed, 399 insertions(+), 54 deletions(-) create mode 100644 web/directives/channel-redirect.js create mode 100644 web/public/templates/channel-redirect.html diff --git a/index.js b/index.js index 87411a3..20d57bb 100644 --- a/index.js +++ b/index.js @@ -131,7 +131,7 @@ app.use('/images', express.static(path.join(process.env.DATABASE, 'images'))) app.use(express.static(path.join(__dirname, 'web/public'))) app.use('/images', express.static(path.join(process.env.DATABASE, 'images'))) app.use(api.router(db, channelDB, xmltvInterval)) -app.use(video.router(db)) +app.use(video.router( channelDB, db)) app.use(hdhr.router) app.listen(process.env.PORT, () => { console.log(`HTTP server running on port: http://*:${process.env.PORT}`) diff --git a/src/channel-cache.js b/src/channel-cache.js index beacc58..681e99a 100644 --- a/src/channel-cache.js +++ b/src/channel-cache.js @@ -9,8 +9,12 @@ async function getChannelConfig(channelDB, channelId) { if ( typeof(configCache[channelId]) === 'undefined') { let channel = await channelDB.getChannel(channelId) - //console.log("channel=" + JSON.stringify(channel) ); - configCache[channelId] = [channel]; + if (channel == null) { + configCache[channelId] = []; + } else { + //console.log("channel=" + JSON.stringify(channel) ); + configCache[channelId] = [channel]; + } } //console.log("channel=" + JSON.stringify(configCache[channelId]).slice(0,200) ); return configCache[channelId]; diff --git a/src/dao/channel-db.js b/src/dao/channel-db.js index 5684d05..ac81bf4 100644 --- a/src/dao/channel-db.js +++ b/src/dao/channel-db.js @@ -9,18 +9,23 @@ class ChannelDB { async getChannel(number) { let f = path.join(this.folder, `${number}.json` ); - return await new Promise( (resolve, reject) => { - fs.readFile(f, (err, data) => { - if (err) { - return reject(err); - } - try { - resolve( JSON.parse(data) ) - } catch (err) { - reject(err); - } - }) - }); + try { + return await new Promise( (resolve, reject) => { + fs.readFile(f, (err, data) => { + if (err) { + return reject(err); + } + try { + resolve( JSON.parse(data) ) + } catch (err) { + reject(err); + } + }) + }); + } catch (err) { + console.error(err); + return null; + } } async saveChannel(number, json) { diff --git a/src/helperFuncs.js b/src/helperFuncs.js index c8259f9..f5b8068 100644 --- a/src/helperFuncs.js +++ b/src/helperFuncs.js @@ -42,6 +42,20 @@ function createLineup(obj, channel, isFirst) { let lineup = [] + if ( typeof(activeProgram.err) !== 'undefined') { + let remaining = activeProgram.duration - timeElapsed; + lineup.push( { + type: 'offline', + title: 'Error', + err: activeProgram.err, + streamDuration: remaining, + duration: remaining, + start: 0 + }) + return lineup; + } + + if (activeProgram.isOffline === true) { //offline case let remaining = activeProgram.duration - timeElapsed; diff --git a/src/plex-player.js b/src/plex-player.js index 1fb2bf3..baf5c9c 100644 --- a/src/plex-player.js +++ b/src/plex-player.js @@ -9,6 +9,7 @@ const PlexTranscoder = require('./plexTranscoder') const EventEmitter = require('events'); const helperFuncs = require('./helperFuncs') const FFMPEG = require('./ffmpeg') +const constants = require('./constants'); let USED_CLIENTS = {}; @@ -60,8 +61,10 @@ class PlexPlayer { let ffmpeg = new FFMPEG(ffmpegSettings, channel); // Set the transcoder options this.ffmpeg = ffmpeg; let streamDuration; - if (typeof(streamDuration)!=='undefined') { - streamDuration = lineupItem.streamDuration / 1000; + if (typeof(lineupItem.streamDuration)!=='undefined') { + if (lineupItem.start + lineupItem.streamDuration + constants.SLACK < lineupItem.duration) { + streamDuration = lineupItem.streamDuration / 1000; + } } let deinterlace = ffmpegSettings.enableFFMPEGTranscoding; //for now it will always deinterlace when transcoding is enabled but this is sub-optimal diff --git a/src/program-player.js b/src/program-player.js index 30e1283..576cff9 100644 --- a/src/program-player.js +++ b/src/program-player.js @@ -34,7 +34,7 @@ class ProgramPlayer { // people might want the codec normalization to stay because of player support context.ffmpegSettings.normalizeResolution = false; } - if (program.err instanceof Error) { + if ( typeof(program.err) !== 'undefined') { console.log("About to play error stream"); this.delegate = new OfflinePlayer(true, context); } else if (program.type === 'loading') { diff --git a/src/video.js b/src/video.js index 84bd32c..cf5144c 100644 --- a/src/video.js +++ b/src/video.js @@ -9,7 +9,7 @@ const channelCache = require('./channel-cache') module.exports = { router: video } -function video(db) { +function video( channelDB , db) { var router = express.Router() router.get('/setup', (req, res) => { @@ -49,7 +49,7 @@ function video(db) { return } let number = parseInt(req.query.channel, 10); - let channel = await channelCache.getChannelConfig(db, number); + let channel = await channelCache.getChannelConfig(channelDB, number); if (channel.length === 0) { res.status(500).send("Channel doesn't exist") return @@ -118,7 +118,7 @@ function video(db) { } let m3u8 = (req.query.m3u8 === '1'); let number = parseInt(req.query.channel); - let channel = await channelCache.getChannelConfig(db, number); + let channel = await channelCache.getChannelConfig(channelDB, number); if (channel.length === 0) { res.status(404).send("Channel doesn't exist") @@ -150,6 +150,11 @@ function video(db) { // Get video lineup (array of video urls with calculated start times and durations.) let t0 = (new Date()).getTime(); let lineupItem = channelCache.getCurrentLineupItem( channel.number, t0); + let prog = null; + let brandChannel = channel; + let redirectChannels = []; + let upperBounds = []; + if (isLoading) { lineupItem = { type: 'loading', @@ -158,8 +163,57 @@ function video(db) { start: 0, }; } else if (lineupItem == null) { - let prog = helperFuncs.getCurrentProgramAndTimeElapsed(t0, channel) + prog = helperFuncs.getCurrentProgramAndTimeElapsed(t0, channel); + + while (true) { + redirectChannels.push( brandChannel ); + upperBounds.push( prog.program.duration - prog.timeElapsed ); + if ( !(prog.program.isOffline) || (prog.program.type != 'redirect') ) { + break; + } + channelCache.recordPlayback( brandChannel.number, t0, { + /*type: 'offline',*/ + title: 'Error', + err: Error("Recursive channel redirect found"), + duration : 60000, + start: 0, + }); + + + + let newChannelNumber= prog.program.channel; + let newChannel = await channelCache.getChannelConfig(channelDB, newChannelNumber); + + if (newChannel.length == 0) { + let err = Error("Invalid redirect to a channel that doesn't exist"); + console.error("Invalid redirect to channel that doesn't exist.", err); + prog = { + program: { + isOffline: true, + err: err, + duration : 60000, + }, + timeElapsed: 0, + } + continue; + } + newChannel = newChannel[0]; + brandChannel = newChannel; + lineupItem = channelCache.getCurrentLineupItem( newChannel.number, t0); + if (lineupItem != null) { + lineupItem = JSON.parse( JSON.stringify(lineupItem)) ; + break; + } else { + prog = helperFuncs.getCurrentProgramAndTimeElapsed(t0, newChannel); + } + } + } + if (lineupItem == null) { + if (prog == null) { + res.status(500).send("server error"); + throw Error("Shouldn't prog be non-null?"); + } if (prog.program.isOffline && channel.programs.length == 1) { //there's only one program and it's offline. So really, the channel is //permanently offline, it doesn't matter what duration was set @@ -180,15 +234,33 @@ function video(db) { if ( (prog == null) || (typeof(prog) === 'undefined') || (prog.program == null) || (typeof(prog.program) == "undefined") ) { throw "No video to play, this means there's a serious unexpected bug or the channel db is corrupted." } - let lineup = helperFuncs.createLineup(prog, channel, isFirst) + let lineup = helperFuncs.createLineup(prog, brandChannel, isFirst) lineupItem = lineup.shift() } - + + if ( !isLoading && (lineupItem != null) ) { + let upperBound = 1000000000; + //adjust upper bounds and record playbacks + for (let i = redirectChannels.length-1; i >= 0; i--) { + lineupItem = JSON.parse( JSON.stringify(lineupItem )); + let u = upperBounds[i]; + if (typeof(u) !== 'undefined') { + let u2 = upperBound; + if ( typeof(lineupItem.streamDuration) !== 'undefined') { + u2 = Math.min(u2, lineupItem.streamDuration); + } + lineupItem.streamDuration = Math.min(u2, u); + upperBound = lineupItem.streamDuration; + } + channelCache.recordPlayback( redirectChannels[i].number, t0, lineupItem ); + } + } + console.log("========================================================="); console.log("! Start playback"); console.log(`! Channel: ${channel.name} (${channel.number})`); - if (typeof(lineupItem) === 'undefined') { + if (typeof(lineupItem.title) === 'undefined') { lineupItem.title = 'Unknown'; } console.log(`! Title: ${lineupItem.title}`); @@ -206,7 +278,7 @@ function video(db) { let playerContext = { lineupItem : lineupItem, ffmpegSettings : ffmpegSettings, - channel: channel, + channel: brandChannel, db: db, m3u8: m3u8, } @@ -267,7 +339,7 @@ function video(db) { } let channelNum = parseInt(req.query.channel, 10) - let channel = await channelCache.getChannelConfig(db, channelNum ); + let channel = await channelCache.getChannelConfig(channelDB, channelNum ); if (channel.length === 0) { res.status(500).send("Channel doesn't exist") return @@ -312,7 +384,7 @@ function video(db) { } let channelNum = parseInt(req.query.channel, 10) - let channel = await channelCache.getChannelConfig(db, channelNum ); + let channel = await channelCache.getChannelConfig(channelDB, channelNum ); if (channel.length === 0) { res.status(500).send("Channel doesn't exist") return diff --git a/web/app.js b/web/app.js index 57904f9..9360f62 100644 --- a/web/app.js +++ b/web/app.js @@ -18,6 +18,7 @@ app.directive('programConfig', require('./directives/program-config')) app.directive('offlineConfig', require('./directives/offline-config')) app.directive('frequencyTweak', require('./directives/frequency-tweak')) app.directive('removeShows', require('./directives/remove-shows')) +app.directive('channelRedirect', require('./directives/channel-redirect')) app.directive('plexServerEdit', require('./directives/plex-server-edit')) app.directive('channelConfig', require('./directives/channel-config')) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index a8a0f30..bb0acb9 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -1,9 +1,10 @@ -module.exports = function ($timeout, $location) { +module.exports = function ($timeout, $location, dizquetv) { return { restrict: 'E', templateUrl: 'templates/channel-config.html', replace: true, scope: { + visible: "=visible", channels: "=channels", channel: "=channel", onDone: "=onDone" @@ -93,6 +94,11 @@ module.exports = function ($timeout, $location) { updateChannelDuration(); setTimeout( () => { scope.showRotatedNote = true }, 1, 'funky'); } + scope._selectedRedirect = { + isOffline : true, + type : "redirect", + duration : 60*60*1000, + } scope.finshedProgramEdit = (program) => { scope.channel.programs[scope.selectedProgram] = program @@ -151,7 +157,7 @@ module.exports = function ($timeout, $location) { let newProgs = [] let progs = scope.channel.programs for (let i = 0, l = progs.length; i < l; i++) { - if (progs[i].type === 'movie') { + if ( progs[i].isOffline || (progs[i].type === 'movie') ) { movies.push(progs[i]) } else { if (typeof shows[progs[i].showTitle] === 'undefined') @@ -241,7 +247,9 @@ module.exports = function ($timeout, $location) { let tmpProgs = {} let progs = scope.channel.programs for (let i = 0, l = progs.length; i < l; i++) { - if (progs[i].type === 'movie') { + if ( progs[i].type ==='redirect' ) { + tmpProgs['_redirect ' + progs[i].channel + ' _ '+ progs[i].duration ] = progs[i]; + } else if (progs[i].type === 'movie') { tmpProgs[progs[i].title + progs[i].durationStr] = progs[i] } else { tmpProgs[progs[i].showTitle + '-' + progs[i].season + '-' + progs[i].episode] = progs[i] @@ -258,7 +266,7 @@ module.exports = function ($timeout, $location) { let tmpProgs = [] let progs = scope.channel.programs for (let i = 0, l = progs.length; i < l; i++) { - if (progs[i].isOffline !== true) { + if ( (progs[i].isOffline !== true) || (progs[i].type === 'redirect') ) { tmpProgs.push(progs[i]); } } @@ -278,9 +286,17 @@ module.exports = function ($timeout, $location) { updateChannelDuration() } + scope.getShowTitle = (program) => { + if (program.isOffline && program.type == 'redirect') { + return `Redirect to channel ${program.channel}`; + } else { + return program.showTitle; + } + } + scope.startRemoveShows = () => { scope._removablePrograms = scope.channel.programs - .map(program => program.showTitle) + .map(scope.getShowTitle) .reduce((dedupedArr, showTitle) => { if (!dedupedArr.includes(showTitle)) { dedupedArr.push(showTitle) @@ -292,7 +308,9 @@ module.exports = function ($timeout, $location) { } scope.removeShows = (deletedShowNames) => { const p = scope.channel.programs; - scope.channel.programs = p.filter(program => deletedShowNames.indexOf(program.showTitle) === -1); + let set = {}; + deletedShowNames.forEach( (a) => set[a] = true ); + scope.channel.programs = p.filter( (a) => (set[scope.getShowTitle(a)]!==true) ); } scope.describeFallback = () => { @@ -312,31 +330,45 @@ module.exports = function ($timeout, $location) { scope.programSquareStyle = (program) => { let background =""; - if (program.isOffline) { + if ( (program.isOffline) && (program.type !== 'redirect') ) { background = "rgb(255, 255, 255)"; } else { let r = 0, g = 0, b = 0, r2=0, g2=0,b2=0; - let i = 0; let angle = 45; let w = 3; - if (program.type === 'episode') { + if (program.type === 'redirect') { + angle = 0; + w = 4 + (program.channel % 10); + let c = (program.channel * 100019); + //r = 255, g = 0, b = 0; + //r2 = 0, g2 = 0, b2 = 255; + + r = ( (c & 3) * 77 ); + g = ( ( (c >> 1) & 3) * 77 ); + b = ( ( (c >> 2) & 3) * 77 ); + r2 = ( ( (c >> 5) & 3) * 37 ); + g2 = ( ( (c >> 3) & 3) * 37 ); + b2 = ( ( (c >> 4) & 3) * 37 ); + + } else if (program.type === 'episode') { let h = Math.abs(scope.getHashCode(program.showTitle, false)); let h2 = Math.abs(scope.getHashCode(program.showTitle, true)); r = h % 256; g = (h / 256) % 256; b = (h / (256*256) ) % 256; - i = h % 360; r2 = (h2 / (256*256) ) % 256; g2 = (h2 / (256*256) ) % 256; b2 = (h2 / (256*256) ) % 256; - angle = -90 + h % 180 + angle = (360 - 90 + h % 180) % 360; + if ( angle >= 350 || angle < 10 ) { + angle += 53; + } } else { r = 10, g = 10, b = 10; r2 = 245, g2 = 245, b2 = 245; angle = 45; w = 6; } - let rgb1 = "rgb("+ r + "," + g + "," + b +")"; let rgb2 = "rgb("+ r2 + "," + g2 + "," + b2 +")" background = "repeating-linear-gradient( " + angle + "deg, " + rgb1 + ", " + rgb1 + " " + w + "px, " + rgb2 + " " + w + "px, " + rgb2 + " " + (w*2) + "px)"; @@ -376,7 +408,7 @@ module.exports = function ($timeout, $location) { return hash; } - scope.nightChannel = (a, b) => { + scope.nightChannel = (a, b, ch) => { let o =(new Date()).getTimezoneOffset() * 60 * 1000; let m = 24*60*60*1000; a = (m + a * 60 * 60 * 1000 + o) % m; @@ -405,6 +437,8 @@ module.exports = function ($timeout, $location) { { duration: d, isOffline: true, + channel: ch, + type: (typeof(ch) === 'undefined') ? undefined: "redirect", } ) t += d; @@ -419,6 +453,8 @@ module.exports = function ($timeout, $location) { { duration: d, isOffline: true, + type: (typeof(ch) === 'undefined') ? undefined: "redirect", + channel: ch, } ) } @@ -433,7 +469,7 @@ module.exports = function ($timeout, $location) { let tired = 0; for (let i = 0, l = scope.channel.programs.length; i <= l; i++) { let prog = scope.channel.programs[i % l]; - if (prog.isOffline) { + if (prog.isOffline && prog.type != 'redirect') { tired = 0; } else { if (tired + prog.duration >= after) { @@ -557,7 +593,7 @@ module.exports = function ($timeout, $location) { scope.startFrequencyTweak = () => { let programs = {}; for (let i = 0; i < scope.channel.programs.length; i++) { - if (! scope.channel.programs[i].isOffline) { + if ( !scope.channel.programs[i].isOffline || (scope.channel.programs[i].type === 'redirect') ) { let c = getShowCode(scope.channel.programs[i]); if ( typeof(programs[c]) === 'undefined') { programs[c] = 0; @@ -629,7 +665,9 @@ module.exports = function ($timeout, $location) { function getShowCode(program) { //used for equalize and frequency tweak let showName = "_internal.Unknown"; - if ( (program.type == 'episode') && ( typeof(program.showTitle) !== 'undefined' ) ) { + if ( program.isOffline && (program.type == 'redirect') ) { + showName = `Redirect to channel ${program.channel}`; + } else if ( (program.type == 'episode') && ( typeof(program.showTitle) !== 'undefined' ) ) { showName = program.showTitle; } else { showName = "_internal.Movies"; @@ -657,11 +695,11 @@ module.exports = function ($timeout, $location) { let shows = {}; let progs = []; for (let i = 0; i < array.length; i++) { - if (array[i].isOffline) { + if (array[i].isOffline && array[i].type !== 'redirect') { continue; } - vid = array[i]; - let code = getShowCode(array[i]); + let vid = array[i]; + let code = getShowCode(vid); if ( typeof(shows[code]) === 'undefined') { shows[code] = { total: 0, @@ -708,7 +746,7 @@ module.exports = function ($timeout, $location) { let counts = {}; // some precalculation, useful to stop the shuffle from being quadratic... for (let i = 0; i < array.length; i++) { - var vid = array[i]; + let vid = array[i]; if (vid.type === 'episode' && vid.season != 0) { let countKey = { title: vid.showTitle, @@ -752,10 +790,10 @@ module.exports = function ($timeout, $location) { }); shuffle(array); for (let i = 0; i < array.length; i++) { - if (array[i].type !== 'movie' && array[i].season != 0) { + if (array[i].type === 'episode' && array[i].season != 0) { let title = array[i].showTitle; var sequence = shows[title]; - var j = next[title]; + let j = next[title]; array[i] = sequence[j].it; next[title] = (j + 1) % sequence.length; @@ -827,12 +865,37 @@ module.exports = function ($timeout, $location) { }, 0 ); } + scope.finishRedirect = (program) => { + if (scope.selectedProgram == -1) { + scope.channel.programs.splice(scope.minProgramIndex, 0, program); + } else { + scope.channel.programs[ scope.selectedProgram ] = program; + } + updateChannelDuration(); + } + scope.addRedirect = () => { + scope.selectedProgram = -1; + scope._displayRedirect = true; + scope._redirectTitle = "Add Redirect"; + scope._selectedRedirect = { + isOffline : true, + type : "redirect", + duration : 60*60*1000, + } + + }; scope.selectProgram = (index) => { scope.selectedProgram = index; let program = scope.channel.programs[index]; if(program.isOffline) { - scope._selectedOffline = scope.makeOfflineFromChannel( Math.round( (program.duration + 500) / 1000 ) ); + if (program.type === 'redirect') { + scope._displayRedirect = true; + scope._redirectTitle = "Edit Redirect"; + scope._selectedRedirect = JSON.parse(angular.toJson(program)); + } else { + scope._selectedOffline = scope.makeOfflineFromChannel( Math.round( (program.duration + 500) / 1000 ) ); + } } else { scope._selectedProgram = JSON.parse(angular.toJson(program)); } @@ -841,6 +904,32 @@ module.exports = function ($timeout, $location) { scope.channel.programs.splice(x, 1) updateChannelDuration() } + scope.knownChannels = [ + { id: -1, description: "# Channel #"}, + ] + scope.loadChannels = async () => { + let channelNumbers = await dizquetv.getChannelNumbers(); + try { + await Promise.all( channelNumbers.map( async(x) => { + let desc = await dizquetv.getChannelDescription(x); + if (desc.number != scope.channel.number) { + scope.knownChannels.push( { + id: desc.number, + description: `${desc.number} - ${desc.name}`, + }); + } + }) ); + } catch (err) { + console.error(err); + } + scope.knownChannels.sort( (a,b) => a.id - b.id); + scope.channelsDownloaded = true; + $timeout( () => scope.$apply(), 0); + + + }; + scope.loadChannels(); + scope.paddingOptions = [ { id: -1, description: "Allowed start times", allow5: false }, { id: 30, description: ":00, :30", allow5: false }, @@ -894,6 +983,9 @@ module.exports = function ($timeout, $location) { scope.nightEndHours = [ { id: -1, description: "End" } ]; scope.nightStart = -1; scope.nightEnd = -1; + scope.atNightChannelNumber = -1; + scope.atNightStart = -1; + scope.atNightEnd = -1; for (let i=0; i < 24; i++) { let v = { id: i, description: ( (i<10) ? "0" : "") + i + ":00" }; scope.nightStartHours.push(v); diff --git a/web/directives/channel-redirect.js b/web/directives/channel-redirect.js new file mode 100644 index 0000000..d9c9607 --- /dev/null +++ b/web/directives/channel-redirect.js @@ -0,0 +1,85 @@ +module.exports = function ($timeout, dizquetv) { + return { + restrict: 'E', + templateUrl: 'templates/channel-redirect.html', + replace: true, + scope: { + formTitle: "=formTitle", + visible: "=visible", + program: "=program", + _onDone: "=onDone" + }, + link: function (scope, element, attrs) { + scope.error = ""; + scope.options = []; + scope.loading = true; + + scope.$watch('program', () => { + if (typeof(scope.program) === 'undefined') { + return; + } + if ( isNaN(scope.program.duration) ) { + scope.program.duration = 15000; + } + scope.durationSeconds = Math.ceil( scope.program.duration / 1000.0 );; + }) + + scope.refreshChannels = async() => { + let channelNumbers = await dizquetv.getChannelNumbers(); + try { + await Promise.all( channelNumbers.map( async(x) => { + let desc = await dizquetv.getChannelDescription(x); + let option = { + id: x, + description: `${x} - ${desc.name}`, + }; + let i = 0; + while (i < scope.options.length) { + if (scope.options[i].id == x) { + scope.options[i] = option; + break; + } + i++; + } + if (i == scope.options.length) { + scope.options.push(option); + } + scope.$apply(); + }) ); + } catch (err) { + console.error(err); + } + scope.options.sort( (a,b) => a.id - b.id ); + scope.loading = false; + $timeout( () => scope.$apply(), 0); + }; + scope.refreshChannels(); + + scope.onCancel = () => { + scope.visible = false; + } + + scope.onDone = () => { + scope.error = ""; + if (typeof(scope.program.channel) === 'undefined') { + scope.error = "Please select a channel."; + } + if ( isNaN(scope.program.channel) ) { + scope.error = "Channel must be a number."; + } + if ( isNaN(scope.durationSeconds) ) { + scope.error = "Duration must be a number."; + } + if ( scope.error != "" ) { + $timeout( () => scope.error = "", 60000); + return; + } + scope.program.duration = scope.durationSeconds * 1000; + scope._onDone( scope.program ); + scope.visible = false; + + }; + + } + }; +} diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index 17f50f6..829d993 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -150,6 +150,13 @@
Add Breaks

Adds Flex breaks between programs, attempting to avoid groups of consecutive programs that exceed the specified number of minutes.

+
Add Redirect
+

Adds a channel redirect. During this period of time, the channel will redirect to another channel.

+ +
"Channel at Night"
+

Will redirect to another channel while between the selected hours.

+ +
Remove Duplicates

Removes repeated videos.

@@ -207,9 +214,10 @@
+
- +
@@ -252,6 +260,30 @@
+
+
+ +
+ +
+
+
+
+ + + +
+ +
+
+ + +
+ +
@@ -307,7 +339,8 @@ {{ x.type === 'episode' ? x.showTitle + ' - S' + x.season.toString().padStart(2, '0') + 'E' + x.episode.toString().padStart(2, '0') : x.title}}
- Flex + Flex + Redirect to channel: {{x.channel}}
diff --git a/web/public/templates/channel-redirect.html b/web/public/templates/channel-redirect.html new file mode 100644 index 0000000..19cb498 --- /dev/null +++ b/web/public/templates/channel-redirect.html @@ -0,0 +1,35 @@ +
+ +
\ No newline at end of file diff --git a/web/public/templates/plex-server-edit.html b/web/public/templates/plex-server-edit.html index 9a0d7d7..6859629 100644 --- a/web/public/templates/plex-server-edit.html +++ b/web/public/templates/plex-server-edit.html @@ -11,7 +11,7 @@
- +
From c6787f9f2a5b49d9d15c3b86431bf37afb3ab1e1 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 26 Aug 2020 20:00:39 -0400 Subject: [PATCH 4/6] Reruns --- web/directives/channel-config.js | 78 ++++++++++++++++++++++++ web/public/templates/channel-config.html | 23 ++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index bb0acb9..a21fde0 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -408,6 +408,66 @@ module.exports = function ($timeout, $location, dizquetv) { return hash; } + scope.doReruns = (rerunStart, rerunBlockSize, rerunRepeats) => { + let o =(new Date()).getTimezoneOffset() * 60 * 1000; + let start = (o + rerunStart * 60 * 60 * 1000) % (24*60*60*1000); + let blockSize = rerunBlockSize * 60*60* 1000; + let repeats = rerunRepeats; + + let programs = []; + let block = []; + let currentBlockSize = 0; + let currentSize = 0; + let addBlock = () => { + + let high = currentSize + currentBlockSize; + let m = high % blockSize; + if (m >= 1000) { + high = high - m + blockSize; + } + high -= currentSize; + let rem = Math.max(0, high - currentBlockSize); + if (rem >= 1000) { + currentBlockSize += rem; + let t = block.length; + if ( + (t > 0) + && block[t-1].isOffline + && (block[t-1].type !== 'redirect') + ) { + block[t-1].duration += rem; + } else { + block.push( { + isOffline: true, + duration: rem, + } ); + } + } + for (let i = 0; i < repeats; i++) { + for (let j = 0; j < block.length; j++) { + programs.push( JSON.parse( JSON.stringify(block[j]) ) ); + } + } + currentSize += repeats * currentBlockSize; + block = []; + currentBlockSize = 0; + + }; + for (let i = 0; i < scope.channel.programs.length; i++) { + if (currentBlockSize + scope.channel.programs[i].duration - 500 > blockSize) { + addBlock(); + } + block.push( scope.channel.programs[i] ); + currentBlockSize += scope.channel.programs[i].duration; + } + if (currentBlockSize != 0) { + addBlock(); + } + scope.channel.startTime = new Date( scope.channel.startTime.getTime() - scope.channel.startTime % (24*60*60*1000) + start ); + scope.channel.programs = programs; + scope.updateChannelDuration(); + }; + scope.nightChannel = (a, b, ch) => { let o =(new Date()).getTimezoneOffset() * 60 * 1000; let m = 24*60*60*1000; @@ -979,6 +1039,23 @@ module.exports = function ($timeout, $location, dizquetv) { ] scope.maxBreakSizeOptions = scope.maxBreakSizeOptions.concat(breakSizeOptions); + scope.rerunStart = -1; + scope.rerunBlockSize = -1; + scope.rerunBlockSizes = [ + { id: -1, description: "Block" }, + { id: 6, description: "6 Hours" }, + { id: 8, description: "8 Hours" }, + { id: 12, description: "12 Hours" }, + ]; + scope.rerunRepeats = -1; + scope.rerunRepeatOptions = [ + { id: -1, description: "Repeats" }, + { id: 2, description: "2" }, + { id: 3, description: "3" }, + { id: 4, description: "4" }, + ]; + + scope.nightStartHours = [ { id: -1, description: "Start" } ]; scope.nightEndHours = [ { id: -1, description: "End" } ]; scope.nightStart = -1; @@ -991,6 +1068,7 @@ module.exports = function ($timeout, $location, dizquetv) { scope.nightStartHours.push(v); scope.nightEndHours.push(v); } + scope.rerunStartHours = scope.nightStartHours; scope.paddingMod = 30; } } diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index 829d993..2cfe8e4 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -150,6 +150,9 @@
Add Breaks

Adds Flex breaks between programs, attempting to avoid groups of consecutive programs that exceed the specified number of minutes.

+
Repeat Blocks
+

There's hopefully going to be an explanation here

+
Add Redirect

Adds a channel redirect. During this period of time, the channel will redirect to another channel.

@@ -260,6 +263,24 @@
+
+
+
+ + + +
+ +
+
+ +
@@ -325,7 +346,7 @@ ⋮
-
From c342d42db97a8c64583f4462862bec53b6d0f136 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 26 Aug 2020 20:49:40 -0400 Subject: [PATCH 5/6] xmltv delivery is not synchronous anymore. It is no longer possible to change xmltv file location through UI or the api. Basic exception handling for the API but the api will need further cleanup. --- src/api.js | 171 ++++++++++++++++++++++- src/helperFuncs.js | 20 ++- src/video.js | 2 +- src/xmltv.js | 19 +-- web/public/templates/xmltv-settings.html | 3 +- 5 files changed, 197 insertions(+), 18 deletions(-) diff --git a/src/api.js b/src/api.js index 4fcac57..e2d953b 100644 --- a/src/api.js +++ b/src/api.js @@ -1,6 +1,6 @@ const express = require('express') -const fs = require('fs') +const path = require('path') const databaseMigration = require('./database-migration'); const channelCache = require('./channel-cache') const constants = require('./constants'); @@ -14,21 +14,32 @@ function api(db, channelDB, xmltvInterval) { let plexServerDB = new PlexServerDB(channelDB, channelCache, db); router.get('/api/version', async (req, res) => { + try { let ffmpegSettings = db['ffmpeg-settings'].find()[0]; let v = await (new FFMPEGInfo(ffmpegSettings)).getVersion(); res.send( { "dizquetv" : constants.VERSION_NAME, "ffmpeg" : v, } ); + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }); // Plex Servers router.get('/api/plex-servers', (req, res) => { + try { let servers = db['plex-servers'].find() servers.sort( (a,b) => { return a.index - b.index } ); res.send(servers) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.post("/api/plex-servers/status", async (req, res) => { + try { let servers = db['plex-servers'].find( { name: req.body.name, }); @@ -47,8 +58,13 @@ function api(db, channelDB, xmltvInterval) { res.send( { status: s, }); + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.post("/api/plex-servers/foreignstatus", async (req, res) => { + try { let server = req.body; let plex = new Plex(server); let s = await Promise.race( [ @@ -62,14 +78,23 @@ function api(db, channelDB, xmltvInterval) { res.send( { status: s, }); + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.delete('/api/plex-servers', async (req, res) => { + try { let name = req.body.name; if (typeof(name) === 'undefined') { return res.status(400).send("Missing name"); } let report = await plexServerDB.deleteServer(name); res.send(report) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.post('/api/plex-servers', async (req, res) => { try { @@ -93,11 +118,17 @@ function api(db, channelDB, xmltvInterval) { // Channels router.get('/api/channels', async (req, res) => { + try { let channels = await channelDB.getAllChannels(); channels.sort((a, b) => { return a.number < b.number ? -1 : 1 }) res.send(channels) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.get('/api/channel/:number', async (req, res) => { + try { let number = parseInt(req.params.number, 10); let channel = await channelCache.getChannelConfig(channelDB, number); if (channel.length == 1) { @@ -106,8 +137,13 @@ function api(db, channelDB, xmltvInterval) { } else { return res.status(404).send("Channel not found"); } + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.get('/api/channel/description/:number', async (req, res) => { + try { let number = parseInt(req.params.number, 10); let channel = await channelCache.getChannelConfig(channelDB, number); if (channel.length == 1) { @@ -120,62 +156,114 @@ function api(db, channelDB, xmltvInterval) { } else { return res.status(404).send("Channel not found"); } + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.get('/api/channelNumbers', async (req, res) => { + try { let channels = await channelDB.getAllChannelNumbers(); channels.sort( (a,b) => { return parseInt(a) - parseInt(b) } ); res.send(channels) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.post('/api/channel', async (req, res) => { + try { cleanUpChannel(req.body); await channelDB.saveChannel( req.body.number, req.body ); channelCache.clear(); res.send( { number: req.body.number} ) updateXmltv() + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.put('/api/channel', async (req, res) => { + try { cleanUpChannel(req.body); await channelDB.saveChannel( req.body.number, req.body ); channelCache.clear(); res.send( { number: req.body.number} ) updateXmltv() + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.delete('/api/channel', async (req, res) => { + try { await channelDB.deleteChannel( req.body.number ); channelCache.clear(); res.send( { number: req.body.number} ) updateXmltv() + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) // FFMPEG SETTINGS router.get('/api/ffmpeg-settings', (req, res) => { + try { let ffmpeg = db['ffmpeg-settings'].find()[0] res.send(ffmpeg) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.put('/api/ffmpeg-settings', (req, res) => { + try { db['ffmpeg-settings'].update({ _id: req.body._id }, req.body) let ffmpeg = db['ffmpeg-settings'].find()[0] res.send(ffmpeg) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.post('/api/ffmpeg-settings', (req, res) => { // RESET + try { let ffmpeg = databaseMigration.defaultFFMPEG() ; ffmpeg.ffmpegPath = req.body.ffmpegPath; db['ffmpeg-settings'].update({ _id: req.body._id }, ffmpeg) ffmpeg = db['ffmpeg-settings'].find()[0] res.send(ffmpeg) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) // PLEX SETTINGS router.get('/api/plex-settings', (req, res) => { + try { let plex = db['plex-settings'].find()[0] res.send(plex) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } }) router.put('/api/plex-settings', (req, res) => { + try { db['plex-settings'].update({ _id: req.body._id }, req.body) let plex = db['plex-settings'].find()[0] res.send(plex) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.post('/api/plex-settings', (req, res) => { // RESET + try { db['plex-settings'].update({ _id: req.body._id }, { streamPath: 'plex', debugLogging: true, @@ -199,24 +287,57 @@ function api(db, channelDB, xmltvInterval) { }) let plex = db['plex-settings'].find()[0] res.send(plex) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.get('/api/xmltv-last-refresh', (req, res) => { + try { res.send(JSON.stringify({ value: xmltvInterval.lastUpdated.valueOf() })) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) // XMLTV SETTINGS router.get('/api/xmltv-settings', (req, res) => { + try { let xmltv = db['xmltv-settings'].find()[0] res.send(xmltv) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.put('/api/xmltv-settings', (req, res) => { - db['xmltv-settings'].update({ _id: req.body._id }, req.body) + try { let xmltv = db['xmltv-settings'].find()[0] + db['xmltv-settings'].update( + { _id: req.body._id }, + { + _id: req.body._id, + cache: req.body.cache, + refresh: req.body.refresh, + file: xmltv.file, + } + ); + xmltv = db['xmltv-settings'].find()[0] res.send(xmltv) updateXmltv() + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.post('/api/xmltv-settings', (req, res) => { + try { db['xmltv-settings'].update({ _id: req.body._id }, { _id: req.body._id, cache: 12, @@ -226,20 +347,38 @@ function api(db, channelDB, xmltvInterval) { var xmltv = db['xmltv-settings'].find()[0] res.send(xmltv) updateXmltv() + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) //HDHR SETTINGS router.get('/api/hdhr-settings', (req, res) => { + try { let hdhr = db['hdhr-settings'].find()[0] res.send(hdhr) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.put('/api/hdhr-settings', (req, res) => { + try { db['hdhr-settings'].update({ _id: req.body._id }, req.body) let hdhr = db['hdhr-settings'].find()[0] res.send(hdhr) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) router.post('/api/hdhr-settings', (req, res) => { + try { db['hdhr-settings'].update({ _id: req.body._id }, { _id: req.body._id, tunerCount: 1, @@ -247,18 +386,32 @@ function api(db, channelDB, xmltvInterval) { }) var hdhr = db['hdhr-settings'].find()[0] res.send(hdhr) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) // XMLTV.XML Download router.get('/api/xmltv.xml', (req, res) => { + try { + res.set('Cache-Control', 'no-store') res.type('text') let xmltvSettings = db['xmltv-settings'].find()[0] - res.send(fs.readFileSync(xmltvSettings.file)) + let f = path.resolve(xmltvSettings.file); + res.sendFile(f) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) // CHANNELS.M3U Download router.get('/api/channels.m3u', async (req, res) => { + try { res.type('text') let channels = await channelDB.getAllChannels(); channels.sort((a, b) => { return a.number < b.number ? -1 : 1 }) @@ -272,10 +425,17 @@ function api(db, channelDB, xmltvInterval) { data += `${req.protocol}://${req.get('host')}/setup\n` } res.send(data) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + + }) // hls.m3u Download is not really working correctly right now router.get('/api/hls.m3u', async (req, res) => { + try { res.type('text') let channels = await channelDB.getAllChannels(); channels.sort((a, b) => { return a.number < b.number ? -1 : 1 }) @@ -289,6 +449,11 @@ function api(db, channelDB, xmltvInterval) { data += `${req.protocol}://${req.get('host')}/setup\n` } res.send(data) + } catch(err) { + console.error(err); + res.status(500).send("error"); + } + }) diff --git a/src/helperFuncs.js b/src/helperFuncs.js index f5b8068..4151d6f 100644 --- a/src/helperFuncs.js +++ b/src/helperFuncs.js @@ -8,10 +8,22 @@ let channelCache = require('./channel-cache'); const SLACK = require('./constants').SLACK; function getCurrentProgramAndTimeElapsed(date, channel) { - let channelStartTime = new Date(channel.startTime) - if (channelStartTime > date) - throw new Error("startTime cannot be set in the future. something fucked up..") - let timeElapsed = (date.valueOf() - channelStartTime.valueOf()) % channel.duration + let channelStartTime = (new Date(channel.startTime)).getTime(); + if (channelStartTime > date) { + let t0 = date; + let t1 = channelStartTime; + console.log(t0, t1); + console.log("Channel start time is above the given date. Flex time is picked till that."); + return { + program: { + isOffline: true, + duration : t1 - t0, + }, + timeElapsed: 0, + programIndex: -1, + } + } + let timeElapsed = (date - channelStartTime) % channel.duration let currentProgramIndex = -1 for (let y = 0, l2 = channel.programs.length; y < l2; y++) { let program = channel.programs[y] diff --git a/src/video.js b/src/video.js index cf5144c..aab95ba 100644 --- a/src/video.js +++ b/src/video.js @@ -214,7 +214,7 @@ function video( channelDB , db) { res.status(500).send("server error"); throw Error("Shouldn't prog be non-null?"); } - if (prog.program.isOffline && channel.programs.length == 1) { + if (prog.program.isOffline && channel.programs.length == 1 && prog.programIndex != -1) { //there's only one program and it's offline. So really, the channel is //permanently offline, it doesn't matter what duration was set //and it's best to give it a long duration to ensure there's always diff --git a/src/xmltv.js b/src/xmltv.js index 5901527..b677533 100644 --- a/src/xmltv.js +++ b/src/xmltv.js @@ -70,19 +70,21 @@ function _writeChannels(xw, channels) { } async function _writePrograms(xw, channel, date, cache) { - let prog = helperFuncs.getCurrentProgramAndTimeElapsed(date, channel) + let item = helperFuncs.getCurrentProgramAndTimeElapsed(date.getTime(), channel) + let prog = item.program; let cutoff = new Date( date.valueOf() + (cache * 60 * 60 * 1000) ) - let temp = new Date(date.valueOf() - prog.timeElapsed) - if (channel.programs.length === 0) + let temp = new Date(date.valueOf() - item.timeElapsed) + if (channel.programs.length === 0) { return - let i = prog.programIndex + } + let i = item.programIndex; for (; temp < cutoff;) { await _throttle(); //let's not block for this process let program = { - program: channel.programs[i], + program: prog, channel: channel.number, start: new Date(temp.valueOf()), - stop: new Date(temp.valueOf() + channel.programs[i].duration) + stop: new Date(temp.valueOf() + prog.duration) } let ni = (i + 1) % channel.programs.length; if ( @@ -92,13 +94,14 @@ async function _writePrograms(xw, channel, date, cache) { && (channel.programs[ni].duration < constants.TVGUIDE_MAXIMUM_PADDING_LENGTH_MS ) ) { - program.stop = new Date(temp.valueOf() + channel.programs[i].duration + channel.programs[ni].duration) + program.stop = new Date(temp.valueOf() + prog.duration + channel.programs[ni].duration) i = (i + 2) % channel.programs.length; } else { i = ni; } _writeProgramme(channel, xw, program, cutoff) temp = program.stop; + prog = channel.programs[i]; } } @@ -159,11 +162,9 @@ async function _writeProgramme(channel, xw, program, cutoff) { xw.endElement() } function _createXMLTVDate(d) { - //console.log("d=" + d.getTime() ); try { return d.toISOString().substring(0,19).replace(/[-T:]/g,"") + " +0000"; } catch(e) { - console.log("d=" + d.getTime(), e); return (new Date()).toISOString().substring(0,19).replace(/[-T:]/g,"") + " +0000"; } } diff --git a/web/public/templates/xmltv-settings.html b/web/public/templates/xmltv-settings.html index 0d685b2..f5cb495 100644 --- a/web/public/templates/xmltv-settings.html +++ b/web/public/templates/xmltv-settings.html @@ -9,7 +9,8 @@
Output Path
- + + You can edit this location in file xmltv-settings.json.
From 05833c3d143851b042afc29eab9708a3909390eb Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 26 Aug 2020 21:13:07 -0400 Subject: [PATCH 6/6] Log message to explain when ffmpeg is transcoding or copying streams. Removed minimum bitrates... --- src/ffmpeg.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ffmpeg.js b/src/ffmpeg.js index 73316e0..8c0219b 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -299,7 +299,6 @@ class FFMPEG extends events.EventEmitter { // add the video encoder flags ffmpegArgs.push( `-b:v`, `${this.opts.videoBitrate}k`, - `-minrate:v`, `${this.opts.videoBitrate}k`, `-maxrate:v`, `${this.opts.videoBitrate}k`, `-bufsize:v`, `${this.opts.videoBufSize}k` ); @@ -308,7 +307,6 @@ class FFMPEG extends events.EventEmitter { // add the audio encoder flags ffmpegArgs.push( `-b:a`, `${this.opts.audioBitrate}k`, - `-minrate:a`, `${this.opts.audioBitrate}k`, `-maxrate:a`, `${this.opts.audioBitrate}k`, `-bufsize:a`, `${this.opts.videoBufSize}k` ); @@ -319,6 +317,15 @@ class FFMPEG extends events.EventEmitter { ); } } + if (transcodeAudio && transcodeVideo) { + console.log("Video and Audio are being transcoded by ffmpeg"); + } else if (transcodeVideo) { + console.log("Video is being transcoded by ffmpeg. Audio is being copied."); + } else if (transcodeAudio) { + console.log("Audio is being transcoded by ffmpeg. Video is being copied."); + } else { + console.log("Video and Audio are being copied. ffmpeg is not transcoding."); + } ffmpegArgs.push( `-c:a`, (transcodeAudio ? this.opts.audioEncoder : 'copy'), '-map_metadata', '-1',