From 4dffb666d3b004cb9e54993884720d769bd98000 Mon Sep 17 00:00:00 2001 From: vexorian Date: Sun, 6 Sep 2020 07:50:00 -0400 Subject: [PATCH 01/12] Prepare 0.0.68 development --- README.md | 2 +- src/constants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 317eb5c..5572081 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# dizqueTV 0.0.67-prerelease +# dizqueTV 0.0.68-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 e071776..7cbdb4f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -4,5 +4,5 @@ module.exports = { STEALTH_DURATION: 5 * 60* 1000, TVGUIDE_MAXIMUM_FLEX_DURATION : 6 * 60 * 60 * 1000, - VERSION_NAME: "0.0.67-prerelease" + VERSION_NAME: "0.0.68-prerelease" } From 1395c681ca7e38bc40e16d9af3914c136f8bd2e4 Mon Sep 17 00:00:00 2001 From: vexorian Date: Mon, 7 Sep 2020 21:47:57 -0400 Subject: [PATCH 02/12] Virtual Scroll for channel editor. Big thanks to @TimeBomb for the help. --- package.json | 1 + web/app.js | 3 ++- web/directives/channel-config.js | 12 ++++++--- web/public/templates/channel-config.html | 32 +++++------------------- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 4372b69..22d7a59 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "angular": "^1.7.9", "angular-router-browserify": "0.0.2", + "angular-vs-repeat": "2.0.13", "axios": "^0.19.2", "body-parser": "^1.19.0", "diskdb": "^0.1.17", diff --git a/web/app.js b/web/app.js index a81b421..bc4dfeb 100644 --- a/web/app.js +++ b/web/app.js @@ -3,8 +3,9 @@ require('angular-router-browserify')(angular) require('./ext/lazyload')(angular) require('./ext/dragdrop') require('./ext/angularjs-scroll-glue') +require('angular-vs-repeat'); -var app = angular.module('myApp', ['ngRoute', 'angularLazyImg', 'dndLists', 'luegg.directives']) +var app = angular.module('myApp', ['ngRoute', 'vs-repeat', 'angularLazyImg', 'dndLists', 'luegg.directives']) app.service('plex', require('./services/plex')) app.service('dizquetv', require('./services/dizquetv')) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index a21fde0..3f045b0 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -113,6 +113,7 @@ module.exports = function ($timeout, $location, dizquetv) { setTimeout( () => { scope.channel.programs.splice(dropIndex + index, 0, program); updateChannelDuration() + scope.$apply(); }, 1); return true; } @@ -375,15 +376,16 @@ module.exports = function ($timeout, $location, dizquetv) { } let ems = Math.pow( Math.min(24*60*60*1000, program.duration), 0.7 ); - ems = ems / Math.pow(5*60*1000., 0.7); - ems = Math.max( 0.25 , ems); - let top = Math.max(0.0, (1.75 - ems) / 2.0) ; + ems = 1.3; + let top = 0.01; if (top == 0.0) { top = "1px"; } else { top = top + "em"; } + + return { 'width': '0.5em', 'height': ems + 'em', @@ -869,6 +871,7 @@ module.exports = function ($timeout, $location, dizquetv) { scope.hasFlex = false; for (let i = 0, l = scope.channel.programs.length; i < l; i++) { scope.channel.programs[i].start = new Date(scope.channel.startTime.valueOf() + scope.channel.duration) + scope.channel.programs[i].$index = i; scope.channel.duration += scope.channel.programs[i].duration scope.channel.programs[i].stop = new Date(scope.channel.startTime.valueOf() + scope.channel.duration) if (scope.channel.programs[i].isOffline) { @@ -906,6 +909,9 @@ module.exports = function ($timeout, $location, dizquetv) { scope.error.programs = "No programs have been selected. Select at least one program." else { channel.startTime.setMilliseconds( scope.millisecondsOffset); + for (let i = 0; i < scope.channel.programs.length; i++) { + delete scope.channel.programs[i].$index; + } scope.onDone(JSON.parse(angular.toJson(channel))) } $timeout(() => { scope.error = {} }, 3500) diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index 902bc13..7d177cd 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -340,22 +340,10 @@
-
-
Showing programs {{minProgramIndex+1}} to {{minProgramIndex+100}}
-
- -
-
- {{ dateForGuide(channel.startTime) }} -
- -
- ⋮ -
-
-
-
+
+ +
@@ -371,20 +359,12 @@ Redirect to channel: {{x.channel}}
-
-
-
- {{ dateForGuide(channel.programs[minProgramIndex + 100 - 1].stop)}} -
- -
- ⋮ -
-
+
{{ dateForGuide(channel.programs[channel.programs.length-1].stop)}} From 7dcaf7060824c368d6ecb51999c98cd598879633 Mon Sep 17 00:00:00 2001 From: vexorian Date: Mon, 7 Sep 2020 22:02:51 -0400 Subject: [PATCH 03/12] Fix unraid repo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5572081..5a888e6 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ docker run --name dizquetv -p 8000:8000 -v C:\.dizquetv:/home/node/app/.dizquetv #### Unraid Install Add ``` -https://github.com/vexorian/dizquetv +https://github.com/vexorian/dizquetv/tree/main ``` to your "Template repositories" in the Docker tab. Click the "Add Container" button From 307507a2237a37f073225f1d5af5e3872d1c70fd Mon Sep 17 00:00:00 2001 From: vexorian Date: Mon, 7 Sep 2020 22:03:31 -0400 Subject: [PATCH 04/12] Fix #95 Channel at night channels not rendering in TV guide when used as redirect. --- src/tv-guide-service.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tv-guide-service.js b/src/tv-guide-service.js index 1d89e36..515f9e9 100644 --- a/src/tv-guide-service.js +++ b/src/tv-guide-service.js @@ -32,7 +32,7 @@ class TVGuideService let t = (new Date()).getTime(); this.updateTime = t; this.updateLimit = t + limit; - let channels = inputChannels.filter( ch => (ch.stealth !== true) ); + let channels = inputChannels; this.updateChannels = channels; while( this.lastUpdate < t) { if (this.currentUpdate == -1) { @@ -296,8 +296,10 @@ class TVGuideService } } else { for (let i = 0; i < channels.length; i++) { + if(! channels[i].stealth) { let programs = await this.getChannelPrograms(t0, t1, channels[i] ); result[ channels[i].number ] = programs; + } } } return result; From 0158832fe03f537748d509fa3c55e1966e0da949 Mon Sep 17 00:00:00 2001 From: vexorian Date: Tue, 8 Sep 2020 20:59:17 -0400 Subject: [PATCH 05/12] Channel at night / restrict hours: Differentiate between padding time and restricted time. Fixes #96 --- web/directives/channel-config.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index 3f045b0..63266eb 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -493,6 +493,17 @@ module.exports = function ($timeout, $location, dizquetv) { for (let i = 0, l = scope.channel.programs.length; i < l; i++) { let p = pos(t); if ( (p != 0) && (p + scope.channel.programs[i].duration > b) ) { + if (b - 30000 > p) { + let d = b- p; + t += d; + p = pos(t); + progs.push( + { + duration: d, + isOffline: true, + } + ) + } //time to pad let d = m - p; progs.push( @@ -510,13 +521,23 @@ module.exports = function ($timeout, $location, dizquetv) { t += scope.channel.programs[i].duration; } if (pos(t) != 0) { + if (b > pos(t)) { + let d = b - pos(t) % m; + t += d; + progs.push( + { + duration: d, + isOffline: true, + } + ) + } let d = m - pos(t); progs.push( { duration: d, isOffline: true, - type: (typeof(ch) === 'undefined') ? undefined: "redirect", channel: ch, + type: (typeof(ch) === 'undefined') ? undefined: "redirect", } ) } From 87fb2baa73d265158275d1f6ff7af93b9d492fb6 Mon Sep 17 00:00:00 2001 From: vexorian Date: Tue, 8 Sep 2020 21:54:48 -0400 Subject: [PATCH 06/12] Icons for tool buttons --- web/public/templates/channel-config.html | 80 ++++++++++++++++++------ web/public/templates/offline-config.html | 12 +++- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index 7d177cd..c75a612 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -197,37 +197,53 @@  
- +
- +
- +
- +
- +
- +
- +
- +
@@ -238,7 +254,10 @@
- +
@@ -283,14 +306,18 @@ ng-options="o.id as o.description for o in rerunRepeatOptions"> - +
- +
@@ -304,7 +331,9 @@
- +
@@ -314,19 +343,29 @@
- +
- +
- +
- +
- +
@@ -334,7 +373,8 @@
Add programs to this channel by selecting media from your Plex library

