Skip to content

Commit

Permalink
1.5.4
Browse files Browse the repository at this point in the history
  • Loading branch information
vexorian committed Oct 21, 2024
1 parent 0ea9227 commit f59cca4
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 29 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# dizqueTV 1.5.3
# dizqueTV 1.5.4
![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.
Expand Down Expand Up @@ -33,6 +33,7 @@ EPG (Guide Information) data is stored to `.dizquetv/xmltv.xml`
- dizqueTV does not currently watch your Plex server for media updates/changes. You must manually remove and re-add your programs for any changes to take effect. Same goes for Plex server changes (changing IP, port, etc).. You'll have to update the server settings manually in that case.
- Most players (including Plex) will break after switching episodes if video / audio format is too different. dizqueTV can be configured to use ffmpeg transcoding to prevent this, but that costs resources.
- If you configure Plex DVR, it will always be recording and transcoding the channel's contents.
- In its current state, dizquetv is intended for private use only and you should be discouraged from running dizqueTV in any capacity where other users can have access to dizqueTV's ports. You can use Plex's iptv player feature to share dizqueTV streams or you'll have to come up with some work arounds to make sure that streams can be played without ever actually exposing the dizquetv port to the outside world. Please use it with care, consider exposing dizqueTV's ports as something only advanced users who know what they are doing should try.

## Releases

Expand Down
9 changes: 7 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const OnDemandService = require("./src/services/on-demand-service");
const ProgrammingService = require("./src/services/programming-service");
const ActiveChannelService = require('./src/services/active-channel-service')
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service')

const onShutdown = require("node-graceful-shutdown").onShutdown;

Expand All @@ -50,12 +51,15 @@ if (NODE < 12) {
console.error(`WARNING: Your nodejs version ${process.version} is lower than supported. dizqueTV has been tested best on nodejs 12.16.`);
}


process.env.unlock = false;
for (let i = 0, l = process.argv.length; i < l; i++) {
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
process.env.PORT = process.argv[i + 1]
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
process.env.DATABASE = process.argv[i + 1]
if (process.argv[i] === "--unlock") {
process.env.unlock = true;
}
}

process.env.DATABASE = process.env.DATABASE || path.join(".", ".dizquetv")
Expand Down Expand Up @@ -101,6 +105,7 @@ channelService = new ChannelService(channelDB);
fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelService );
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
let ffmpegSettingsService = new FfmpegSettingsService(db, process.env.unlock);

async function initializeProgramPlayTimeDB() {
try {
Expand Down Expand Up @@ -284,7 +289,7 @@ app.use('/favicon.svg', express.static(
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))

// API Routers
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService ))
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService))
app.use('/api/cache/images', cacheImageService.apiRouters())
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
Expand Down
27 changes: 8 additions & 19 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
const express = require('express')
const path = require('path')
const fs = require('fs')
const databaseMigration = require('./database-migration');
const constants = require('./constants');
const JSONStream = require('JSONStream');
const FFMPEGInfo = require('./ffmpeg-info');
Expand All @@ -25,7 +24,7 @@ function safeString(object) {
}

module.exports = { router: api }
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService ) {
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService ) {
let m3uService = _m3uService;
const router = express.Router()
const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
Expand Down Expand Up @@ -529,7 +528,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
// FFMPEG SETTINGS
router.get('/api/ffmpeg-settings', (req, res) => {
try {
let ffmpeg = db['ffmpeg-settings'].find()[0]
let ffmpeg = ffmpegSettingsService.get();
res.send(ffmpeg)
} catch(err) {
console.error(err);
Expand All @@ -538,9 +537,9 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
})
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]
let err = fixupFFMPEGSettings(ffmpeg);
let result = ffmpegSettingsService.update(req.body);
let err = result.error

if (typeof(err) !== 'undefined') {
return res.status(400).send(err);
}
Expand All @@ -555,7 +554,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
"level" : "info"
}
);
res.send(ffmpeg)
res.send(result.ffmpeg)
} catch(err) {
console.error(err);
res.status(500).send("error");
Expand All @@ -576,10 +575,8 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
})
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]
let ffmpeg = ffmpegSettingsService.reset();

eventService.push(
"settings-update",
{
Expand Down Expand Up @@ -612,14 +609,6 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe

})

function fixupFFMPEGSettings(ffmpeg) {
if (typeof(ffmpeg.maxFPS) === 'undefined') {
ffmpeg.maxFPS = 60;
} else if ( isNaN(ffmpeg.maxFPS) ) {
return "maxFPS should be a number";
}
}

