const m = require('mithril') const client = require('./api/client') const Encoder = { oninit: function(vnode) { this.list_devices = { command: 'ffmpeg.exe', arguments: '-hide_banner -sources decklink', } this.list_formats = { command: '', arguments: '', } this.resetStatus() client.registerComponent(this) }, resetStatus() { this.settingsChanged = false this.settings = { device: '', format_code: '', command: '', } this.encoderStats= null this.encoderStatus = { starting: false, running: false, log: '...', } }, onremove: function(vnode) { client.unregisterComponent(this) }, ioInit: function() { client.on(this, 'encoder.status', status => { this.encoderStatus = status if (!this.settingsChanged) { this.settings = status.settings } this.updateListFormatsHelper() }) client.on(this, 'encoder.stats', stats => { this.encoderStats = stats }) client.emit('encoder.status') }, ioConnectionChanged(connected) { if (!connected) { this.resetStatus() } else { client.emit('encoder.status') } }, listDecklinkDevices() { client.emit('encoder.run', this.list_devices) }, listDecklinkFormats() { if (!this.settings.device) return client.emit('encoder.run', this.list_formats) }, startClicked() { client.emit('encoder.start') }, stopClicked() { client.emit('encoder.stop') }, saveClicked() { this.settingsChanged = false client.emit('encoder.settings', this.settings) }, updateListFormatsHelper() { if (this.settings.device) { this.list_formats.command = this.list_devices.command this.list_formats.arguments = `-hide_banner -f decklink -list_formats 1 ${this.settings.device}` } }, updateSettings(key, vnode) { this.settingsChanged = true this.settings[key] = vnode.target.value this.updateListFormatsHelper() }, view: function(vnode) { let stats = this.encoderStats return [ m('div.column.settings', [ m('h2', 'Encoder status'), m('p', 'Status'), m('input', { type: 'text', readonly: true, class: this.encoderStatus.running ? 'red' : this.encoderStatus.starting ? 'green' : '', value: (!client.isConnected ? '' : this.encoderStatus.stopError ? `Forcefully stopped: ${this.encoderStatus.stopError}` : stats?.errors ? `Streaming: ${stats.errors} Errors` : stats?.showSlowSpeed ? `Streaming (Slow speed)` : this.encoderStatus.running ? 'Streaming' : this.encoderStatus.starting ? 'Starting' : 'Not streaming' ) + ' ' + (stats ? `(fps=${stats.fps} bitrate=${stats.bitrate}kbps speed=${stats.speed % 1 === 0 ? stats.speed : stats.speed.toFixed(3)}x)` : ''), }), m('.row', [ m('button.button', { hidden: this.encoderStatus.running || this.encoderStatus.starting, onclick: this.startClicked.bind(this), }, 'Start'), m('button.button', { hidden: !this.encoderStatus.running && !this.encoderStatus.starting, onclick: this.stopClicked.bind(this), }, 'Stop'), ]), // Decklink device section m('h2', 'Settings'), m('p', [ 'Decklink device, example: ', m('span.pre', '-i "DeckLink Mini Recorder"'), ]), m('div.row', [ m('input', { oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')}, type: 'text', autocomplete: 'off', value: this.settings.device, oninput: this.updateSettings.bind(this, 'device'), }), m('button.button', { onclick: this.listDecklinkDevices.bind(this), }, 'List devices'), ]), m('.row.meta', [ 'Clicking List devices runs ', m('.pre', `${this.list_devices.command} ${this.list_devices.arguments}`) ]), // Decklink format section m('p', [ 'Input code, example: ', m('span.pre', '-format_code Hi50'), ]), m('div.row', [ m('input', { oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')}, type: 'text', autocomplete: 'off', value: this.settings.format_code, oninput: this.updateSettings.bind(this, 'format_code'), }), m('button.button', { disabled: !Boolean(this.settings.device), onclick: this.listDecklinkFormats.bind(this), }, 'List format'), ]), m('.row.meta', [ 'Clicking List format runs ', m('.pre', `${this.list_formats.command} ${this.list_formats.arguments}`) ]), // Encoding options m('p', 'Full encode command'), m('textarea', { oncreate: (vnode) => {vnode.dom.setAttribute('spellcheck', 'false')}, value: this.settings.command, autocomplete: 'off', oninput: this.updateSettings.bind(this, 'command'), }), m('.row.meta', [ 'Any ', m('.pre', 'ffmpeg.exe ... -f decklink ...'), 'will be replaced with ', m('.pre', `ffmpeg.exe ... ${this.settings.format_code} -f decklink ${this.settings.device} ...`), ]), // Controls m('.row', [ m('button.button', { hidden: !this.settingsChanged, onclick: this.saveClicked.bind(this), }, 'Save'), ]), m('p', 'Log'), m('pre', this.encoderStatus.log || '...'), ]), ] }, } module.exports = Encoder