No programs are currently scheduled -
diff --git a/web/public/templates/offline-config.html b/web/public/templates/offline-config.html index 57f6d3b..8762f52 100644 --- a/web/public/templates/offline-config.html +++ b/web/public/templates/offline-config.html @@ -117,13 +117,19 @@
- +
- +
- +
From e506ac15b0dfbf8a49e7f999f017295b069e1e53 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 9 Sep 2020 15:32:16 -0400 Subject: [PATCH 07/12] Fix Memory usage peaking during TV Guide Generation --- index.js | 20 +++++++++++++++----- src/tv-guide-service.js | 9 ++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 241f5e3..1fe19e6 100644 --- a/index.js +++ b/index.js @@ -67,19 +67,29 @@ let xmltvInterval = { interval: null, lastRefresh: null, updateXML: async () => { - let channels = []; - try { + let getChannelsCached = async() => { let channelNumbers = await channelDB.getAllChannelNumbers(); - channels = await Promise.all( channelNumbers.map( async (x) => { - return await channelCache.getChannelConfig(channelDB, x); + return await Promise.all( channelNumbers.map( async (x) => { + return (await channelCache.getChannelConfig(channelDB, x))[0]; }) ); + } + + let channels = []; + + try { + channels = await getChannelsCached(); let xmltvSettings = db['xmltv-settings'].find()[0]; - await guideService.refresh( await channelDB.getAllChannels(), xmltvSettings.cache*60*60*1000 ); + let t = guideService.prepareRefresh(channels, xmltvSettings.cache*60*60*1000); + channels = null; + + await guideService.refresh(t); xmltvInterval.lastRefresh = new Date() console.log('XMLTV Updated at ', xmltvInterval.lastRefresh.toLocaleString()); } catch (err) { console.error("Unable to update TV guide?", err); + return; } + channels = await getChannelsCached(); let plexServers = db['plex-servers'].find() for (let i = 0, l = plexServers.length; i < l; i++) { // Foreach plex server diff --git a/src/tv-guide-service.js b/src/tv-guide-service.js index 515f9e9..841f6b6 100644 --- a/src/tv-guide-service.js +++ b/src/tv-guide-service.js @@ -28,12 +28,16 @@ class TVGuideService return this.cached; } - async refresh(inputChannels, limit) { + prepareRefresh(inputChannels, limit) { let t = (new Date()).getTime(); this.updateTime = t; this.updateLimit = t + limit; let channels = inputChannels; this.updateChannels = channels; + return t; + } + + async refresh(t) { while( this.lastUpdate < t) { if (this.currentUpdate == -1) { this.currentUpdate = this.updateTime; @@ -47,6 +51,9 @@ class TVGuideService } async makeAccumulated(channel) { + if (typeof(channel.programs) === 'undefined') { + throw Error( JSON.stringify(channel).slice(0,200) ); + } let n = channel.programs.length; let arr = new Array( channel.programs.length + 1); arr[0] = 0; From 5a0080c6b758dc36aa429822bf5789f2fc177522 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 9 Sep 2020 15:57:01 -0400 Subject: [PATCH 08/12] Discord icon and font awesome 5 (which in my opinion is noticieably worse than 4, but it has the discord icon) --- web/public/index.html | 15 ++++++++----- web/public/style.css | 2 ++ web/public/templates/channel-config.html | 26 +++++++++++----------- web/public/templates/offline-config.html | 8 +++---- web/public/templates/plex-library.html | 2 +- web/public/templates/plex-server-edit.html | 4 ++-- web/public/templates/plex-settings.html | 8 +++---- web/public/templates/remove-shows.html | 2 +- web/public/views/channels.html | 2 +- 9 files changed, 38 insertions(+), 31 deletions(-) diff --git a/web/public/index.html b/web/public/index.html index 188fe0b..5037cb9 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -4,7 +4,7 @@ dizqueTV - + @@ -14,18 +14,23 @@

dizqueTV - - + + + + + + +

Channels - Guide - Settings - Version - XMLTV + XMLTV - M3U + M3U
diff --git a/web/public/style.css b/web/public/style.css index 94ef966..665ab20 100644 --- a/web/public/style.css +++ b/web/public/style.css @@ -1,3 +1,5 @@ +.pull-right { float: right; } + .commercials-panel { background-color: rgb(70, 70, 70); border-top: 1px solid #daa104; diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index c75a612..fa69d94 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -216,12 +216,12 @@
@@ -256,7 +256,7 @@
@@ -271,7 +271,7 @@ @@ -307,7 +307,7 @@ @@ -316,7 +316,7 @@
@@ -332,7 +332,7 @@ ng-options="o.id as o.description for o in nightEndHours" >
@@ -344,27 +344,27 @@
@@ -400,7 +400,7 @@
diff --git a/web/public/templates/offline-config.html b/web/public/templates/offline-config.html index 8762f52..69d27a7 100644 --- a/web/public/templates/offline-config.html +++ b/web/public/templates/offline-config.html @@ -118,17 +118,17 @@
@@ -149,7 +149,7 @@
diff --git a/web/public/templates/plex-library.html b/web/public/templates/plex-library.html index b281e84..8e2c0d2 100644 --- a/web/public/templates/plex-library.html +++ b/web/public/templates/plex-library.html @@ -114,7 +114,7 @@
  • {{ (selection[selection.length + x].type !== 'episode') ? selection[selection.length + x].title : (selection[selection.length + x].showTitle + ' - S' + selection[selection.length + x].season.toString().padStart(2,'0') + 'E' + selection[selection.length + x].episode.toString().padStart(2,'0'))}}
  • diff --git a/web/public/templates/plex-server-edit.html b/web/public/templates/plex-server-edit.html index 6859629..6a3f63f 100644 --- a/web/public/templates/plex-server-edit.html +++ b/web/public/templates/plex-server-edit.html @@ -53,7 +53,7 @@
    @@ -65,7 +65,7 @@

    If you delete a plex server, all the existing programs that reference to it will be replaced with Flex time. Fillers that reference to the server will be removed. This operation cannot be undone.

    - + diff --git a/web/public/templates/plex-settings.html b/web/public/templates/plex-settings.html index d70b032..f51c7b6 100644 --- a/web/public/templates/plex-settings.html +++ b/web/public/templates/plex-settings.html @@ -33,13 +33,13 @@ {{ x.uri }}
    -
    ok
    -
    error
    +
    ok
    +
    error
    -
    ok
    -
    error
    +
    ok
    +
    error
    diff --git a/web/public/views/channels.html b/web/public/views/channels.html index a5137f5..ee61a79 100644 --- a/web/public/views/channels.html +++ b/web/public/views/channels.html @@ -31,7 +31,7 @@ {{x.name}} (Stealth) From 61b6d67f213e6be829628ea7d4f330618b63e83e Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 9 Sep 2020 21:01:54 -0400 Subject: [PATCH 09/12] Improved resilience to errors in streams. Error stream shouldn't die abruptly anymore. --- src/ffmpeg.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ffmpeg.js b/src/ffmpeg.js index a218943..801b9ba 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -30,6 +30,7 @@ class FFMPEG extends events.EventEmitter { this.audioChannelsSampleRate = this.opts.normalizeAudio; this.ensureResolution = this.opts.normalizeResolution; this.volumePercent = this.opts.audioVolumePercent; + this.hasBeenKilled = false; } async spawnConcat(streamUrl) { return await this.spawn(streamUrl, undefined, undefined, undefined, true, false, undefined, true) @@ -404,24 +405,37 @@ class FFMPEG extends events.EventEmitter { let doLogs = this.opts.logFfmpeg && !isConcatPlaylist; this.ffmpeg = spawn(this.ffmpegPath, ffmpegArgs, { stdio: ['ignore', 'pipe', (doLogs?process.stderr:"ignore") ] } ); - this.ffmpeg.on('close', (code) => { + let ffmpegName = (isConcatPlaylist ? "Concat FFMPEG": "Stream FFMPEG"); + + this.ffmpeg.on('exit', (code, signal) => { if (code === null) { + console.log( `${ffmpegName} exited due to signal: ${signal}` ); this.emit('close', code) } else if (code === 0) { + console.log( `${ffmpegName} exited normally.` ); this.emit('end') } else if (code === 255) { + if (this.hasBeenKilled) { + console.log( `${ffmpegName} finished with code 255.` ); + this.emit('close', code) + return; + } if (! this.sentData) { this.emit('error', { code: code, cmd: `${this.opts.ffmpegPath} ${ffmpegArgs.join(' ')}` }) } + console.log( `${ffmpegName} exited with code 255.` ); this.emit('close', code) } else { + console.log( `${ffmpegName} exited with code ${code}.` ); this.emit('error', { code: code, cmd: `${this.opts.ffmpegPath} ${ffmpegArgs.join(' ')}` }) } - }) + }); + return this.ffmpeg.stdout; } kill() { if (typeof this.ffmpeg != "undefined") { + this.hasBeenKilled = true; this.ffmpeg.kill() } } From 904444ebc5a343dc8722e5983d1e054f258fa891 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 9 Sep 2020 23:19:21 -0400 Subject: [PATCH 10/12] Do not allow to enable subtitles if direct play is forced. --- src/plexTranscoder.js | 6 ++++-- web/directives/plex-settings.js | 3 +++ web/public/templates/plex-settings.html | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plexTranscoder.js b/src/plexTranscoder.js index a88f6d7..47caba5 100644 --- a/src/plexTranscoder.js +++ b/src/plexTranscoder.js @@ -42,9 +42,11 @@ class PlexTranscoder { this.log(` deinterlace: ${deinterlace}`) this.log(` streamPath: ${this.settings.streamPath}`) - - if (this.settings.streamPath === 'direct' || this.settings.forceDirectPlay) { + if (this.settings.enableSubtitles) { + console.log("Direct play is forced, so subtitles are forcibly disabled."); + this.settings.enableSubtitles = false; + } stream = {directPlay: true} } else { try { diff --git a/web/directives/plex-settings.js b/web/directives/plex-settings.js index 3b7abca..353962c 100644 --- a/web/directives/plex-settings.js +++ b/web/directives/plex-settings.js @@ -145,6 +145,9 @@ module.exports = function (plex, dizquetv, $timeout) { return r; } + scope.shouldDisableSubtitles = () => { + return scope.settings.forceDirectPlay || (scope.settings.streamPath === "direct" ); + } scope.addPlexServer = async () => { scope.isProcessing = true; diff --git a/web/public/templates/plex-settings.html b/web/public/templates/plex-settings.html index f51c7b6..a28803d 100644 --- a/web/public/templates/plex-settings.html +++ b/web/public/templates/plex-settings.html @@ -178,8 +178,8 @@
    - - + +
    From 1a874f62d8b333c59fa1165875f07db55c04d486 Mon Sep 17 00:00:00 2001 From: vexorian Date: Thu, 10 Sep 2020 00:37:23 -0400 Subject: [PATCH 11/12] New Channel Took : Save/Recover tv show positions. --- web/directives/channel-config.js | 100 +++++++++++++++++++++++ web/public/templates/channel-config.html | 32 +++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index 63266eb..b3d7cc7 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -16,6 +16,9 @@ module.exports = function ($timeout, $location, dizquetv) { scope._frequencyMessage = ""; scope.millisecondsOffset = 0; scope.minProgramIndex = 0; + scope.episodeMemory = { + saved : false, + }; if (typeof scope.channel === 'undefined' || scope.channel == null) { scope.channel = {} scope.channel.programs = [] @@ -544,6 +547,103 @@ module.exports = function ($timeout, $location, dizquetv) { scope.channel.programs = progs; updateChannelDuration(); } + scope.savePositions = () => { + scope.episodeMemory = { + saved : false, + }; + let array = scope.channel.programs; + for (let i = 0; i < array.length; i++) { + if (array[i].type === 'episode' && array[i].season != 0) { + let key = array[i].showTitle; + if (typeof(scope.episodeMemory[key]) === 'undefined') { + scope.episodeMemory[key] = { + season: array[i].season, + episode: array[i].episode, + } + } + } + } + scope.episodeMemory.saved = true; + } + scope.recoverPositions = () => { + //this is basically the code for cyclic shuffle + let array = scope.channel.programs; + let shows = {}; + let next = {}; + let counts = {}; + // some precalculation, useful to stop the shuffle from being quadratic... + for (let i = 0; i < array.length; i++) { + let vid = array[i]; + if (vid.type === 'episode' && vid.season != 0) { + let countKey = { + title: vid.showTitle, + s: vid.season, + e: vid.episode, + } + let key = JSON.stringify(countKey); + let c = ( (typeof(counts[key]) === 'undefined') ? 0 : counts[key] ); + counts[key] = c + 1; + let showEntry = { + c: c, + it: vid + } + if ( typeof(shows[vid.showTitle]) === 'undefined') { + shows[vid.showTitle] = []; + } + shows[vid.showTitle].push(showEntry); + } + } + //this is O(|N| log|M|) where |N| is the total number of TV + // episodes and |M| is the maximum number of episodes + // in a single show. I am pretty sure this is a lower bound + // on the time complexity that's possible here. + Object.keys(shows).forEach(function(key,index) { + shows[key].sort( (a,b) => { + if (a.c == b.c) { + if (a.it.season == b.it.season) { + if (a.it.episode == b.it.episode) { + return 0; + } else { + return (a.it.episode < b.it.episode)?-1: 1; + } + } else { + return (a.it.season < b.it.season)?-1: 1; + } + } else { + return (a.c < b.c)? -1: 1; + } + }); + next[key] = 0; + if (typeof(scope.episodeMemory[key]) !== 'undefined') { + for (let i = 0; i < shows[key].length; i++) { + if ( + (shows[key][i].it.season === scope.episodeMemory[key].season) + &&(shows[key][i].it.episode === scope.episodeMemory[key].episode) + ) { + next[key] = i; + break; + } + } + } + }); + for (let i = 0; i < array.length; i++) { + if (array[i].type === 'episode' && array[i].season != 0) { + let title = array[i].showTitle; + var sequence = shows[title]; + let j = next[title]; + array[i] = sequence[j].it; + + next[title] = (j + 1) % sequence.length; + } + } + scope.channel.programs = array; + updateChannelDuration(); + + } + scope.cannotRecoverPositions = () => { + return scope.episodeMemory.saved !== true; + } + scope.addBreaks = (afterMinutes, minDurationSeconds, maxDurationSeconds) => { let after = afterMinutes * 60 * 1000 + 5000; //allow some seconds of excess let minDur = minDurationSeconds; diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html index fa69d94..5dd4798 100644 --- a/web/public/templates/channel-config.html +++ b/web/public/templates/channel-config.html @@ -139,6 +139,9 @@
    Sort Release Dates

    Sorts everything by its release date. This will only work correctly if the release dates in Plex are correct. In case any item does not have a release date specified, it will be moved to the bottom.

    +
    Sort Release Dates
    +

    Sorts everything by its release date. This will only work correctly if the release dates in Plex are correct. In case any item does not have a release date specified, it will be moved to the bottom.

    +
    Balance Shows

    Attempts to make the total amount of time each TV show appears in the programming as balanced as possible. This works by adding multiple copies of TV shows that have too little total time and by possibly removing duplicated episodes from TV shows that have too much total time. Note that in many situations it would be impossible to achieve perfect balance because channel duration is not infinite. Movies/Clips are treated as a single TV show. Note that this will most likely result in a larger channel and that having large channels makes some UI operations slower.

    @@ -160,6 +163,10 @@
    Reruns

    Divides the programming in blocks of 6, 8 or 12 hours then repeats each of the blocks the specified number of times. For example, you can make a channel that plays exactly the same channels in the morning and in the afternoon.

    +
    Save|Recover Episode Positions
    +

    The "Save" button saves the current episodes that are next to be played for each tv show. Then whenever you click the "Recover Episode Popsitions" button, episodes will be rearranged cyclically and they will start with the saved positions. So you can maintain episode sequences even after modifying the channel. If there are any new TV shows, they will start at their current positions. Movies and specials won't change positions. +

    +
    Add Redirect

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

    @@ -189,7 +196,7 @@
    - +
    @@ -225,6 +232,7 @@
    +