Merge commit 'f59cca4ea4f141b220e4586100ba541b2ba8fe96' into edge
This commit is contained in:
commit
7e9db8c1f6
@ -1,4 +1,4 @@
|
|||||||
# dizqueTV 1.5.3
|
# dizqueTV 1.5.4
|
||||||
  
|
  
|
||||||
|
|
||||||
Create live TV channel streams from media on your Plex servers.
|
Create live TV channel streams from media on your Plex servers.
|
||||||
@ -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.
|
- 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.
|
- 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.
|
- 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
|
## Releases
|
||||||
|
|
||||||
|
|||||||
9
index.js
9
index.js
@ -31,6 +31,7 @@ const OnDemandService = require("./src/services/on-demand-service");
|
|||||||
const ProgrammingService = require("./src/services/programming-service");
|
const ProgrammingService = require("./src/services/programming-service");
|
||||||
const ActiveChannelService = require('./src/services/active-channel-service')
|
const ActiveChannelService = require('./src/services/active-channel-service')
|
||||||
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
|
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
|
||||||
|
const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service')
|
||||||
|
|
||||||
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
||||||
|
|
||||||
@ -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.`);
|
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++) {
|
for (let i = 0, l = process.argv.length; i < l; i++) {
|
||||||
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
|
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
|
||||||
process.env.PORT = process.argv[i + 1]
|
process.env.PORT = process.argv[i + 1]
|
||||||
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
|
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
|
||||||
process.env.DATABASE = process.argv[i + 1]
|
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")
|
process.env.DATABASE = process.env.DATABASE || path.join(".", ".dizquetv")
|
||||||
@ -101,6 +105,7 @@ channelService = new ChannelService(channelDB);
|
|||||||
fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelService );
|
fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelService );
|
||||||
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
|
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
|
||||||
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
|
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
|
||||||
|
let ffmpegSettingsService = new FfmpegSettingsService(db, process.env.unlock);
|
||||||
|
|
||||||
async function initializeProgramPlayTimeDB() {
|
async function initializeProgramPlayTimeDB() {
|
||||||
try {
|
try {
|
||||||
@ -284,7 +289,7 @@ app.use('/favicon.svg', express.static(
|
|||||||
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))
|
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))
|
||||||
|
|
||||||
// API Routers
|
// 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('/api/cache/images', cacheImageService.apiRouters())
|
||||||
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
|
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
|
||||||
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
|
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
|
||||||
|
|||||||
27
src/api.js
27
src/api.js
@ -2,7 +2,6 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const databaseMigration = require('./database-migration');
|
|
||||||
const constants = require('./constants');
|
const constants = require('./constants');
|
||||||
const JSONStream = require('JSONStream');
|
const JSONStream = require('JSONStream');
|
||||||
const FFMPEGInfo = require('./ffmpeg-info');
|
const FFMPEGInfo = require('./ffmpeg-info');
|
||||||
@ -25,7 +24,7 @@ function safeString(object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { router: api }
|
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;
|
let m3uService = _m3uService;
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
|
const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
|
||||||
@ -529,7 +528,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
// FFMPEG SETTINGS
|
// FFMPEG SETTINGS
|
||||||
router.get('/api/ffmpeg-settings', (req, res) => {
|
router.get('/api/ffmpeg-settings', (req, res) => {
|
||||||
try {
|
try {
|
||||||
let ffmpeg = db['ffmpeg-settings'].find()[0]
|
let ffmpeg = ffmpegSettingsService.get();
|
||||||
res.send(ffmpeg)
|
res.send(ffmpeg)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -538,9 +537,9 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
})
|
})
|
||||||
router.put('/api/ffmpeg-settings', (req, res) => {
|
router.put('/api/ffmpeg-settings', (req, res) => {
|
||||||
try {
|
try {
|
||||||
db['ffmpeg-settings'].update({ _id: req.body._id }, req.body)
|
let result = ffmpegSettingsService.update(req.body);
|
||||||
let ffmpeg = db['ffmpeg-settings'].find()[0]
|
let err = result.error
|
||||||
let err = fixupFFMPEGSettings(ffmpeg);
|
|
||||||
if (typeof(err) !== 'undefined') {
|
if (typeof(err) !== 'undefined') {
|
||||||
return res.status(400).send(err);
|
return res.status(400).send(err);
|
||||||
}
|
}
|
||||||
@ -555,7 +554,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
"level" : "info"
|
"level" : "info"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
res.send(ffmpeg)
|
res.send(result.ffmpeg)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.status(500).send("error");
|
res.status(500).send("error");
|
||||||
@ -576,10 +575,8 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
})
|
})
|
||||||
router.post('/api/ffmpeg-settings', (req, res) => { // RESET
|
router.post('/api/ffmpeg-settings', (req, res) => { // RESET
|
||||||
try {
|
try {
|
||||||
let ffmpeg = databaseMigration.defaultFFMPEG() ;
|
let ffmpeg = ffmpegSettingsService.reset();
|
||||||
ffmpeg.ffmpegPath = req.body.ffmpegPath;
|
|
||||||
db['ffmpeg-settings'].update({ _id: req.body._id }, ffmpeg)
|
|
||||||
ffmpeg = db['ffmpeg-settings'].find()[0]
|
|
||||||
eventService.push(
|
eventService.push(
|
||||||
"settings-update",
|
"settings-update",
|
||||||
{
|
{
|
||||||
@ -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
|
// PLEX SETTINGS
|
||||||
router.get('/api/plex-settings', (req, res) => {
|
router.get('/api/plex-settings', (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -35,5 +35,5 @@ module.exports = {
|
|||||||
// staying active, it checks every 5 seconds
|
// staying active, it checks every 5 seconds
|
||||||
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,
|
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,
|
||||||
|
|
||||||
VERSION_NAME: "1.5.3"
|
VERSION_NAME: "1.5.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
|
||||||
const TARGET_VERSION = 803;
|
const TARGET_VERSION = 804;
|
||||||
|
const DAY_MS = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
const STEPS = [
|
const STEPS = [
|
||||||
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
|
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
|
||||||
@ -43,6 +44,7 @@ const STEPS = [
|
|||||||
[ 800, 801, (db) => addImageCache(db) ],
|
[ 800, 801, (db) => addImageCache(db) ],
|
||||||
[ 801, 802, () => addGroupTitle() ],
|
[ 801, 802, () => addGroupTitle() ],
|
||||||
[ 802, 803, () => fixNonIntegerDurations() ],
|
[ 802, 803, () => fixNonIntegerDurations() ],
|
||||||
|
[ 803, 804, (db) => addFFMpegLock(db) ],
|
||||||
]
|
]
|
||||||
|
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
@ -384,7 +386,7 @@ function ffmpeg() {
|
|||||||
//How default ffmpeg settings should look
|
//How default ffmpeg settings should look
|
||||||
configVersion: 5,
|
configVersion: 5,
|
||||||
ffmpegPath: "/usr/bin/ffmpeg",
|
ffmpegPath: "/usr/bin/ffmpeg",
|
||||||
threads: 4,
|
pathLockDate: new Date().getTime() + DAY_MS,
|
||||||
concatMuxDelay: "0",
|
concatMuxDelay: "0",
|
||||||
logFfmpeg: false,
|
logFfmpeg: false,
|
||||||
enableFFMPEGTranscoding: true,
|
enableFFMPEGTranscoding: true,
|
||||||
@ -765,6 +767,19 @@ function addScalingAlgorithm(db) {
|
|||||||
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
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) {
|
function moveBackup(path) {
|
||||||
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
|
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class FFMPEGInfo {
|
|||||||
var m = s.match( /version\s+([^\s]+)\s+.*Copyright/ )
|
var m = s.match( /version\s+([^\s]+)\s+.*Copyright/ )
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
console.error("ffmpeg -version command output not in the expected format: " + s);
|
console.error("ffmpeg -version command output not in the expected format: " + s);
|
||||||
return s;
|
return "Unknown";
|
||||||
}
|
}
|
||||||
return m[1];
|
return m[1];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
123
src/services/ffmpeg-settings-service.js
Normal file
123
src/services/ffmpeg-settings-service.js
Normal file
@ -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;
|
||||||
@ -11,8 +11,13 @@ module.exports = function (dizquetv, resolutionOptions) {
|
|||||||
scope.settings = settings
|
scope.settings = settings
|
||||||
})
|
})
|
||||||
scope.updateSettings = (settings) => {
|
scope.updateSettings = (settings) => {
|
||||||
|
delete scope.settingsError;
|
||||||
dizquetv.updateFfmpegSettings(settings).then((_settings) => {
|
dizquetv.updateFfmpegSettings(settings).then((_settings) => {
|
||||||
scope.settings = _settings
|
scope.settings = _settings
|
||||||
|
}).catch( (err) => {
|
||||||
|
if ( typeof(err.data) === "string") {
|
||||||
|
scope.settingsError = err.data;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
scope.resetSettings = (settings) => {
|
scope.resetSettings = (settings) => {
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<small class="text-danger" nf-show="settingsError">
|
||||||
|
{{ settingsError }}
|
||||||
|
</small>
|
||||||
|
|
||||||
<h5>FFMPEG Settings
|
<h5>FFMPEG Settings
|
||||||
|
|
||||||
<button class="pull-right btn btn-sm btn-success" style="margin-left: 5px;" ng-click="updateSettings(settings)">
|
<button class="pull-right btn btn-sm btn-success" style="margin-left: 5px;" ng-click="updateSettings(settings)">
|
||||||
@ -8,9 +12,48 @@
|
|||||||
Reset Options
|
Reset Options
|
||||||
</button>
|
</button>
|
||||||
</h5>
|
</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>
|
<hr></hr>
|
||||||
<small id="ffmpegHelp" class="form-text text-muted">FFMPEG version 4.2+ required. Check by opening the version tab</small>
|
<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>
|
<hr></hr>
|
||||||
<h6>Miscellaneous Options</h6>
|
<h6>Miscellaneous Options</h6>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='form-group col-md-7' ng-if="schedule.randomDistribution == 'weighted'" >
|
<div class='form-group col-md-7' ng-if="schedule.randomDistribution == 'weighted'" >
|
||||||
<label ng-if="$index==0" for="weightRange{{$index}}">Weight</label>
|
<label ng-if="$index==0" for="weightRange{{$index}}">Weight</label>
|
||||||
<input class='form-control-range custom-range' id="weightRange{{$index}}" type='range' ng-model='slot.weight' min=1 max=600
|
<input class='form-control-range custom-range' id="weightRange{{$index}}" type='range' ng-model='slot.weight' min=1 max=60000
|
||||||
data-toggle="tooltip" data-placement="bottom" title="Slots with more weight will be picked more frequently."
|
data-toggle="tooltip" data-placement="bottom" title="Slots with more weight will be picked more frequently."
|
||||||
ng-change="refreshSlots()"
|
ng-change="refreshSlots()"
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user