From 621f261a59840ec973f1cd41d76597db7703b6d7 Mon Sep 17 00:00:00 2001 From: vexorian Date: Thu, 17 Sep 2020 23:51:33 -0400 Subject: [PATCH 1/6] 1.0.1 --- README.md | 2 +- src/constants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be9e28c..88d9556 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# dizqueTV 1.0.1-prerelease +# dizqueTV 1.0.1 ![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 3c885d2..aeb54e7 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: "1.0.1-prerelease" + VERSION_NAME: "1.0.1" } From e9fe6001e199a3832ab248c511eeee5d43ccefee Mon Sep 17 00:00:00 2001 From: vexorian Date: Tue, 22 Sep 2020 12:17:08 -0400 Subject: [PATCH 2/6] Fix #132 Save Program not working at all. --- web/directives/channel-config.js | 5 +++-- web/directives/program-config.js | 10 ---------- web/public/templates/program-config.html | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js index 8734685..8a2f0ec 100644 --- a/web/directives/channel-config.js +++ b/web/directives/channel-config.js @@ -1080,8 +1080,9 @@ module.exports = function ($timeout, $location, dizquetv) { } scope.importPrograms = (selectedPrograms) => { - for (let i = 0, l = selectedPrograms.length; i < l; i++) - selectedPrograms[i].commercials = [] + for (let i = 0, l = selectedPrograms.length; i < l; i++) { + delete selectedPrograms[i].commercials; + } scope.channel.programs = scope.channel.programs.concat(selectedPrograms) updateChannelDuration() setTimeout( diff --git a/web/directives/program-config.js b/web/directives/program-config.js index ce50987..f274eb5 100644 --- a/web/directives/program-config.js +++ b/web/directives/program-config.js @@ -9,13 +9,6 @@ module.exports = function ($timeout) { onDone: "=onDone" }, link: function (scope, element, attrs) { - scope.selectedCommercials = (items) => { - scope.program.commercials = scope.program.commercials.concat(items) - for (let i = 0, l = scope.program.commercials.length; i < l; i++) { - if (typeof scope.program.commercials[i].commercialPosition === 'undefined') - scope.program.commercials[i].commercialPosition = 0 - } - } scope.finished = (prog) => { if (prog.title === "") scope.error = { title: 'You must set a program title.' } @@ -37,9 +30,6 @@ module.exports = function ($timeout) { return } - prog.duration = prog.duration - for (let i = 0, l = prog.commercials.length; i < l; i++) - prog.duration += prog.commercials[i].duration scope.onDone(JSON.parse(angular.toJson(prog))) scope.program = null } diff --git a/web/public/templates/program-config.html b/web/public/templates/program-config.html index 3577ea7..ce48dab 100644 --- a/web/public/templates/program-config.html +++ b/web/public/templates/program-config.html @@ -84,5 +84,5 @@ - + \ No newline at end of file From 43bf85db2053d333baf59100e014702956ab71ae Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 23 Sep 2020 18:38:56 -0400 Subject: [PATCH 3/6] Fix #69 (nice?) Hopefully for good this time. It was already very difficult to create an infinite cache loop. Now even if the worst happens, it might repeat the last few seconds of a video once but nothing more. --- src/channel-cache.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/channel-cache.js b/src/channel-cache.js index 681e99a..a0459f2 100644 --- a/src/channel-cache.js +++ b/src/channel-cache.js @@ -31,10 +31,21 @@ function getCurrentLineupItem(channelId, t1) { let recorded = cache[channelId]; let lineupItem = JSON.parse( JSON.stringify(recorded.lineupItem) ); let diff = t1 - recorded.t0; - if ( (diff <= SLACK) && (lineupItem.duration >= 2*SLACK) ) { + let rem = lineupItem.duration - lineupItem.start; + if (typeof(lineupItem.streamDuration) !== 'undefined') { + rem = Math.min(rem, lineupItem.streamDuration); + } + if ( (diff <= SLACK) && (diff + SLACK < rem) ) { //closed the stream and opened it again let's not lose seconds for //no reason - return lineupItem; + let originalT0 = recorded.lineupItem.originalT0; + if (typeof(originalT0) === 'undefined') { + originalT0 = recorded.t0; + } + if (t1 - originalT0 <= SLACK) { + lineupItem.originalT0 = originalT0; + return lineupItem; + } } lineupItem.start += diff; From 8a6fb782ad8b2b691ed3b703f73a7e9849004467 Mon Sep 17 00:00:00 2001 From: vexorian Date: Wed, 23 Sep 2020 19:10:34 -0400 Subject: [PATCH 4/6] Attempts to make error screen more likely to appear instead of stream just dying --- src/constants.js | 1 + src/plex-player.js | 26 +++++++++++++++++++++++--- src/throttler.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/video.js | 30 +++++++++++++++++++++++------- 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/throttler.js diff --git a/src/constants.js b/src/constants.js index 733ea56..19547c9 100644 --- a/src/constants.js +++ b/src/constants.js @@ -3,6 +3,7 @@ module.exports = { TVGUIDE_MAXIMUM_PADDING_LENGTH_MS: 30*60*1000, STEALTH_DURATION: 5 * 60* 1000, TVGUIDE_MAXIMUM_FLEX_DURATION : 6 * 60 * 60 * 1000, + TOO_FREQUENT: 100, VERSION_NAME: "1.0.2-prerelease" } diff --git a/src/plex-player.js b/src/plex-player.js index baf5c9c..73ba96f 100644 --- a/src/plex-player.js +++ b/src/plex-player.js @@ -82,18 +82,38 @@ class PlexPlayer { let emitter = new EventEmitter(); //setTimeout( () => { let ff = await ffmpeg.spawnStream(stream.streamUrl, stream.streamStats, streamStart, streamDuration, enableChannelIcon, lineupItem.type); // Spawn the ffmpeg process - ff.pipe(outStream); + ff.pipe(outStream, {'end':false} ); //}, 100); plexTranscoder.startUpdatingPlex(); - + ffmpeg.on('end', () => { emitter.emit('end'); }); ffmpeg.on('close', () => { emitter.emit('close'); }); - ffmpeg.on('error', (err) => { + ffmpeg.on('error', async (err) => { + console.log("Replacing failed stream with error streram"); + ff.unpipe(outStream); + ffmpeg.removeAllListeners('data'); + ffmpeg.removeAllListeners('end'); + ffmpeg.removeAllListeners('error'); + ffmpeg.removeAllListeners('close'); + ffmpeg = new FFMPEG(ffmpegSettings, channel); // Set the transcoder options + ffmpeg.on('close', () => { + emitter.emit('close'); + }); + ffmpeg.on('end', () => { + emitter.emit('end'); + }); + ffmpeg.on('error', (err) => { + emitter.emit('error', err ); + }); + + ff = await ffmpeg.spawnError('oops', 'oops', Math.min(streamStats.duration, 60000) ); + ff.pipe(outStream); + emitter.emit('error', err); }); return emitter; diff --git a/src/throttler.js b/src/throttler.js new file mode 100644 index 0000000..543cbe9 --- /dev/null +++ b/src/throttler.js @@ -0,0 +1,45 @@ +let constants = require('./constants'); + +let cache = {} + + +function equalItems(a, b) { + if ( (typeof(a) === 'undefined') || a.isOffline || b.isOffline ) { + return false; + } + console.log("no idea how to compare this: " + JSON.stringify(a) ); + console.log(" with this: " + JSON.stringify(b) ); + return true; + +} + + +function wereThereTooManyAttempts(sessionId, lineupItem) { + let obj = cache[sessionId]; + let t1 = (new Date()).getTime(); + if (typeof(obj) === 'undefined') { + previous = cache[sessionId] = { + t0: t1 - constants.TOO_FREQUENT * 5 + }; + + } else { + clearTimeout(obj.timer); + } + previous.timer = setTimeout( () => { + cache[sessionId].timer = null; + delete cache[sessionId]; + }, constants.TOO_FREQUENT*5 ); + + let result = false; + + if (previous.t0 + constants.TOO_FREQUENT >= t1) { + //certainly too frequent + result = equalItems( previous.lineupItem, lineupItem ); + } + cache[sessionId].t0 = t1; + cache[sessionId].lineupItem = lineupItem; + return result; + +} + +module.exports = wereThereTooManyAttempts; \ No newline at end of file diff --git a/src/video.js b/src/video.js index 0468820..f41eb39 100644 --- a/src/video.js +++ b/src/video.js @@ -6,9 +6,12 @@ const PlexTranscoder = require('./plexTranscoder') const fs = require('fs') const ProgramPlayer = require('./program-player'); const channelCache = require('./channel-cache') +const wereThereTooManyAttempts = require('./throttler'); module.exports = { router: video } +let StreamCount = 0; + function video( channelDB , db) { var router = express.Router() @@ -107,7 +110,7 @@ function video( channelDB , db) { let channelNum = parseInt(req.query.channel, 10) let ff = await ffmpeg.spawnConcat(`http://localhost:${process.env.PORT}/playlist?channel=${channelNum}`); - ff.pipe(res); + ff.pipe(res ); }) // Stream individual video to ffmpeg concat above. This is used by the server, NOT the client router.get('/stream', async (req, res) => { @@ -116,6 +119,7 @@ function video( channelDB , db) { res.status(400).send("No Channel Specified") return } + let session = parseInt(req.query.session); let m3u8 = (req.query.m3u8 === '1'); let number = parseInt(req.query.channel); let channel = await channelCache.getChannelConfig(channelDB, number); @@ -274,6 +278,14 @@ function video( channelDB , db) { if (! isLoading) { channelCache.recordPlayback(channel.number, t0, lineupItem); } + if (wereThereTooManyAttempts(session, lineupItem)) { + lineupItem = { + isOffline: true, + err: Error("Too many attempts, throttling.."), + duration : 60000, + }; + } + let playerContext = { lineupItem : lineupItem, @@ -329,6 +341,8 @@ function video( channelDB , db) { router.get('/m3u8', async (req, res) => { + let sessionId = StreamCount++; + //res.type('application/vnd.apple.mpegurl') res.type("application/x-mpegURL"); @@ -363,13 +377,13 @@ function video( channelDB , db) { if ( ffmpegSettings.enableFFMPEGTranscoding === true) { //data += `#EXTINF:${cur},\n`; - data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=0&m3u8=1\n`; + data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=0&m3u8=1&session=${sessionId}\n`; } //data += `#EXTINF:${cur},\n`; - data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=1&m3u8=1\n` + data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=1&m3u8=1&session=${sessionId}\n` for (var i = 0; i < maxStreamsToPlayInARow - 1; i++) { //data += `#EXTINF:${cur},\n`; - data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&m3u8=1\n` + data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&m3u8=1&session=${sessionId}\n` } res.send(data) @@ -398,6 +412,8 @@ function video( channelDB , db) { let ffmpegSettings = db['ffmpeg-settings'].find()[0] + let sessionId = StreamCount++; + if ( (ffmpegSettings.enableFFMPEGTranscoding === true) && (ffmpegSettings.normalizeVideoCodec === true) @@ -405,11 +421,11 @@ function video( channelDB , db) { && (ffmpegSettings.normalizeResolution === true) && (ffmpegSettings.normalizeAudio === true) ) { - data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=0'\n`; + data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=0&session=${sessionId}'\n`; } - data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=1'\n` + data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=1&session=${sessionId}'\n` for (var i = 0; i < maxStreamsToPlayInARow - 1; i++) { - data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}'\n` + data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&session=${sessionId}'\n` } res.send(data) From 665e71e24ee5e93d9c9c90545addb53fdc235ff6 Mon Sep 17 00:00:00 2001 From: Dan Ferguson <65367335+DEFENDORe@users.noreply.github.com> Date: Thu, 24 Sep 2020 17:02:41 -0400 Subject: [PATCH 5/6] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ec75841 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Dan Ferguson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 0df7622a325381e755d09ff1e85caac665faff60 Mon Sep 17 00:00:00 2001 From: vexorian Date: Thu, 24 Sep 2020 18:49:41 -0400 Subject: [PATCH 6/6] dizqueTV license --- LICENSE | 32 +++++++++++++++----------------- README.md | 5 +++++ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/LICENSE b/LICENSE index ec75841..90a253c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,19 @@ -MIT License +zlib License -Copyright (c) 2020 Dan Ferguson +Copyright (c) 2020 Dan Ferguson, Victor Hugo Soliz Kuncar -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/README.md b/README.md index 88d9556..a86bd46 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,8 @@ npm run dev-server * Pull requests welcome but please read the [Code of Conduct](CODE_OF_CONDUCT.md) and the [Pull Request Template](pull_request_template.md) first. * Tip Jar: https://buymeacoffee.com/vexorian + +## License + + * Original pseudotv-Plex code was released under [MIT license (c) 2020 Dan Ferguson](https://github.com/DEFENDORe/pseudotv/blob/665e71e24ee5e93d9c9c90545addb53fdc235ff6/LICENSE) + * dizqueTV's improvements are released under zlib license (c) 2020 Victor Hugo Soliz Kuncar \ No newline at end of file