// PLEX SETTINGS
router.get('/api/plex-settings', (req, res) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ module.exports = {
// staying active, it checks every 5 seconds
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,

VERSION_NAME: "1.5.3"
VERSION_NAME: "1.5.4"
}
19 changes: 17 additions & 2 deletions src/database-migration.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
const path = require('path');
var fs = require('fs');

const TARGET_VERSION = 803;
const TARGET_VERSION = 804;
const DAY_MS = 1000 * 60 * 60 * 24;

const STEPS = [
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
Expand All @@ -43,6 +44,7 @@ const STEPS = [
[ 800, 801, (db) => addImageCache(db) ],
[ 801, 802, () => addGroupTitle() ],
[ 802, 803, () => fixNonIntegerDurations() ],
[ 803, 804, (db) => addFFMpegLock(db) ],
]

const { v4: uuidv4 } = require('uuid');
Expand Down Expand Up @@ -384,7 +386,7 @@ function ffmpeg() {
//How default ffmpeg settings should look
configVersion: 5,
ffmpegPath: "/usr/bin/ffmpeg",
threads: 4,
pathLockDate: new Date().getTime() + DAY_MS,
concatMuxDelay: "0",
logFfmpeg: false,
enableFFMPEGTranscoding: true,
Expand Down Expand Up @@ -765,6 +767,19 @@ function addScalingAlgorithm(db) {
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
}

function addFFMpegLock(db) {
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
if ( typeof(ffmpegSettings.pathLockDate) === 'undefined' || ffmpegSettings.pathLockDate == null ) {

console.log("Adding ffmpeg lock. For your security it will not be possible to modify the ffmpeg path using the UI anymore unless you launch dizquetv by following special instructions..");
// We are migrating an existing db that had a ffmpeg path. Make sure
// it's already locked.
ffmpegSettings.pathLockDate = new Date().getTime() - 2 * DAY_MS;
}
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
}

function moveBackup(path) {
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
let i = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/ffmpeg-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class FFMPEGInfo {
var m = s.match( /version\s+([^\s]+)\s+.*Copyright/ )
if (m == null) {
console.error("ffmpeg -version command output not in the expected format: " + s);
return s;
return "Unknown";
}
return m[1];
} catch (err) {
Expand Down
123 changes: 123 additions & 0 deletions src/services/ffmpeg-settings-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const databaseMigration = require('../database-migration');
const DAY_MS = 1000 * 60 * 60 * 24;
const path = require('path');
const fs = require('fs');

class FfmpegSettingsService {
constructor(db, unlock) {
this.db = db;
if (unlock) {
this.unlock();
}
}

get() {
let ffmpeg = this.getCurrentState();
if (isLocked(ffmpeg)) {
ffmpeg.lock = true;
}
// Hid this info from the API
delete ffmpeg.pathLockDate;
return ffmpeg;
}

unlock() {
let ffmpeg = this.getCurrentState();
console.log("ffmpeg path UI unlocked for another day...");
ffmpeg.pathLockDate = new Date().getTime() + DAY_MS;
this.db['ffmpeg-settings'].update({ _id: ffmpeg._id }, ffmpeg)
}


update(attempt) {
let ffmpeg = this.getCurrentState();
attempt.pathLockDate = ffmpeg.pathLockDate;
if (isLocked(ffmpeg)) {
console.log("Note: ffmpeg path is not being updated since it's been locked for your security.");
attempt.ffmpegPath = ffmpeg.ffmpegPath;
if (typeof(ffmpeg.pathLockDate) === 'undefined') {
// make sure to lock it even if it was undefined
attempt.pathLockDate = new Date().getTime() - DAY_MS;
}
} else if (attempt.addLock === true) {
// lock it right now
attempt.pathLockDate = new Date().getTime() - DAY_MS;
} else {
attempt.pathLockDate = new Date().getTime() + DAY_MS;
}
delete attempt.addLock;
delete attempt.lock;

let err = fixupFFMPEGSettings(attempt);
if ( typeof(err) !== "undefined" ) {
return {
error: err
}
}

this.db['ffmpeg-settings'].update({ _id: ffmpeg._id }, attempt)
return {
ffmpeg: this.get()
}
}

reset() {
// Even if reseting, it's impossible to unlock the ffmpeg path
let ffmpeg = databaseMigration.defaultFFMPEG() ;
this.update(ffmpeg);
return this.get();
}

getCurrentState() {
return this.db['ffmpeg-settings'].find()[0]
}


}

function fixupFFMPEGSettings(ffmpeg) {
if (typeof(ffmpeg.ffmpegPath) !== 'string') {
return "ffmpeg path is required."
}
if (! isValidFilePath(ffmpeg.ffmpegPath)) {
return "ffmpeg path must be a valid file path."
}

if (typeof(ffmpeg.maxFPS) === 'undefined') {
ffmpeg.maxFPS = 60;
return null;
} else if ( isNaN(ffmpeg.maxFPS) ) {
return "maxFPS should be a number";
}
}

//These checks are good but might not be enough, as long as we are letting the
//user choose any path and we are making dizqueTV execute, it is too risky,
//hence why we are also adding the lock feature on top of these checks.
function isValidFilePath(filePath) {
const normalizedPath = path.normalize(filePath);

if (!path.isAbsolute(normalizedPath)) {
return false;
}

try {
const stats = fs.statSync(normalizedPath);
return stats.isFile();
} catch (err) {
// Handle potential errors (e.g., file not found, permission issues)
if (err.code === 'ENOENT') {
return false; // File does not exist
} else {
throw err; // Re-throw other errors for debugging
}
}
}

function isLocked(ffmpeg) {
return isNaN(ffmpeg.pathLockDate) || ffmpeg.pathLockDate < new Date().getTime();
}



module.exports =FfmpegSettingsService;
5 changes: 5 additions & 0 deletions web/directives/ffmpeg-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ module.exports = function (dizquetv, resolutionOptions) {
scope.settings = settings
})
scope.updateSettings = (settings) => {
delete scope.settingsError;
dizquetv.updateFfmpegSettings(settings).then((_settings) => {
scope.settings = _settings
}).catch( (err) => {
if ( typeof(err.data) === "string") {
scope.settingsError = err.data;
}
})
}
scope.resetSettings = (settings) => {
Expand Down
49 changes: 46 additions & 3 deletions web/public/templates/ffmpeg-settings.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<div>
<small class="text-danger" nf-show="settingsError">
{{ settingsError }}
</small>

<h5>FFMPEG Settings

<button class="pull-right btn btn-sm btn-success" style="margin-left: 5px;" ng-click="updateSettings(settings)">
Expand All @@ -8,9 +12,48 @@ <h5>FFMPEG Settings
Reset Options
</button>
</h5>
<h6>FFMPEG Executable Path (eg: C:\ffmpeg\bin\ffmpeg.exe || /usr/bin/ffmpeg)</h6>
<input type="text" class="form-control form-control-sm" ria-describedby="ffmpegHelp" ng-model="settings.ffmpegPath"></input>
<small id="ffmpegHelp" class="form-text text-muted">FFMPEG version 4.2+ required. Check by opening the version tab</small>

<hr></hr>
<h6>FFMPEG Executable Path</h6>
<div class="row" ng-show="settings.lock !== true">
<div class="col-sm-9">
<div class="form-group">
<div class="form-group">
<label>Path</label>
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath"></input>
<small class="form-text text-muted">
The path to the ffmpeg executable. (e.g: /usr/bin/ffmpeg or C:\ffmpeg\bin\ffmpeg.exe) FFMPEG version 4.2+ required. Check by opening the version tab.

</small>

</div>
</div>
</div>
<div class="col-sm-9">
<div class="form-group">
<input id="lockFfmpeg" type="checkbox" ng-model="settings.addLock"></input>
<label for="lockFfmpeg">Lock ffmpeg path setting</label>
<small class="form-text text-muted">This will lock the ffmpeg path setting so that it is no longer editable from UI. Even if you don't toggle this option, the setting will get locked in 24 hours.</small>
</div>
</div>
</div>


<div class="row" ng-show="settings.lock === true">
<div class="col-sm-9">
<div class="form-group">
<div class="form-group">
<label>Path</label>
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath" readonly></input>
<small class="form-text text-muted">
The ffmpeg path setting is currently locked and can't be edited from the UI. It's not usually necessary to update this path once it's known to be working. Run dizquetv with the <b>--unlock</b> command line argument to enable editing it again.
</small>

</div>
</div>
</div>
</div>

<hr></hr>
<h6>Miscellaneous Options</h6>
<div class="row">
Expand Down

0 comments on commit f59cca4

Please sign in to comment.