Merge pull request #241 from vexorian/20210117_dev

20210117 dev
This commit is contained in:
vexorian 2021-01-17 16:34:31 -04:00 committed by GitHub
commit e64dc93dca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 198 additions and 58 deletions

View File

@ -187,7 +187,11 @@ app.listen(process.env.PORT, () => {
})
function initDB(db, channelDB) {
dbMigration.initDB(db, channelDB);
if (!fs.existsSync(process.env.DATABASE + '/images/dizquetv.png')) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/dizquetv.png')))
fs.writeFileSync(process.env.DATABASE + '/images/dizquetv.png', data)
}
dbMigration.initDB(db, channelDB, __dirname);
if (!fs.existsSync(process.env.DATABASE + '/font.ttf')) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/font.ttf')))
fs.writeFileSync(process.env.DATABASE + '/font.ttf', data)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -29,7 +29,7 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.0547013"
inkscape:cx="173.01248"
inkscape:cx="55.816079"
inkscape:cy="84.726326"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
@ -48,7 +48,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -58,7 +58,7 @@
id="layer1"
transform="translate(0,-244.08278)">
<rect
style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:1.46508551;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
style="opacity:1;fill:#a1a1a1;fill-opacity:0.86666667;stroke:none;stroke-width:1.46508551;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4712"
width="52.211964"
height="51.512306"
@ -67,7 +67,7 @@
transform="matrix(0.99980416,-0.01978974,0.00448328,0.99998995,0,0)" />
<g
id="g4581"
style="fill:#1f1f1f;fill-opacity:1;stroke-width:0.68901283"
style="fill:#080808;fill-opacity:1;stroke-width:0.68901283"
transform="matrix(1.2119871,0,0,1.7379906,-82.577875,-167.18505)">
<rect
transform="rotate(-0.94645665)"
@ -76,7 +76,7 @@
height="27.75024"
width="41.471352"
id="rect4524"
style="opacity:1;fill:#1f1f1f;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
style="opacity:1;fill:#080808;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<rect
style="opacity:1;fill:#9cbc28;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -20,7 +20,7 @@
const path = require('path');
var fs = require('fs');
const TARGET_VERSION = 702;
const TARGET_VERSION = 800;
const STEPS = [
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
@ -34,7 +34,12 @@ const STEPS = [
[ 600, 601, (db) => addFPS(db) ],
[ 601, 700, (db) => migrateWatermark(db) ],
[ 700, 701, (db) => addScalingAlgorithm(db) ],
[ 701, 702, (db) => addDeinterlaceFilter(db) ]
[ 701, 703, (db,channels,dir) => reAddIcon(dir) ],
[ 703, 800, (db) => addDeinterlaceFilter(db) ],
// there was a bit of thing in which for a while 1.3.x migrated 701 to 702 using
// the addDeinterlaceFilter step. This 702 step no longer exists as a target
// but we have to migrate it to 800 using the reAddIcon.
[ 702, 800, (db,channels,dir) => reAddIcon(dir) ],
]
const { v4: uuidv4 } = require('uuid');
@ -333,7 +338,7 @@ function commercialsRemover(db) {
}
function initDB(db, channelDB ) {
function initDB(db, channelDB, dir ) {
if (typeof(channelDB) === 'undefined') {
throw Error("???");
}
@ -348,7 +353,7 @@ function initDB(db, channelDB ) {
ran = true;
console.log("Migrating from db version " + dbVersion.version + " to: " + STEPS[i][1] + "...");
try {
STEPS[i][2](db, channelDB);
STEPS[i][2](db, channelDB, dir);
if (typeof(dbVersion._id) === 'undefined') {
db['db-version'].save( {'version': STEPS[i][1] } );
} else {
@ -398,7 +403,7 @@ function ffmpeg() {
normalizeAudio: true,
maxFPS: 60,
scalingAlgorithm: "bicubic",
deinterlaceFilter: "none"
deinterlaceFilter: "none",
}
}
@ -757,6 +762,40 @@ function addScalingAlgorithm(db) {
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
}
function moveBackup(path) {
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
let i = 0;
while (fs.existsSync( `${process.env.DATABASE}${path}.bak.${i}`) ) {
i++;
}
fs.renameSync(`${process.env.DATABASE}${path}`, `${process.env.DATABASE}${path}.bak.${i}` );
}
}
function reAddIcon(dir) {
moveBackup('/images/dizquetv.png');
let data = fs.readFileSync(path.resolve(path.join(dir, 'resources/dizquetv.png')));
fs.writeFileSync(process.env.DATABASE + '/images/dizquetv.png', data);
if (fs.existsSync(`${process.env.DATABASE}/images/pseudotv.png`) ) {
moveBackup('/images/pseudotv.png');
let data = fs.readFileSync(path.resolve(path.join(dir, 'resources/dizquetv.png')));
fs.writeFileSync(process.env.DATABASE + '/images/pseudotv.png', data);
}
moveBackup('/images/generic-error-screen.png');
data = fs.readFileSync(path.resolve(path.join(dir, 'resources/generic-error-screen.png')))
fs.writeFileSync(process.env.DATABASE + '/images/generic-error-screen.png', data)
moveBackup('/images/generic-offline-screen.png');
data = fs.readFileSync(path.resolve(path.join(dir, 'resources/generic-offline-screen.png')));
fs.writeFileSync(process.env.DATABASE + '/images/generic-offline-screen.png', data);
moveBackup('/images/loading-screen.png');
data = fs.readFileSync(path.resolve(path.join(dir, 'resources/loading-screen.png')))
fs.writeFileSync(process.env.DATABASE + '/images/loading-screen.png', data)
}
function addDeinterlaceFilter(db) {
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');

View File

@ -62,6 +62,10 @@ class FFMPEG extends events.EventEmitter {
this.ensureResolution = this.opts.normalizeResolution;
this.volumePercent = this.opts.audioVolumePercent;
this.hasBeenKilled = false;
this.audioOnly = false;
}
setAudioOnly(audioOnly) {
this.audioOnly = audioOnly;
}
async spawnConcat(streamUrl) {
return await this.spawn(streamUrl, undefined, undefined, undefined, true, false, undefined, true)
@ -108,8 +112,17 @@ class FFMPEG extends events.EventEmitter {
`-threads`, isConcatPlaylist? 1 : this.opts.threads,
`-fflags`, `+genpts+discardcorrupt+igndts`];
if (limitRead === true)
ffmpegArgs.push(`-re`)
if (
(limitRead === true)
&&
(
(this.audioOnly !== true)
||
( typeof(streamUrl.errorTitle) === 'undefined')
)
) {
ffmpegArgs.push(`-re`);
}
if (typeof startTime !== 'undefined')
@ -186,24 +199,27 @@ class FFMPEG extends events.EventEmitter {
iH = this.wantedH;
}
if ( this.audioOnly !== true) {
ffmpegArgs.push("-r" , "24");
if ( streamUrl.errorTitle == 'offline' ) {
ffmpegArgs.push(
'-loop', '1',
'-i', `${this.channel.offlinePicture}`,
);
videoComplex = `;[0:0]loop=loop=-1:size=1:start=0[looped];[looped]scale=${iW}:${iH}[videoy];[videoy]realtime[videox]`;
videoComplex = `;[${inputFiles++}:0]loop=loop=-1:size=1:start=0[looped];[looped]scale=${iW}:${iH}[videoy];[videoy]realtime[videox]`;
} else if (this.opts.errorScreen == 'static') {
ffmpegArgs.push(
'-f', 'lavfi',
'-i', `nullsrc=s=64x36`);
videoComplex = `;geq=random(1)*255:128:128[videoz];[videoz]scale=${iW}:${iH}[videoy];[videoy]realtime[videox]`;
inputFiles++;
} else if (this.opts.errorScreen == 'testsrc') {
ffmpegArgs.push(
'-f', 'lavfi',
'-i', `testsrc=size=${iW}x${iH}`,
);
videoComplex = `;realtime[videox]`;
inputFiles++;
} else if (this.opts.errorScreen == 'text') {
var sz2 = Math.ceil( (iH) / 33.0);
var sz1 = Math.ceil( sz2 * 3. / 2. );
@ -213,6 +229,7 @@ class FFMPEG extends events.EventEmitter {
'-f', 'lavfi',
'-i', `color=c=black:s=${iW}x${iH}`
);
inputFiles++;
videoComplex = `;drawtext=fontfile=${process.env.DATABASE}/font.ttf:fontsize=${sz1}:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:text='${streamUrl.errorTitle}',drawtext=fontfile=${process.env.DATABASE}/font.ttf:fontsize=${sz2}:fontcolor=white:x=(w-text_w)/2:y=(h+text_h+${sz3})/2:text='${streamUrl.subtitle}'[videoy];[videoy]realtime[videox]`;
} else if (this.opts.errorScreen == 'blank') {
@ -220,14 +237,17 @@ class FFMPEG extends events.EventEmitter {
'-f', 'lavfi',
'-i', `color=c=black:s=${iW}x${iH}`
);
inputFiles++;
videoComplex = `;realtime[videox]`;
} else {//'pic'
ffmpegArgs.push(
'-loop', '1',
'-i', `${this.errorPicturePath}`,
);
videoComplex = `;[0:0]scale=${iW}:${iH}[videoy];[videoy]realtime[videox]`;
inputFiles++;
videoComplex = `;[${videoFile+1}:0]scale=${iW}:${iH}[videoy];[videoy]realtime[videox]`;
}
}
let durstr = `duration=${streamStats.duration}ms`;
//silent
audioComplex = `;aevalsrc=0:${durstr}[audioy]`;
@ -239,14 +259,26 @@ class FFMPEG extends events.EventEmitter {
ffmpegArgs.push('-i', `${this.channel.offlineSoundtrack}`);
// I don't really understand why, but you need to use this
// 'size' in order to make the soundtrack actually loop
audioComplex = `;[1:a]aloop=loop=-1:size=2147483647[audioy]`;
audioComplex = `;[${inputFiles++}:a]aloop=loop=-1:size=2147483647[audioy]`;
}
} else if (this.opts.errorAudio == 'whitenoise') {
audioComplex = `;aevalsrc=-2+0.1*random(0):${durstr}[audioy]`;
} else if (
(this.opts.errorAudio == 'whitenoise')
||
(
!(this.opts.errorAudio == 'sine')
&&
(this.audioOnly === true) //when it's in audio-only mode, silent stream is confusing for errors.
)
) {
audioComplex = `;aevalsrc=random(0):${durstr}[audioy]`;
this.volumePercent = Math.min(70, this.volumePercent);
} else if (this.opts.errorAudio == 'sine') {
audioComplex = `;sine=f=440:${durstr}[audiox];[audiox]volume=-35dB[audioy]`;
audioComplex = `;sine=f=440:${durstr}[audioy]`;
this.volumePercent = Math.min(70, this.volumePercent);
}
if ( this.audioOnly !== true ) {
ffmpegArgs.push('-pix_fmt' , 'yuv420p' );
}
ffmpegArgs.push('-pix_fmt' , 'yuv420p' );
audioComplex += ';[audioy]arealtime[audiox]';
currentVideo = "[videox]";
currentAudio = "[audiox]";
@ -319,7 +351,7 @@ class FFMPEG extends events.EventEmitter {
}
// Channel watermark:
if (doOverlay) {
if (doOverlay && (this.audioOnly !== true) ) {
var pW =watermark.width;
var w = Math.round( pW * iW / 100.0 );
var mpHorz = watermark.horizontalMargin;
@ -361,7 +393,8 @@ class FFMPEG extends events.EventEmitter {
currentAudio = '[boosted]';
}
// Align audio is just the apad filter applied to audio stream
if (this.apad) {
if (this.apad && (this.audioOnly !== true) ) {
//it doesn't make much sense to pad audio when there is no video
audioComplex += `;${currentAudio}apad=whole_dur=${streamStats.duration}ms[padded]`;
currentAudio = '[padded]';
} else if (this.audioChannelsSampleRate) {
@ -382,11 +415,13 @@ class FFMPEG extends events.EventEmitter {
} else {
console.log(resizeMsg)
}
if (currentVideo != '[video]') {
transcodeVideo = true; //this is useful so that it adds some lines below
filterComplex += videoComplex;
} else {
currentVideo = `${videoFile}:${videoIndex}`;
if (this.audioOnly !== true) {
if (currentVideo != '[video]') {
transcodeVideo = true; //this is useful so that it adds some lines below
filterComplex += videoComplex;
} else {
currentVideo = `${videoFile}:${videoIndex}`;
}
}
// same with audio:
if (currentAudio != '[audio]') {
@ -403,15 +438,18 @@ class FFMPEG extends events.EventEmitter {
ffmpegArgs.push('-shortest');
}
}
if (this.audioOnly !== true) {
ffmpegArgs.push(
'-map', currentVideo,
`-c:v`, (transcodeVideo ? this.opts.videoEncoder : 'copy'),
`-sc_threshold`, `1000000000`,
);
}
ffmpegArgs.push(
'-map', currentVideo,
'-map', currentAudio,
`-c:v`, (transcodeVideo ? this.opts.videoEncoder : 'copy'),
`-flags`, `cgop+ilme`,
`-sc_threshold`, `1000000000`
);
if ( transcodeVideo ) {
if ( transcodeVideo && (this.audioOnly !== true) ) {
// add the video encoder flags
ffmpegArgs.push(
`-b:v`, `${this.opts.videoBitrate}k`,
@ -453,8 +491,11 @@ class FFMPEG extends events.EventEmitter {
//Concat stream is simpler and should always copy the codec
ffmpegArgs.push(
`-probesize`, 32 /*`100000000`*/,
`-i`, streamUrl,
`-map`, `0:v`,
`-i`, streamUrl );
if (this.audioOnly !== true) {
ffmpegArgs.push( `-map`, `0:v` );
}
ffmpegArgs.push(
`-map`, `0:${audioIndex}`,
`-c`, `copy`,
`-muxdelay`, this.opts.concatMuxDelay,

View File

@ -19,6 +19,7 @@ class OfflinePlayer {
context.channel.offlineSoundtrack = undefined;
}
this.ffmpeg = new FFMPEG(context.ffmpegSettings, context.channel);
this.ffmpeg.setAudioOnly(this.context.audioOnly);
}
cleanUp() {
@ -37,7 +38,7 @@ class OfflinePlayer {
} else {
ff = await ffmpeg.spawnOffline(duration);
}
ff.pipe(outStream);
ff.pipe(outStream, {'end':false} );
ffmpeg.on('end', () => {
emitter.emit('end');
@ -45,8 +46,33 @@ class OfflinePlayer {
ffmpeg.on('close', () => {
emitter.emit('close');
});
ffmpeg.on('error', (err) => {
emitter.emit('error', err);
ffmpeg.on('error', async (err) => {
//wish this code wasn't repeated.
if (! this.error ) {
console.log("Replacing failed stream with error stream");
ff.unpipe(outStream);
ffmpeg.removeAllListeners('data');
ffmpeg.removeAllListeners('end');
ffmpeg.removeAllListeners('error');
ffmpeg.removeAllListeners('close');
ffmpeg = new FFMPEG(this.context.ffmpegSettings, this.context.channel); // Set the transcoder options
ffmpeg.setAudioOnly(this.context.audioOnly);
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(duration, 60000) );
ff.pipe(outStream);
} else {
emitter.emit('error', err);
}
});
return emitter;
} catch(err) {

View File

@ -62,6 +62,7 @@ class PlexPlayer {
this.plexTranscoder = plexTranscoder;
let watermark = this.context.watermark;
let ffmpeg = new FFMPEG(ffmpegSettings, channel); // Set the transcoder options
ffmpeg.setAudioOnly( this.context.audioOnly );
this.ffmpeg = ffmpeg;
let streamDuration;
if (typeof(lineupItem.streamDuration)!=='undefined') {
@ -97,13 +98,14 @@ class PlexPlayer {
emitter.emit('close');
});
ffmpeg.on('error', async (err) => {
console.log("Replacing failed stream with error streram");
console.log("Replacing failed stream with error stream");
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.setAudioOnly(this.context.audioOnly);
ffmpeg.on('close', () => {
emitter.emit('close');
});

View File

@ -1,5 +1,6 @@
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');
const fs = require('fs');
class PlexTranscoder {
constructor(clientId, server, settings, channel, lineupItem) {
@ -73,6 +74,14 @@ class PlexTranscoder {
// Update transcode decision for session
await this.getDecision(stream.directPlay);
stream.streamUrl = (this.settings.streamPath === 'direct') ? this.file : this.plexFile;
if(this.settings.streamPath === 'direct') {
fs.access(this.file, fs.F_OK, (err) => {
if (err) {
throw Error("Can't access this file", err);
return
}
})
}
if (typeof(stream.streamUrl) == 'undefined') {
throw Error("Direct path playback is not possible for this program because it was registered at a time when the direct path settings were not set. To fix this, you must either revert the direct path setting or rebuild this channel.");
}
@ -291,10 +300,6 @@ lang=en`
}
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' }
})

View File

@ -16,9 +16,9 @@
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="dizquetv.svg"
inkscape:export-filename="/home/vx/dev/dizqueanimation/01.png"
inkscape:export-xdpi="245.75999"
inkscape:export-ydpi="245.75999">
inkscape:export-filename="/home/vx/dev/pseudotv/resources/dizquetv.png"
inkscape:export-xdpi="240"
inkscape:export-ydpi="240">
<defs
id="defs2" />
<sodipodi:namedview
@ -29,8 +29,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="-91.800821"
inkscape:cy="221.87049"
inkscape:cx="74.610162"
inkscape:cy="39.873047"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
@ -48,7 +48,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -58,7 +58,7 @@
id="layer1"
transform="translate(0,-244.08278)">
<rect
style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:1.46501946;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
style="opacity:1;fill:#3f3f3f;fill-opacity:0.86666667;stroke:none;stroke-width:1.46501946;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4712"
width="52.206699"
height="35.866219"
@ -67,7 +67,7 @@
transform="matrix(0.99990505,-0.01378015,0.00643904,0.99997927,0,0)" />
<g
id="g4581"
style="fill:#1f1f1f;fill-opacity:1;stroke-width:0.82573813"
style="fill:#080808;fill-opacity:1;stroke-width:0.82573813"
transform="matrix(1.2119871,0,0,1.2100891,-82.577875,-32.337926)">
<rect
transform="rotate(-0.94645665)"
@ -76,7 +76,7 @@
height="27.75024"
width="41.471352"
id="rect4524"
style="opacity:1;fill:#1f1f1f;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
style="opacity:1;fill:#080808;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<rect
style="opacity:1;fill:#9cbc28;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -45,7 +45,7 @@ function video( channelDB , fillerDB, db) {
})
})
// Continuously stream video to client. Leverage ffmpeg concat for piecing together videos
router.get('/video', async (req, res) => {
let concat = async (req, res, audioOnly) => {
// Check if channel queried is valid
if (typeof req.query.channel === 'undefined') {
res.status(500).send("No Channel Specified")
@ -75,6 +75,7 @@ function video( channelDB , fillerDB, db) {
console.log(`\r\nStream starting. Channel: ${channel.number} (${channel.name})`)
let ffmpeg = new FFMPEG(ffmpegSettings, channel); // Set the transcoder options
ffmpeg.setAudioOnly(audioOnly);
let stopped = false;
function stop() {
@ -109,9 +110,16 @@ function video( channelDB , fillerDB, db) {
})
let channelNum = parseInt(req.query.channel, 10)
let ff = await ffmpeg.spawnConcat(`http://localhost:${process.env.PORT}/playlist?channel=${channelNum}`);
let ff = await ffmpeg.spawnConcat(`http://localhost:${process.env.PORT}/playlist?channel=${channelNum}&audioOnly=${audioOnly}`);
ff.pipe(res );
})
};
router.get('/video', async(req, res) => {
return await concat(req, res, false);
} );
router.get('/radio', async(req, res) => {
return await concat(req, res, true);
} );
// Stream individual video to ffmpeg concat above. This is used by the server, NOT the client
router.get('/stream', async (req, res) => {
// Check if channel queried is valid
@ -119,6 +127,8 @@ function video( channelDB , fillerDB, db) {
res.status(400).send("No Channel Specified")
return
}
let audioOnly = ("true" == req.query.audioOnly);
console.log(`/stream audioOnly=${audioOnly}`);
let session = parseInt(req.query.session);
let m3u8 = (req.query.m3u8 === '1');
let number = parseInt(req.query.channel);
@ -296,6 +306,7 @@ function video( channelDB , fillerDB, db) {
channel: combinedChannel,
db: db,
m3u8: m3u8,
audioOnly : audioOnly,
}
let player = new ProgramPlayer(playerContext);
@ -416,6 +427,7 @@ function video( channelDB , fillerDB, db) {
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
let sessionId = StreamCount++;
let audioOnly = ("true" == req.query.audioOnly);
if (
(ffmpegSettings.enableFFMPEGTranscoding === true)
@ -423,12 +435,14 @@ function video( channelDB , fillerDB, db) {
&& (ffmpegSettings.normalizeAudioCodec === true)
&& (ffmpegSettings.normalizeResolution === true)
&& (ffmpegSettings.normalizeAudio === true)
&& (audioOnly !== true) /* loading screen is pointless in audio mode (also for some reason it makes it fail when codec is aac, and I can't figure out why) */
) {
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=0&session=${sessionId}'\n`;
//loading screen
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=0&session=${sessionId}&audioOnly=${audioOnly}'\n`;
}
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=1&session=${sessionId}'\n`
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&first=1&session=${sessionId}&audioOnly=${audioOnly}'\n`
for (var i = 0; i < maxStreamsToPlayInARow - 1; i++) {
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&session=${sessionId}'\n`
data += `file 'http://localhost:${process.env.PORT}/stream?channel=${channelNum}&session=${sessionId}&audioOnly=${audioOnly}'\n`
}
res.send(data)

View File

@ -13,7 +13,9 @@
<body ng-app="myApp" style="min-width: 340px;">
<div class="container">
<h1>dizqueTV
<h1>
<a href="#!/guide"><img id='dizquetv-logo' src="images/dizquetv.png" alt="logo" /></a>
dizqueTV
<small class="pull-right" style="padding: 5px;">
<a href="https://github.com/vexorian/dizquetv" title='Git Repository'>
<span class="fab fa-github text-sm"></span>

View File

@ -356,3 +356,10 @@ div.programming-programs div.list-group-item {
.watermark-preview .alternate-aspect {
background : rgba(255,255,255, 0.1);
}
#dizquetv-logo {
width: 1em;
height: 1em;
margin-bottom: 0.25em;
}