diff --git a/.gitignore b/.gitignore
index 5255da1..a2ef243 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ dist/
bin/
.pseudotv/
.dizquetv/
-web/public/bundle.js
\ No newline at end of file
+web/public/bundle.js
+*.orig
\ No newline at end of file
diff --git a/README.md b/README.md
index 5d6c6f0..3bab198 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,11 @@ docker run --name dizquetv -p 8000:8000 -v C:\.dizquetv:/home/node/app/.dizquetv
If you were a pseudotv user, make sure to stop the pseudotv container and use the same folder you used for configuration in pseudotv as configuration for dizquetv.
+#### Unraid
+
+Template Repository: [https://github.com/vexorian/dizquetv/tree/main](https://github.com/vexorian/dizquetv/tree/main)
+
+
#### Building Docker image from source
Build docker image from source and run the container. (replace `C:\.dizquetv` with your desired config directory location)
diff --git a/dizquetv-nvidia.xml b/dizquetv-nvidia.xml
index 26a87e6..f55aeac 100644
--- a/dizquetv-nvidia.xml
+++ b/dizquetv-nvidia.xml
@@ -15,7 +15,7 @@ dizqueTV will show up as a HDHomeRun device within Plex. When configuring your P
http://[IP]:[PORT:8000]
- https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png
+ https://raw.githubusercontent.com/vexorian/dizquetv/main/resources/dizquetv.png--runtime=nvidia
diff --git a/dizquetv.xml b/dizquetv.xml
index da9178b..e939a5d 100644
--- a/dizquetv.xml
+++ b/dizquetv.xml
@@ -13,7 +13,7 @@
http://[IP]:[PORT:8000]
- https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png
+ https://raw.githubusercontent.com/vexorian/dizquetv/main/resources/dizquetv.png
diff --git a/index.js b/index.js
index b0150da..1336335 100644
--- a/index.js
+++ b/index.js
@@ -19,9 +19,9 @@ console.log(
` \\
dizqueTV ${constants.VERSION_NAME}
.------------.
-|###:::||| o |
-|###:::||| |
-'###:::||| o |
+|:::///### o |
+|:::///### |
+':::///### o |
'------------'
`);
@@ -102,6 +102,24 @@ xmltvInterval.startInterval()
let hdhr = HDHR(db)
let app = express()
app.use(bodyParser.json({limit: '50mb'}))
+app.get('/version.js', (req, res) => {
+ res.writeHead(200, {
+ 'Content-Type': 'application/javascript'
+ });
+
+ res.write( `
+ function setUIVersionNow() {
+ setTimeout( setUIVersionNow, 1000);
+ var element = document.getElementById("uiversion");
+ if (element != null) {
+ element.innerHTML = "${constants.VERSION_NAME}";
+ }
+ }
+ setTimeout( setUIVersionNow, 1000);
+ ` );
+ res.end();
+});
+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, xmltvInterval))
diff --git a/src/api.js b/src/api.js
index 5f0fe39..96c4f1d 100644
--- a/src/api.js
+++ b/src/api.js
@@ -3,15 +3,21 @@ const express = require('express')
const fs = require('fs')
const databaseMigration = require('./database-migration');
const channelCache = require('./channel-cache')
-const constants = require('./constants')
+const constants = require('./constants');
+const FFMPEGInfo = require('./ffmpeg-info');
module.exports = { router: api }
function api(db, xmltvInterval) {
let router = express.Router()
- router.get('/api/version', (req, res) => {
- res.send( { "dizquetv" : constants.VERSION_NAME } )
- })
+ router.get('/api/version', async (req, res) => {
+ let ffmpegSettings = db['ffmpeg-settings'].find()[0];
+ let v = await (new FFMPEGInfo(ffmpegSettings)).getVersion();
+ res.send( {
+ "dizquetv" : constants.VERSION_NAME,
+ "ffmpeg" : v,
+ } );
+ });
// Plex Servers
router.get('/api/plex-servers', (req, res) => {
@@ -177,13 +183,14 @@ function api(db, xmltvInterval) {
router.get('/api/channels.m3u', (req, res) => {
res.type('text')
let channels = db['channels'].find()
+ channels.sort((a, b) => { return a.number < b.number ? -1 : 1 })
var data = "#EXTM3U\n"
for (var i = 0; i < channels.length; i++) {
data += `#EXTINF:0 tvg-id="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n`
data += `${req.protocol}://${req.get('host')}/video?channel=${channels[i].number}\n`
}
if (channels.length === 0) {
- data += `#EXTINF:0 tvg-id="1" tvg-name="dizqueTV" tvg-logo="https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
+ data += `#EXTINF:0 tvg-id="1" tvg-name="dizqueTV" tvg-logo="https://raw.githubusercontent.com/vexorian/dizquetv/main/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
data += `${req.protocol}://${req.get('host')}/setup\n`
}
res.send(data)
diff --git a/src/channel-cache.js b/src/channel-cache.js
index d6b03f0..4af4272 100644
--- a/src/channel-cache.js
+++ b/src/channel-cache.js
@@ -27,7 +27,7 @@ function getCurrentLineupItem(channelId, t1) {
let recorded = cache[channelId];
let lineupItem = JSON.parse( JSON.stringify(recorded.lineupItem) );
let diff = t1 - recorded.t0;
- if (diff <= SLACK) {
+ if ( (diff <= SLACK) && (lineupItem.actualDuration >= 2*SLACK) ) {
//closed the stream and opened it again let's not lose seconds for
//no reason
return lineupItem;
diff --git a/src/constants.js b/src/constants.js
index 4a3a1c9..5cffd7c 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.61"
+ VERSION_NAME: "0.0.62-prerelease"
}
diff --git a/src/ffmpeg-info.js b/src/ffmpeg-info.js
new file mode 100644
index 0000000..ef264fd
--- /dev/null
+++ b/src/ffmpeg-info.js
@@ -0,0 +1,26 @@
+const exec = require('child_process').exec;
+
+class FFMPEGInfo {
+ constructor(opts) {
+ this.ffmpegPath = opts.ffmpegPath
+ }
+ async getVersion() {
+ try {
+ let s = await new Promise( (resolve, reject) => {
+ exec( `"${this.ffmpegPath}" -version`, function(error, stdout, stderr){
+ if (error !== null) {
+ reject(error);
+ } else {
+ resolve(stdout);
+ }
+ });
+ });
+ return s.match( /version ([^\s]+) Copyright/ )[1];
+ } catch (err) {
+ console.error("Error getting ffmpeg version", err);
+ return "Error";
+ }
+ }
+}
+
+module.exports = FFMPEGInfo
\ No newline at end of file
diff --git a/src/ffmpeg.js b/src/ffmpeg.js
index 3dfa9f5..0d0d4bf 100644
--- a/src/ffmpeg.js
+++ b/src/ffmpeg.js
@@ -1,8 +1,6 @@
const spawn = require('child_process').spawn
const events = require('events')
-//they can customize this by modifying the picture in .dizquetv folder
-
const MAXIMUM_ERROR_DURATION_MS = 60000;
class FFMPEG extends events.EventEmitter {
diff --git a/src/program-player.js b/src/program-player.js
index f3354ab..b3b727b 100644
--- a/src/program-player.js
+++ b/src/program-player.js
@@ -29,6 +29,11 @@ class ProgramPlayer {
constructor( context ) {
this.context = context;
let program = context.lineupItem;
+ if (context.m3u8) {
+ context.ffmpegSettings.normalizeAudio = false;
+ // people might want the codec normalization to stay because of player support
+ context.ffmpegSettings.normalizeResolution = false;
+ }
if (program.err instanceof Error) {
console.log("About to play error stream");
this.delegate = new OfflinePlayer(true, context);
diff --git a/src/video.js b/src/video.js
index 2c4a886..7899d3f 100644
--- a/src/video.js
+++ b/src/video.js
@@ -135,7 +135,7 @@ function video(db) {
res.status(400).send("No Channel Specified")
return
}
-
+ let m3u8 = (req.query.m3u8 === '1');
let number = parseInt(req.query.channel);
let channel = channelCache.getChannelConfig(db, number);
@@ -228,6 +228,7 @@ function video(db) {
ffmpegSettings : ffmpegSettings,
channel: channel,
db: db,
+ m3u8: m3u8,
}
let player = new ProgramPlayer(playerContext);
@@ -320,11 +321,11 @@ function video(db) {
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
if ( ffmpegSettings.enableFFMPEGTranscoding === true) {
- data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=0\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=1\n`
+ data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&first=1&m3u8=1\n`
for (var i = 0; i < maxStreamsToPlayInARow - 1; i++) {
- data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}\n`
+ data += `${req.protocol}://${req.get('host')}/stream?channel=${channelNum}&m3u8=1\n`
}
res.send(data)
@@ -353,7 +354,13 @@ function video(db) {
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
- if ( ffmpegSettings.enableFFMPEGTranscoding === true) {
+ if (
+ (ffmpegSettings.enableFFMPEGTranscoding === true)
+ && (ffmpegSettings.normalizeVideoCodec === true)
+ && (ffmpegSettings.normalizeAudioCodec === true)
+ && (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=1'\n`
diff --git a/src/xmltv.js b/src/xmltv.js
index df8cffb..5901527 100644
--- a/src/xmltv.js
+++ b/src/xmltv.js
@@ -15,7 +15,7 @@ function WriteXMLTV(channels, xmlSettings) {
_writeDocStart(xw)
async function middle() {
if (channels.length === 0) { // Write Dummy dizqueTV Channel if no channel exists
- _writeChannels(xw, [{ number: 1, name: "dizqueTV", icon: "https://raw.githubusercontent.com/vexorain/dizquetv/master/resources/dizquetv.png" }])
+ _writeChannels(xw, [{ number: 1, name: "dizqueTV", icon: "https://raw.githubusercontent.com/vexorain/dizquetv/main/resources/dizquetv.png" }])
let program = {
program: {
type: 'movie',
diff --git a/web/controllers/version.js b/web/controllers/version.js
index cd11aac..8f0f35d 100644
--- a/web/controllers/version.js
+++ b/web/controllers/version.js
@@ -1,7 +1,9 @@
module.exports = function ($scope, dizquetv) {
$scope.version = "Getting dizqueTV version..."
+ $scope.ffmpegVersion = "Getting ffmpeg version..."
dizquetv.getVersion().then((version) => {
- $scope.version = version.dizquetv
+ $scope.version = version.dizquetv;
+ $scope.ffmpegVersion = version.ffmpeg;
})
diff --git a/web/directives/channel-config.js b/web/directives/channel-config.js
index 0d542ad..ca75418 100644
--- a/web/directives/channel-config.js
+++ b/web/directives/channel-config.js
@@ -137,7 +137,7 @@ module.exports = function ($timeout, $location) {
isOffline: true
}
scope.updateChannelFromOfflineResult(result);
- scope.channel.programs.push( program );
+ scope.channel.programs.splice(scope.minProgramIndex, 0, program);
scope._selectedOffline = null
scope._addingOffline = null;
updateChannelDuration()
@@ -268,6 +268,20 @@ module.exports = function ($timeout, $location) {
updateChannelDuration()
}
+ scope.wipeSpecials = () => {
+ let tmpProgs = []
+ let progs = scope.channel.programs
+ for (let i = 0, l = progs.length; i < l; i++) {
+ if (progs[i].season !== 0) {
+ tmpProgs.push(progs[i]);
+ }
+ }
+ scope.channel.programs = tmpProgs
+ updateChannelDuration()
+ }
+
+
+
scope.describeFallback = () => {
if (scope.channel.offlineMode === 'pic') {
if (
diff --git a/web/directives/plex-library.js b/web/directives/plex-library.js
index 2e32c3f..272d783 100644
--- a/web/directives/plex-library.js
+++ b/web/directives/plex-library.js
@@ -10,6 +10,7 @@ module.exports = function (plex, dizquetv, $timeout) {
limit: "@limit",
},
link: function (scope, element, attrs) {
+ scope.errors=[];
if ( typeof(scope.limit) == 'undefined') {
scope.limit = 1000000000;
}
@@ -45,8 +46,12 @@ module.exports = function (plex, dizquetv, $timeout) {
await scope.wait(0);
scope.pending += 1;
try {
- item.streams = await plex.getStreams(scope.plexServer, item.key)
+ item.streams = await plex.getStreams(scope.plexServer, item.key, scope.errors)
scope.selection.push(JSON.parse(angular.toJson(item)))
+ } catch (err) {
+ let msg = "Unable to add item: " + item.key + " " + item.title;
+ scope.errors.push(msg);
+ console.error(msg, err);
} finally {
scope.pending -= 1;
}
@@ -99,7 +104,7 @@ module.exports = function (plex, dizquetv, $timeout) {
}
scope.fillNestedIfNecessary = async (x, isLibrary) => {
if ( (typeof(x.nested) === 'undefined') && (x.type !== 'collection') ) {
- x.nested = await plex.getNested(scope.plexServer, x.key, isLibrary);
+ x.nested = await plex.getNested(scope.plexServer, x.key, isLibrary, scope.errors);
}
}
scope.getNested = (list, isLibrary) => {
diff --git a/web/public/index.html b/web/public/index.html
index 6cea21f..ed623f3 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -6,6 +6,7 @@
+
diff --git a/web/public/templates/channel-config.html b/web/public/templates/channel-config.html
index c2624e1..1652b04 100644
--- a/web/public/templates/channel-config.html
+++ b/web/public/templates/channel-config.html
@@ -139,7 +139,7 @@
Similar to Balance TV Shows, but this allows you to pick the weights for each of the shows, so you can decide that some shows should be less frequent than other shows. It has similar caveats as "Balance Shows".
Add Flex
-
Adds a "Flex" Time Slot. Can be configured to play a fallback screen and/or random "filler" content (e.g "commercials", trailers, prerolls, countdowns, music videos, channel bumpers, etc.). Short Flex periods are hidden from the TV guide and are displayed as extensions to the previous program. Long Flex periods appear as the channel name in the TV guide.
+
Adds a "Flex" Time Slot. Can be configured to play a fallback screen and/or random "filler" content (e.g "commercials", trailers, prerolls, countdowns, music videos, channel bumpers, etc.). Short Flex periods are hidden from the TV guide and are displayed as extensions to the previous program. Long Flex periods appear as the channel name in the TV guide. Normally this is not the best way to add Flex time, and you'd be better off using the Pad Times, Restrict Hours or Add Breaks features. This one is for adding specific, single instances of flex time.
Pad Times
Adds Flex breaks after each TV episode or movie to ensure that the program starts at one of the allowed minute marks. For example, you can use this to ensure that all your programs start at either XX:00 times or XX:30 times. Removes any existing Flex periods before adding the new ones.
@@ -156,6 +156,9 @@
Remove Flex
Removes any Flex periods from the schedule.
+
Remove Specials
+
Removes any specials from the schedule. Specials are episodes with season "00".
+
Remove All
Wipes out the schedule so that you can start over.
- FFMPEG version 4.2+ required. Check by running '{{settings.ffmpegPath}} -version' from the command line
+ FFMPEG version 4.2+ required. Check by opening the version tab