Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
- Default names to types
- Functionality for making windows stay on top
- Support for Caspar CGs HTML producer
- Support for SCALE_MODE for Caspar media for Caspar 2.5.x
- Support for Caspar CGs image scroller
### Changed
- Some features have moved to the footer of the app window
- Context menus now follow the color theme
Expand Down
Binary file modified media/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 52 additions & 34 deletions plugins/caspar/lib/AMCP.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const types = require('./types')
* Construct a channel-layer string
* for use in commands
* @param { AMCPOptions | undefined } opts
* @returns { String }
* @returns { string }
*/
function layerString (opts = {}) {
if (opts.channel == null) {
Expand All @@ -32,12 +32,22 @@ function layerString (opts = {}) {
* Construct a transition-string
* for use in commands
* @param { AMCPOptions | undefined } opts
* @returns { String }
* @returns { string }
*/
function transitionString (opts = {}) {
return ` ${types.TRANSITION_NAME_ENUM[opts.transitionName] || ''} ${opts.transitionDuration || '0'} ${(opts.transitionEasing || 'LINEAR')} ${(types.TRANSITION_DIRECTION_ENUM[opts.transitionDirection] || 'LEFT')}`.toUpperCase()
}

/**
* Construct a scale mode string
* for use in commands
* @param { AMCPOptions | undefined } opts
* @returns { string }
*/
function scaleModeString (opts = {}) {
return ` SCALE_MODE ${(types.SCALE_MODE_ENUM[opts.scaleMode] || 'STRETCH').toUpperCase()}`
}

/**
* List media files in the media directory
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#cls
Expand Down Expand Up @@ -86,106 +96,114 @@ exports.clear = opts => `CLEAR ${layerString(opts)}`
/**
* Load a media item in the background
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#loadbg
* @param { String } file The file to play
* @param { Boolean } loop
* @param { Number } seek
* @param { Number } length
* @param { String } filter
* @param { Boolean } auto
* @param { string } file The file to play
* @param { boolean } loop
* @param { number } seek
* @param { number } length
* @param { string } filter
* @param { boolean } auto
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.loadbg = (file, loop, seek, length, filter, auto, opts) => `LOADBG ${layerString(opts)}${file ? ` "${file}"` : ''}${loop ? ' LOOP' : ''}${seek ? ` SEEK ${seek}` : ''}${length ? ` LENGTH ${length}` : ''}${filter ? ` FILTER ${filter}` : ''}${transitionString(opts)} ${auto ? 'AUTO' : ''}`
exports.loadbg = (file, loop, seek, length, filter, auto, opts) => `LOADBG ${layerString(opts)}${file ? ` "${file}"` : ''}${loop ? ' LOOP' : ''}${seek ? ` SEEK ${seek}` : ''}${length ? ` LENGTH ${length}` : ''}${filter ? ` FILTER ${filter}` : ''}${transitionString(opts)}${scaleModeString(opts)} ${auto ? 'AUTO' : ''}`

/**
* Play a media item in the foreground
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#play
* @param { String } file The file to play
* @param { Boolean } loop
* @param { Number } seek
* @param { Number } length
* @param { String } filter
* @param { Boolean } auto
* @param { string } file The file to play
* @param { boolean } loop
* @param { number } seek
* @param { number } length
* @param { string } filter
* @param { boolean } auto
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.play = (file, loop, seek, length, filter, auto, opts) => `PLAY ${layerString(opts)}${file ? ` "${file}"` : ''}${loop ? ' LOOP' : ''}${seek ? ` SEEK ${seek}` : ''}${length ? ` LENGTH ${length}` : ''}${filter ? ` FILTER ${filter}` : ''}${transitionString(opts)} ${auto ? ' AUTO' : ''}`
exports.play = (file, loop, seek, length, filter, auto, opts) => `PLAY ${layerString(opts)}${file ? ` "${file}"` : ''}${loop ? ' LOOP' : ''}${seek ? ` SEEK ${seek}` : ''}${length ? ` LENGTH ${length}` : ''}${filter ? ` FILTER ${filter}` : ''}${transitionString(opts)}${scaleModeString(opts)} ${auto ? ' AUTO' : ''}`

/**
* Play a media item in the foreground that
* has already been loaded in the background
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#play
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.playLoaded = opts => `PLAY ${layerString(opts)}`

/**
* Play an image scroller
* @param { string } file The file to play
* @param { AMCPOptions } opts
* @returns { string }
*/
exports.playImageScroller = (file, opts) => `PLAY ${layerString(opts)}${file ? ` "${file}"` : ''} BLUR ${opts?.blur || 0} SPEED ${opts?.speed || 7}${opts?.progressive ? ' PROGRESSIVE' : ''}`

/**
* Stop an item running in the foreground
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#stop
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.stop = opts => `STOP ${layerString(opts)}`

/**
* Add a template
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#cg-add
* @param { String } template
* @param { string } template
* @param { Any } data
* @param { Boolean } playOnLoad
* @param { boolean } playOnLoad
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.cgAdd = (template, data, playOnLoad = true, opts) => `CG ${layerString(opts)} ADD ${opts.cgLayer ?? 1} "${template}" ${playOnLoad ? 1 : 0} ${JSON.stringify(data || '')}`

/**
* Stop a template
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#cg-stop
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.cgStop = opts => `CG ${layerString(opts)} STOP ${opts.cgLayer ?? 1}`

/**
* Update a template
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#cg-update
* @param { String } data
* @param { string } data
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.cgUpdate = (data, opts) => `CG ${layerString(opts)} UPDATE ${opts.cgLayer ?? 1} ${JSON.stringify(data || '')}`

/**
* Change the opacity of a layer
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#mixer-opacity
* @param { String } opacity
* @param { string } opacity
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.mixerOpacity = (opacity, opts) => `MIXER ${layerString(opts)} OPACITY ${opacity}${transitionString(opts)}`

/**
* Change the volume of a layer
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#mixer-volume
* @param { String } volume
* @param { string } volume
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.mixerVolume = (volume, opts) => `MIXER ${layerString(opts)} VOLUME ${volume}${transitionString(opts)}`

/**
* Get the thumbnail for a file
* @see https://github.com/CasparCG/help/wiki/AMCP-Protocol#thumbnail-retrieve
* @param { String } fileName
* @returns { String }
* @param { string } fileName
* @returns { string }
*/
exports.thumbnailRetrieve = fileName => `THUMBNAIL RETRIEVE "${fileName}"`

/**
* Start the HTML producer
* @param { String } url
* @param { string } url
* @param { AMCPOptions } opts
* @returns { String }
* @returns { string }
*/
exports.html = (url, opts) => `PLAY ${layerString(opts)} [HTML] "${url}"${transitionString(opts)}`
6 changes: 6 additions & 0 deletions plugins/caspar/lib/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const PLAY_HANDLERS = {
'bridge.caspar.media': async (serverId, item) => {
return commands.sendCommand(serverId, 'play', item?.data?.caspar?.target, item?.data?.caspar?.loop, 0, undefined, undefined, undefined, item?.data?.caspar)
},
'bridge.caspar.image-scroller': async (serverId, item) => {
return commands.sendCommand(serverId, 'playImageScroller', item?.data?.caspar?.target, item?.data?.caspar)
},
'bridge.caspar.load': async (serverId, item) => {
return commands.sendCommand(serverId, 'loadbg', item?.data?.caspar?.target, item?.data?.caspar?.loop, 0, undefined, undefined, item?.data?.caspar?.auto, item?.data?.caspar)
},
Expand All @@ -48,6 +51,9 @@ const STOP_HANDLERS = {
'bridge.caspar.media': (serverId, item) => {
return commands.sendCommand(serverId, 'stop', item?.data?.caspar)
},
'bridge.caspar.image-scroller': (serverId, item) => {
return commands.sendCommand(serverId, 'stop', item?.data?.caspar)
},
'bridge.caspar.load': async (serverId, item) => {

},
Expand Down
45 changes: 45 additions & 0 deletions plugins/caspar/lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ exports.TRANSITION_NAME_ENUM = TRANSITION_NAME_ENUM
const TRANSITION_DIRECTION_ENUM = ['Left', 'Right']
exports.TRANSITION_DIRECTION_ENUM = TRANSITION_DIRECTION_ENUM

const SCALE_MODE_ENUM = ['Stretch', 'Fit', 'Fill', 'Original', 'HFILL', 'VFILL']
exports.SCALE_MODE_ENUM = SCALE_MODE_ENUM

const DEFAULT_SERVER_ID = 'group:0'

function init (htmlPath) {
Expand Down Expand Up @@ -147,6 +150,13 @@ function init (htmlPath) {
allowsVariables: true,
'ui.group': 'Caspar'
},
'caspar.scaleMode': {
name: 'Scale mode',
type: 'enum',
enum: SCALE_MODE_ENUM,
default: '0',
'ui.group': 'Caspar'
},
'caspar.loop': {
name: 'Loop',
type: 'boolean',
Expand Down Expand Up @@ -201,6 +211,41 @@ function init (htmlPath) {
}
})

bridge.types.registerType({
id: 'bridge.caspar.image-scroller',
name: 'Image scroller',
category: 'Caspar',
inherits: 'bridge.caspar.playable',
properties: {
'caspar.target': {
name: 'Target',
type: 'string',
allowsVariables: true,
'ui.group': 'Caspar'
},
'caspar.speed': {
name: 'Speed',
type: 'string',
default: '7',
allowsVariables: true,
'ui.group': 'Image scroller'
},
'caspar.blur': {
name: 'Blur',
type: 'string',
default: '0',
allowsVariables: true,
'ui.group': 'Image scroller'
},
'caspar.progressive': {
name: 'Progressive',
type: 'boolean',
default: false,
'ui.group': 'Image scroller'
}
}
})

bridge.types.registerType({
id: 'bridge.caspar.template',
name: 'Template',
Expand Down