Internationalisation
Bundled Plugins API
Plugins Overview
CommandPost has a very easy to use and powerful plugins functionality, and because Lua is quite a simple language to use, it's really easy to throw together your own CommandPost Plugins!

Plugins Location

CommandPost will load any 3rd Party Plugins located in:
~/Application Support/CommandPost/Plugins
Internally, all the user interface and features for CommandPost are also plugins. They are automatically loaded from:
CommandPost.app/Contents/Resources/plugins/
This means if you build a plugin that's really awesome, it's very easily to add it to the main CommandPost Application when it's ready.

Plugins Structure

A CommandPost Plugin is simply a macOS bundle which contains Lua scripts (and any other resources you want to throw in it).
When you first run CommandPost it will register the .cpPlugin extension in macOS, which means if you create a folder called SOMETHING.cpPlugin it will prompt you with:
CommandPost Plugin
If you click Add it will turn that newly created folder into a CommandPost Plugin Bundle.
You view the contents of the bundle, simply right click and select Show Package Contents.
Show Package Contents
At a very minimum, the plugin should contain a single .lua file, which will be the Lua code that's executed.
CommandPost Plugins can either be pure Lua or a mixture of Lua and Objective-C (although since they are just dynamically loaded libraries, they could ultimately be compiled in almost any language such as Swift.

Simple Example Plugin

Below is a simple Plugin example that adds a button to the very bottom of the CommandPost menubar, and when click it displays the CommandPost version in a dialog box.
  • To make things easy for others to understand, and to help with automatic documentation generation, we recommend you use these naming conventions when building your own plugins.
  • You can learn more about the plugins API here.
  • You can learn more about cp.config here.
1
--- === plugins.core.example ===
2
---
3
--- An example plugin.
4
5
--------------------------------------------------------------------------------
6
--
7
-- EXTENSIONS:
8
--
9
--------------------------------------------------------------------------------
10
11
--------------------------------------------------------------------------------
12
-- CommandPost Extensions:
13
--------------------------------------------------------------------------------
14
local config = require("cp.config")
15
local dialog = require("cp.dialog")
16
17
--------------------------------------------------------------------------------
18
--
19
-- THE MODULE:
20
--
21
--------------------------------------------------------------------------------
22
local mod = {}
23
24
--- plugins.core.example.displayVersion() -> nil
25
--- Function
26
--- Displays a Dialog Box that displays the CommandPost Version.
27
---
28
--- Parameters:
29
--- * None
30
---
31
--- Returns:
32
--- * None
33
function mod.displayVersion()
34
dialog.displayMessage("The CommandPost Version is: " .. config.appVersion)
35
end
36
37
--------------------------------------------------------------------------------
38
--
39
-- THE PLUGIN:
40
--
41
--------------------------------------------------------------------------------
42
local plugin = {
43
id = "core.example",
44
group = "core",
45
dependencies = {
46
["core.menu.bottom"] = "bottom",
47
}
48
}
49
50
--------------------------------------------------------------------------------
51
-- INITIALISE PLUGIN:
52
--------------------------------------------------------------------------------
53
function plugin.init(deps)
54
deps.bottom:addSeparator(9999999999):addItem(99999999999, function()
55
return { title = "Example Menu Item", fn = mod.displayVersion }
56
end)
57
58
return mod
59
end
60
61
return plugin
Copied!

Advanced Example Plugin

Here's a more advanced example plugin for adding CINEMA 4D support to CommandPost:
1
--- === plugins.cinema4D ===
2
---
3
--- Example Cinema4D Commands Plugin throw together for Sarah Kimberly Euschen.
4
---
5
--- Sorry, this looks way more complicated than it actually is... I promise!
6
---
7
--- You can learn more about Lua coding here:
8
--- https://dev.commandpost.io/lua/overview/
9
10
--------------------------------------------------------------------------------
11
--
12
-- EXTENSIONS:
13
--
14
--------------------------------------------------------------------------------
15
16
--------------------------------------------------------------------------------
17
-- Hammerspoon Extensions:
18
--------------------------------------------------------------------------------
19
local log = require("hs.logger").new("cinema4D")
20
local application = require("hs.application")
21
local eventtap = require("hs.eventtap")
22
local filter = require("hs.window").filter
23
24
--------------------------------------------------------------------------------
25
-- CommandPost Extensions:
26
--------------------------------------------------------------------------------
27
local commands = require("cp.commands")
28
29
--------------------------------------------------------------------------------
30
--
31
-- THE MODULE:
32
--
33
--------------------------------------------------------------------------------
34
local mod = {}
35
36
--- plugins.cinema4D.id() -> none
37
--- Constant
38
--- The ID used internally by CommandPost to identify CINEMA 4D.
39
mod.id = "cinema4D"
40
41
--- plugins.cinema4D.appName() -> none
42
--- Constant
43
--- The application name used for Window Detection.
44
mod.appName = "CINEMA 4D Lite" -- You probably want to change this to something else, maybe just "CINEMA 4D"?
45
46
--- plugins.cinema4D.init() -> none
47
--- Function
48
--- Initialises the Plugin
49
---
50
--- Parameters:
51
--- * None
52
---
53
--- Returns:
54
--- * None
55
function mod.init()
56
57
--------------------------------------------------------------------------------
58
-- Setup Translations:
59
-- Documentation: https://dev.commandpost.io/api/i18n/
60
--------------------------------------------------------------------------------
61
i18n.set("en.plugin_group_" .. mod.id, mod.appName) -- This sets the label used by the Group dropdown boxes.
62
i18n.set("en.shortcut_group_" .. mod.id, mod.appName) -- This sets the label used by the Shortcut Group dropdown box.
63
i18n.set("en." .. mod.id .. "_midi_label", mod.appName) -- This sets the label used in the Plugins Preference Page.
64
65
--------------------------------------------------------------------------------
66
-- Creates New CINEMA 4D Command Collection:
67
-- Documentation: https://dev.commandpost.io/api/cp/cp.commands.html
68
--------------------------------------------------------------------------------
69
mod.cmds = commands.new(mod.id)
70
71
--------------------------------------------------------------------------------
72
-- Subscribe to Window Events using `hs.window.filter`:
73
-- Documentation: https://dev.commandpost.io/api/hs/hs.window.filter.html
74
--------------------------------------------------------------------------------
75
mod._filter = filter.new{mod.appName}
76
:subscribe(filter.windowFocused, function()
77
--------------------------------------------------------------------------------
78
-- Window has focus:
79
--------------------------------------------------------------------------------
80
log.df("Cinema 4D Focussed") -- This is just debugging code. It will write the result to the CommandPost Error Log.
81
mod._manager.groupStatus(mod.id, true)
82
mod.cmds:enable()
83
end)
84
:subscribe(filter.windowUnfocused, function()
85
--------------------------------------------------------------------------------
86
-- Window has lost focus:
87
--------------------------------------------------------------------------------
88
log.df("Cinema 4D Unfocussed") -- This is just debugging code. It will write the result to the CommandPost Error Log.
89
mod._manager.groupStatus(mod.id, false)
90
mod.cmds:disable()
91
end)
92
93
--------------------------------------------------------------------------------
94
-- Set up Application Watcher using `hs.application.watcher`:
95
-- Documentation: https://dev.commandpost.io/api/hs/hs.application.watcher.html
96
--------------------------------------------------------------------------------
97
mod._appWatcher = application.watcher.new(function(name, event, app)
98
if name == mod.appName then
99
if event == application.watcher.activated then
100
log.df("Cinema 4D Activated") -- This is just debugging code. It will write the result to the CommandPost Error Log.
101
mod._manager.groupStatus(mod.id, true)
102
mod.cmds:enable()
103
elseif event == application.watcher.deactivated then
104
log.df("Cinema 4D Deactivated") -- This is just debugging code. It will write the result to the CommandPost Error Log.
105
mod._manager.groupStatus(mod.id, false)
106
mod.cmds:disable()
107
end
108
end
109
end):start()
110
111
--------------------------------------------------------------------------------
112
-- Using a table to generate shortcuts:
113
--------------------------------------------------------------------------------
114
local shortcuts = {
115
["example1"] = { ["label"] = "Example 1",
116
["fn"] = function()
117
--------------------------------------------------------------------------------
118
-- Selects a menu item:
119
--------------------------------------------------------------------------------
120
local app = application.frontmostApplication()
121
app:selectMenuItem({"CINEMA 4D Lite", "About CINEMA 4D Lite"})
122
end,
123
},
124
["example2"] = { ["label"] = "Example 2",
125
["fn"] = function()
126
--------------------------------------------------------------------------------
127
-- Selects a menu item:
128
--------------------------------------------------------------------------------
129
local app = application.frontmostApplication()
130
app:selectMenuItem({"CINEMA 4D Lite", "About CINEMA 4D Lite"})
131
end,
132
},
133
}
134
for id, v in pairs(shortcuts) do
135
i18n.set("en." .. id .. "_title", v.label)
136
mod.cmds:add(id):whenPressed(v.fn)
137
end
138
139
--------------------------------------------------------------------------------
140
-- Add "About Cinema 4D" Shortcut:
141
-- This is an example of how to trigger menu items.
142
--------------------------------------------------------------------------------
143
i18n.set("en.cinema4DHelpAbout_title", "About CINEMA 4D")
144
mod.cmds:add("cinema4DHelpAbout")
145
:activatedBy():ctrl():alt():cmd("1") -- This is the default shortcut.
146
:whenPressed(function()
147
local app = application.frontmostApplication()
148
--------------------------------------------------------------------------------
149
-- Selects a menu item:
150
--------------------------------------------------------------------------------
151
app:selectMenuItem({"CINEMA 4D Lite", "About CINEMA 4D Lite"})
152
end)
153
:groupedBy("cinema4D")
154
155
--------------------------------------------------------------------------------
156
-- Add "New" Shortcut:
157
-- This is an example of how to simulate keypresses.
158
--------------------------------------------------------------------------------
159
i18n.set("en.cinema4DNew_title", "New")
160
mod.cmds:add("cinema4DNew")
161
:activatedBy():ctrl():alt():cmd("2") -- This is the default shortcut.
162
:whenPressed(function()
163
log.df("New CINEMA 4D Shortcut Pressed!") -- This is just debugging code. It will write the result to the CommandPost Error Log.
164
--------------------------------------------------------------------------------
165
-- Simulates a keystroke:
166
-- Documentation: https://dev.commandpost.io/api/hs/hs.eventtap.html
167
--------------------------------------------------------------------------------
168
eventtap.keyStroke({"command"}, "n")
169
end)
170
:groupedBy("cinema4D")
171
172
--------------------------------------------------------------------------------
173
-- Add "Trigger Python" Shortcut:
174
-- This is an example of how to trigger Terminal Commands:
175
--------------------------------------------------------------------------------
176
i18n.set("en.cinema4DPython_title", "Trigger Python")
177
mod.cmds:add("cinema4DPython")
178
:activatedBy():ctrl():alt():cmd("3") -- This is the default shortcut.
179
:whenPressed(function()
180
log.df("CINEMA 4D Python Shortcut Pressed!") -- This is just debugging code. It will write the result to the CommandPost Error Log.
181
--------------------------------------------------------------------------------
182
-- Triggers a Terminal command:
183
-- Documentation: https://dev.commandpost.io/api/hs/hs.html#execute
184
--------------------------------------------------------------------------------
185
local command = [[osascript -e 'tell app "]] .. mod.appName .. [[" to display dialog "Hello World"']]
186
hs.execute(command)
187
end)
188
:groupedBy("cinema4D")
189
190
--------------------------------------------------------------------------------
191
-- Setup Action Handler. This basically takes all our above "commands" and turns
192
-- them into actions (i.e. things that can be selected in the Console popups).
193
-- The reason we confusingly have both "Commands" and "Actions" is because of
194
-- the feature that allows you to control Final Cut Pro shortcuts from within
195
-- Final Cut Pro's own Command Editor. One day we'll make this cleaner.
196
-- https://dev.commandpost.io/api/plugins/plugins.core.action.handler.html
197
--------------------------------------------------------------------------------
198
i18n.set("en." .. mod.id .. "_cmds_action", mod.appName .. " Commands")
199
mod._handler = mod._actionmanager.addHandler(mod.id .. "_cmds", mod.id)
200
:onChoices(mod.onChoices)
201
:onExecute(mod.onExecute)
202
:onActionId(mod.getId)
203
end
204
205
--- plugins.cinema4D.onChoices(choices) -> nothing
206
--- Function
207
--- Adds available choices to the selection.
208
---
209
--- Parameters:
210
--- * `choices` - The `cp.choices` to add choices to.
211
---
212
--- Returns:
213
--- * Nothing
214
function mod.onChoices(choices)
215
for _,cmd in pairs(mod.cmds:getAll()) do
216
local title = cmd:getTitle()
217
if title then
218
local group = cmd:getSubtitle()
219
if not group and cmd:getGroup() then
220
group = i18n(cmd:getGroup().."_group")
221
end
222
group = group or ""
223
local action = {
224
id = cmd:id(),
225
}
226
choices:add(title)
227
:subText(i18n("commandChoiceSubText", {group = group}))
228
:params(action)
229
:id(mod.getId(action))
230
end
231
end
232
end
233
234
--- plugins.cinema4D.getId(action) -> boolean
235
--- Function
236
--- Gets an Action ID.
237
---
238
--- Parameters:
239
--- * `action` - A table representing the action, matching the following:
240
--- * `id` - The specific Command ID within the group.
241
---
242
--- Returns:
243
--- * `true` if the action was executed successfully.
244
function mod.getId(action)
245
return string.format("%s:%s", ID, action.id)
246
end
247
248
--- plugins.cinema4D.execute(action) -> boolean
249
--- Function
250
--- Executes the action with the provided parameters.
251
---
252
--- Parameters:
253
--- * `action` - A table representing the action, matching the following:
254
--- * `id` - The specific Command ID within the group.
255
---
256
--- Returns:
257
--- * `true` if the action was executed successfully.
258
function mod.onExecute(action)
259
local group = mod.cmds
260
if group then
261
local cmdId = action.id
262
if cmdId == nil or cmdId == "" then
263
--------------------------------------------------------------------------------
264
-- No command ID provided:
265
--------------------------------------------------------------------------------
266
dialog.displayMessage(i18n("cmdIdMissingError"))
267
return false
268
end
269
local cmd = group:get(cmdId)
270
if cmd == nil then
271
--------------------------------------------------------------------------------
272
-- No matching command:
273
--------------------------------------------------------------------------------
274
dialog.displayMessage(i18n("cmdDoesNotExistError"), {id = cmdId})
275
return false
276
end
277
--------------------------------------------------------------------------------
278
-- Ensure the command group is active:
279
--------------------------------------------------------------------------------
280
group:activate(
281
function() cmd:activated() end,
282
function() dialog.displayMessage(i18n("cmdGroupNotActivated"), {id = group.id}) end
283
)
284
return true
285
end
286
return false
287
end
288
289
--- plugins.cinema4D.reset() -> nothing
290
--- Function
291
--- Resets the set of choices.
292
---
293
--- Parameters:
294
--- * None
295
---
296
--- Returns:
297
--- * Nothing
298
function mod.reset()
299
mod._handler:reset()
300
end
301
302
--------------------------------------------------------------------------------
303
--
304
-- THE PLUGIN:
305
--
306
--------------------------------------------------------------------------------
307
local plugin = {
308
--------------------------------------------------------------------------------
309
-- This is the internal ID used by CommandPost:
310
--------------------------------------------------------------------------------
311
id = "cinema4D",
312
313
--------------------------------------------------------------------------------
314
-- This is the internal plugin group ID used by CommandPost:
315
--------------------------------------------------------------------------------
316
group = "cinema4D",
317
318
--------------------------------------------------------------------------------
319
-- Below are a list of dependancies of other CommandPost plugins:
320
--------------------------------------------------------------------------------
321
dependencies = {
322
["core.midi.manager"] = "manager",
323
["core.action.manager"] = "actionmanager",
324
}
325
}
326
327
--------------------------------------------------------------------------------
328
-- INITIALISE PLUGIN:
329
--------------------------------------------------------------------------------
330
function plugin.init(deps)
331
--------------------------------------------------------------------------------
332
-- Load dependancies to Module:
333
--------------------------------------------------------------------------------
334
mod._manager = deps.manager
335
mod._actionmanager = deps.actionmanager
336
337
--------------------------------------------------------------------------------
338
-- Load the module:
339
--------------------------------------------------------------------------------
340
return mod.init()
341
end
342
343
--------------------------------------------------------------------------------
344
-- POST INITIALISATION:
345
--------------------------------------------------------------------------------
346
function plugin.postInit()
347
-- You can do Plugin Post Initialisation stuff here if needed.
348
end
349
350
return plugin
Copied!
Last modified 1mo ago