Shinobi/libs/ffmpeg/utils.js

184 lines
7.7 KiB
JavaScript

const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const treekill = require('tree-kill');
module.exports = (s,config,lang) => {
const { mergeDeep } = require('../common.js')
const getPossibleWarnings = require('./possibleWarnings.js')
const activeProbes = {}
const runFFprobe = (url,auth,callback,forceOverlap) => {
var endData = {ok: false, result: {}}
if(!url){
endData.error = 'Missing URL'
callback(endData)
return
}
if(!forceOverlap && activeProbes[auth]){
endData.error = 'Account is already probing'
callback(endData)
return
}
activeProbes[auth] = 1
var stderr = ''
var stdout = ''
const probeCommand = s.splitForFFPMEG(`-analyzeduration 100000 probesize 100000 -v quiet -print_format json -show_format -show_streams -i "${url}"`)
var processTimeout = null
var ffprobeLocation = config.ffmpegDir.split('/')
ffprobeLocation[ffprobeLocation.length - 1] = 'ffprobe'
ffprobeLocation = ffprobeLocation.join('/')
const probeProcess = spawn(ffprobeLocation, probeCommand)
const finishReponse = () => {
delete(activeProbes[auth])
if(!stdout){
endData.error = stderr
}else{
endData.ok = true
endData.result = s.parseJSON(stdout)
}
endData.probe = probeCommand
callback(endData)
}
probeProcess.stderr.on('data',function(data){
stderr += data.toString()
})
probeProcess.stdout.on('data',function(data){
stdout += data.toString()
})
probeProcess.on('close',function(){
clearTimeout(processTimeout)
finishReponse()
})
processTimeout = setTimeout(() => {
treekill(probeProcess.pid)
finishReponse()
},4000)
}
const probeMonitor = (monitor,timeoutAmount,forceOverlap) => {
return new Promise((resolve,reject) => {
const url = s.buildMonitorUrl(monitor);
runFFprobe(url,`${monitor.ke}${monitor.mid}`,(response) => {
setTimeout(() => {
resolve(response)
},timeoutAmount || 1000)
},forceOverlap)
})
}
const getStreamInfoFromProbe = (probeResult) => {
const streamIndex = {
video: [],
audio: [],
all: []
}
const streams = probeResult.streams || []
streams.forEach((stream) => {
try{
const codecType = stream.codec_type || 'video'
const simpleInfo = {
fps: eval(stream.r_frame_rate) || '',
width: stream.coded_width,
height: stream.coded_height,
streamType: stream.codec_type,
codec: (stream.codec_name || '').toLowerCase(),
}
streamIndex.all.push(simpleInfo)
streamIndex[codecType].push(simpleInfo)
}catch(err){
console.log(err)
}
})
if(streamIndex.video.length === 0){
streamIndex.video.push({
streamType: 'video',
codec: 'unknown',
})
}
return streamIndex
}
const createWarningsForConfiguration = (monitor,probeResult) => {
const warnings = []
const possibleWarnings = getPossibleWarnings(monitor,probeResult,config,lang)
console.log(Object.keys(monitor))
console.log(`monitor.protocol`,monitor.protocol)
possibleWarnings.forEach((warning) => {
if(warning.isTrue)warnings.push(warning)
})
return warnings
}
const buildMonitorConfigPartialFromWarnings = (warnings) => {
var configPartial = {}
warnings.forEach((warning) => {
if(warning.automaticChange)configPartial = mergeDeep(configPartial,warning.automaticChange)
})
return configPartial
}
const repairConfiguration = (monitor,probeResult) => {
const warnings = createWarningsForConfiguration(monitor,probeResult)
const configPartial = buildMonitorConfigPartialFromWarnings(warnings)
return mergeDeep(monitor,configPartial)
}
const applyPartialToConfiguration = (activeMonitor,configPartial) => {
Object.keys(configPartial).forEach((key) => {
if(key !== 'details'){
activeMonitor[key] = configPartial[key]
}else{
const details = s.parseJSON(configPartial.details)
Object.keys(details).forEach((key) => {
activeMonitor.details[key] = details[key]
})
}
})
}
const buildWatermarkFiltersFromConfiguration = (prefix,monitor) => {
prefix = prefix ? prefix : ''
const watermarkLocation = monitor.details[`${prefix}watermark_location`]
//bottom right is default
var watermarkPosition = '(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2'
switch(monitor.details[`${prefix}watermark_position`]){
case'tl'://top left
watermarkPosition = '10:10'
break;
case'tr'://top right
watermarkPosition = 'main_w-overlay_w-10:10'
break;
case'bl'://bottom left
watermarkPosition = '10:main_h-overlay_h-10'
break;
}
return `movie=${watermarkLocation}[watermark],[in][watermark]overlay=${watermarkPosition}[out]`
}
const buildRotationFiltersFromConfiguration = (prefix,monitor) => {
prefix = prefix ? prefix : ''
const userChoice = monitor.details[`${prefix}rotate`]
switch(userChoice){
case'2,transpose=2':
case'0':
case'1':
case'2':
case'3':
return `transpose=${userChoice}`
break;
}
return ``
}
const buildTimestampFiltersFromConfiguration = (prefix,monitor) => {
prefix = prefix ? prefix : ''
const timestampFont = monitor.details[`${prefix}timestamp_font`] ? monitor.details[`${prefix}timestamp_font`] : '/usr/share/fonts/truetype/freefont/FreeSans.ttf'
const timestampX = monitor.details[`${prefix}timestamp_x`] ? monitor.details[`${prefix}timestamp_x`] : '(w-tw)/2'
const timestampY = monitor.details[`${prefix}timestamp_y`] ? monitor.details[`${prefix}timestamp_y`] : '0'
const timestampColor = monitor.details[`${prefix}timestamp_color`] ? monitor.details[`${prefix}timestamp_color`] : 'white'
const timestampBackgroundColor = monitor.details[`${prefix}timestamp_box_color`] ? monitor.details[`${prefix}timestamp_box_color`] : '0x00000000@1'
const timestampFontSize = monitor.details[`${prefix}timestamp_font_size`] ? monitor.details[`${prefix}timestamp_font_size`] : '10'
return `'drawtext=fontfile=${timestampFont}:text=\'%{localtime}\':x=${timestampX}:y=${timestampY}:fontcolor=${timestampColor}:box=1:boxcolor=${timestampBackgroundColor}:fontsize=${timestampFontSize}`
}
return {
ffprobe: runFFprobe,
probeMonitor: probeMonitor,
getStreamInfoFromProbe: getStreamInfoFromProbe,
createWarningsForConfiguration: createWarningsForConfiguration,
buildMonitorConfigPartialFromWarnings: buildMonitorConfigPartialFromWarnings,
applyPartialToConfiguration: applyPartialToConfiguration,
repairConfiguration: repairConfiguration,
buildTimestampFiltersFromConfiguration: buildTimestampFiltersFromConfiguration,
buildWatermarkFiltersFromConfiguration: buildWatermarkFiltersFromConfiguration
}
}