Merge branch 'dev/1.1.x' into main

This commit is contained in:
vexorian 2020-11-22 21:19:24 -04:00
commit d52f0adf87
12 changed files with 162 additions and 42 deletions

View File

@ -1,4 +1,4 @@
# dizqueTV 1.1.3
# dizqueTV 1.1.4-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.

View File

@ -17,6 +17,7 @@ const constants = require('./src/constants')
const ChannelDB = require("./src/dao/channel-db");
const FillerDB = require("./src/dao/filler-db");
const TVGuideService = require("./src/tv-guide-service");
const onShutdown = require("node-graceful-shutdown").onShutdown;
console.log(
` \\
@ -209,3 +210,11 @@ function initDB(db, channelDB) {
}
}
onShutdown("log" , [], async() => {
console.log("Received exit signal, attempting graceful shutdonw...");
});
onShutdown("xmltv-writer" , [], async() => {
await xmltv.shutdown();
} );

View File

@ -19,6 +19,7 @@
"angular": "^1.7.9",
"angular-router-browserify": "0.0.2",
"angular-vs-repeat": "2.0.13",
"random-js" : "2.1.0",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"diskdb": "^0.1.17",
@ -26,6 +27,7 @@
"node-ssdp": "^4.0.0",
"request": "^2.88.2",
"uuid": "^8.0.0",
"node-graceful-shutdown" : "1.1.0",
"xml-writer": "^1.7.0"
},
"bin": "dist/index.js",

View File

@ -5,5 +5,5 @@ module.exports = {
TVGUIDE_MAXIMUM_FLEX_DURATION : 6 * 60 * 60 * 1000,
TOO_FREQUENT: 100,
VERSION_NAME: "1.1.3"
VERSION_NAME: "1.1.4-prerelease"
}

View File

@ -6,6 +6,9 @@ module.exports = {
let channelCache = require('./channel-cache');
const SLACK = require('./constants').SLACK;
const randomJS = require("random-js");
const Random = randomJS.Random;
const random = new Random( randomJS.MersenneTwister19937.autoSeed() );
function getCurrentProgramAndTimeElapsed(date, channel) {
let channelStartTime = (new Date(channel.startTime)).getTime();
@ -102,7 +105,7 @@ function createLineup(obj, channel, fillers, isFirst) {
//it's boring and odd to tune into a channel and it's always
//the start of a commercial.
let more = Math.max(0, filler.duration - fillerstart - 15000 - SLACK);
fillerstart += Math.floor(more * Math.random() );
fillerstart += random.integer(0, more);
}
lineup.push({ // just add the video, starting at 0, playing the entire duration
type: 'commercial',
@ -152,12 +155,7 @@ function createLineup(obj, channel, fillers, isFirst) {
}
function weighedPick(a, total) {
if (a==total) {
return true;
} else {
let ran = Math.random();
return ran * total < a;
}
return random.bool(a, total);
}
function pickRandomWithMaxDuration(channel, fillers, maxDuration) {

View File

@ -182,7 +182,7 @@ lang=en`
try {
return this.getVideoStats().videoDecision === "copy";
} catch (e) {
console.log("Error at decision:" + e);
console.log("Error at decision:", e);
return false;
}
}
@ -199,7 +199,7 @@ lang=en`
try {
return this.getVideoStats().videoDecision === "copy" && this.getVideoStats().audioDecision === "copy";
} catch (e) {
console.log("Error at decision:" + e);
console.log("Error at decision:" , e);
return false;
}
}
@ -245,7 +245,7 @@ lang=en`
}
}.bind(this) )
} catch (e) {
console.log("Error at decision:" + e);
console.log("Error at decision:" , e);
}
this.log("Current video stats:")
@ -289,7 +289,11 @@ lang=en`
}
async getDecision(directPlay) {
async getDecisionUnmanaged(directPlay) {
if (this.settings.streamPath === 'direct') {
console.log("Skip get transcode decision because direct path is enabled");
return;
}
let res = await axios.get(`${this.server.uri}/video/:/transcode/universal/decision?${this.transcodingArgs}`, {
headers: { Accept: 'application/json' }
})
@ -307,6 +311,14 @@ lang=en`
}
}
async getDecision(directPlay) {
try {
await this.getDecisionUnmanaged(directPlay);
} catch (err) {
console.error(err);
}
}
getStatusUrl() {
let profileName=`Generic`;

View File

@ -1,11 +1,29 @@
const XMLWriter = require('xml-writer')
const fs = require('fs')
const helperFuncs = require('./helperFuncs')
const constants = require('./constants')
module.exports = { WriteXMLTV: WriteXMLTV }
module.exports = { WriteXMLTV: WriteXMLTV, shutdown: shutdown }
function WriteXMLTV(json, xmlSettings, throttle ) {
let isShutdown = false;
let isWorking = false;
async function WriteXMLTV(json, xmlSettings, throttle ) {
if (isShutdown) {
return;
}
if (isWorking) {
console.log("Concurrent xmltv write attempt detected, skipping");
return;
}
isWorking = true;
try {
await writePromise(json, xmlSettings, throttle);
} catch (err) {
console.error("Error writing xmltv", err);
}
isWorking = false;
}
function writePromise(json, xmlSettings, throttle) {
return new Promise((resolve, reject) => {
let ws = fs.createWriteStream(xmlSettings.file)
let xw = new XMLWriter(true, (str, enc) => ws.write(str, enc))
@ -59,7 +77,9 @@ function _writeChannels(xw, channels) {
async function _writePrograms(xw, channel, programs, throttle) {
for (let i = 0; i < programs.length; i++) {
await throttle();
if (! isShutdown) {
await throttle();
}
await _writeProgramme(channel, programs[i], xw);
}
}
@ -117,8 +137,25 @@ async function _writeProgramme(channel, program, xw) {
function _createXMLTVDate(d) {
return d.substring(0,19).replace(/[-T:]/g,"") + " +0000";
}
function _throttle() {
function wait(x) {
return new Promise((resolve) => {
setTimeout(resolve, 0);
setTimeout(resolve, x);
});
}
async function shutdown() {
isShutdown = true;
console.log("Shutting down xmltv writer.");
if (isWorking) {
let s = "Wait for xmltv writer...";
while (isWorking) {
console.log(s);
await wait(100);
s = "Still waiting for xmltv writer...";
}
console.log("Write finished.");
} else {
console.log("xmltv writer had no pending jobs.");
}
}

View File

@ -110,18 +110,23 @@ module.exports = function ($timeout, $location, dizquetv) {
scope._selectedProgram = null
updateChannelDuration()
}
scope.dropFunction = (dropIndex, index, program) => {
if (scope.channel.programs[index].start == program.start) {
return false;
scope.dropFunction = (dropIndex, program) => {
let y = program.$index;
let z = dropIndex + scope.currentStartIndex - 1;
scope.channel.programs.splice(y, 1);
if (z >= y) {
z--;
}
setTimeout( () => {
scope.channel.programs.splice(dropIndex + index, 0, program);
updateChannelDuration()
scope.$apply();
}, 1);
return true;
scope.channel.programs.splice(z, 0, program );
updateChannelDuration();
$timeout();
return false;
}
scope.setUpWatcher = function setupWatchers() {
this.$watch('vsRepeat.startIndex', function(val) {
scope.currentStartIndex = val;
});
};
let fixFillerCollection = (f) => {
return {
@ -283,6 +288,7 @@ module.exports = function ($timeout, $location, dizquetv) {
newProgs.push(tmpProgs[keys[i]])
}
scope.channel.programs = newProgs
updateChannelDuration(); //oops someone forgot to add this
}
scope.removeOffline = () => {
let tmpProgs = []
@ -1242,6 +1248,8 @@ module.exports = function ($timeout, $location, dizquetv) {
scope.minBreakSize = -1;
scope.maxBreakSize = -1;
let breakSizeOptions = [
{ id: 10, description: "10 seconds" },
{ id: 15, description: "15 seconds" },
{ id: 30, description: "30 seconds" },
{ id: 45, description: "45 seconds" },
{ id: 60, description: "60 seconds" },
@ -1250,8 +1258,9 @@ module.exports = function ($timeout, $location, dizquetv) {
{ id: 180, description: "3 minutes" },
{ id: 300, description: "5 minutes" },
{ id: 450, description: "7.5 minutes" },
{ id: 600, description: "10 minutes" },
{ id: 1200, description: "20 minutes" },
{ id: 10*60, description: "10 minutes" },
{ id: 20*60, description: "20 minutes" },
{ id: 30*60, description: "30 minutes" },
]
scope.minBreakSizeOptions = [
{ id: -1, description: "Min Duration" },

View File

@ -14,6 +14,40 @@ module.exports = function ($timeout) {
scope.visible = false;
scope.error = undefined;
function refreshContentIndexes() {
for (let i = 0; i < scope.content.length; i++) {
scope.content[i].$index = i;
}
}
scope.contentSplice = (a,b) => {
scope.content.splice(a,b)
refreshContentIndexes();
}
scope.dropFunction = (dropIndex, program) => {
let y = program.$index;
let z = dropIndex + scope.currentStartIndex - 1;
scope.content.splice(y, 1);
if (z >= y) {
z--;
}
scope.content.splice(z, 0, program );
$timeout();
return false;
}
scope.setUpWatcher = function setupWatchers() {
this.$watch('vsRepeat.startIndex', function(val) {
scope.currentStartIndex = val;
});
};
scope.movedFunction = (index) => {
console.log("movedFunction(" + index + ")");
}
scope.linker( (filler) => {
if ( typeof(filler) === 'undefined') {
scope.name = "";
@ -26,6 +60,7 @@ module.exports = function ($timeout) {
scope.id = filler.id;
scope.title = "Edit Filler List";
}
refreshContentIndexes();
scope.visible = true;
} );
@ -49,7 +84,10 @@ module.exports = function ($timeout) {
scope.visible = false;
scope.onDone( {
name: scope.name,
content: scope.content,
content: scope.content.map( (c) => {
delete c.$index
return c;
} ),
id: scope.id,
} );
}
@ -58,9 +96,11 @@ module.exports = function ($timeout) {
}
scope.sortFillers = () => {
scope.content.sort( (a,b) => { return a.duration - b.duration } );
refreshContentIndexes();
}
scope.fillerRemoveAllFiller = () => {
scope.content = [];
refreshContentIndexes();
}
scope.fillerRemoveDuplicates = () => {
function getKey(p) {
@ -77,12 +117,14 @@ module.exports = function ($timeout) {
}
}
scope.content = newFiller;
refreshContentIndexes();
}
scope.importPrograms = (selectedPrograms) => {
for (let i = 0, l = selectedPrograms.length; i < l; i++) {
selectedPrograms[i].commercials = []
}
scope.content = scope.content.concat(selectedPrograms);
refreshContentIndexes();
scope.showPlexLibrary = false;
}

View File

@ -25,7 +25,7 @@
</a>
</small>
<small class="pull-right" style="padding: 5px;">
<a href="https://discord.gg/U64P9MR" title='Discord' >
<a href="https://discord.gg/FUpCyZBTDM" title='Discord' >
<span class="fab fa-discord"></span>
</a>
</small>

View File

@ -432,11 +432,12 @@
</div>
<div class="list-group list-group-root">
<div vs-repeat="options" style='max-height: 30em; overflow-y: auto;'>
<div ng-repeat="x in channel.programs track by x.$index" ng-click="selectProgram(x.$index)" dnd-list="" dnd-drop="dropFunction(index , x.$index, item)" style="height: 1.5em; overflow:hidden">
<div vs-repeat style='max-height: 30em; overflow-y: auto;' ng-init="setUpWatcher()" dnd-list="" dnd-drop="dropFunction(index , item)" ng-if="true" >
<div ng-repeat="x in channel.programs track by x.$index" ng-click="selectProgram(x.$index)" style="height: 1.5em; overflow:hidden"
dnd-draggable="x" dnd-moved="" dnd-effect-allowed="move"
>
<div class="list-group-item flex-container program-row" dnd-draggable="x" dnd-moved="channel.programs.splice(x.$index, 1);" dnd-effect-allowed="move"
>
<div class="list-group-item flex-container program-row" >
<div class="program-start">
{{ dateForGuide(x.start) }}

View File

@ -23,7 +23,7 @@
<div>
<button class="btn btn-sm btn-secondary btn-programming-tools"
ng-click="showTools = !showTools"
ng-show="channel.programs.length !== 0">
ng-show="content.length !== 0">
<span
class="fa {{ showTools ? 'fa-chevron-down' : 'fa-chevron-right'}}"></span>&nbsp;&nbsp;Tools
</button>
@ -61,9 +61,19 @@
</div>
</div>
<div vs-repeat class="modal-body container list-group list-group-root filler-list"
dnd-list="content" ng-if="showList()"
vs-repeat-reinitialized="vsReinitialized(event, startIndex, endIndex)"
ng-init="setUpWatcher()"
dnd-drop="dropFunction(index , item)"
dnd-list=""
<div class="modal-body container list-group list-group-root filler-list" vs-repeat="options" dnd-list="content" ng-if="showList()">
<div class="list-group-item flex-container" style="cursor: default;" ng-repeat="x in content" dnd-draggable="x" dnd-moved="content.splice($index, 1)" dnd-effect-allowed="move">
>
<div class="list-group-item flex-container" style="cursor: default; height:1.1em; overflow:hidden" ng-repeat="x in content" track-by="x.$index" dnd-draggable="x"
"
dnd-effect-allowed="move"
dnd-moved="movedFunction(x.$index)"
>
<div class="program-start" >
{{durationString(x.duration)}}
</div>
@ -72,7 +82,7 @@
{{x.title}}
</div>
<div class="flex-pull-right">
<button class="btn btn-sm btn-link" ng-click="content.splice($index,1)">
<button class="btn btn-sm btn-link" ng-click="contentSplice(x.$index,1)">
<i class="text-danger fa fa-trash-alt" ></i>
</button>
</div>