qcmd 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,138 +7,143 @@ module Qcmd
7
7
  # *_RESPONSE lists are commands that expect responses
8
8
  #
9
9
 
10
- NO_MACHINE_RESPONSE = %w(connect)
11
-
12
- MACHINE_RESPONSE = %w(workspaces)
13
-
14
- WORKSPACE_RESPONSE = %w(
15
- cueLists selectedCues runningCues runningOrPausedCues thump
10
+ MACHINE = %w(
11
+ alwaysReply
12
+ connect
13
+ workingDirectory
14
+ workspaces
16
15
  )
17
16
 
18
- WORKSPACE_NO_RESPONSE = %w(
19
- go stop pause resume reset panic
17
+ WORKSPACE = %w(
18
+ cueLists
19
+ go
20
+ hardStop
21
+ new
22
+ panic
23
+ pause
24
+ reset
25
+ resume
26
+ runningCues
27
+ runningOrPausedCues
28
+ select
29
+ selectedCues
30
+ stop
31
+ thump
32
+ toggleFullScreen
33
+ updates
20
34
  )
21
35
 
22
- ALL_WORKSPACE_COMMANDS = [WORKSPACE_RESPONSE + WORKSPACE_NO_RESPONSE]
23
-
24
36
  # commands that take no args and do not respond
25
- CUE_NO_RESPONSE = %w(
26
- start stop pause resume load preview reset panic
27
- )
28
-
29
- # commands that take args but do not respond
30
- CUE_ARG_NO_RESPONSE = %w(
37
+ CUE = %w(
38
+ actionElapsed
39
+ allowsEditingDuration
40
+ armed
41
+ children
42
+ colorName
43
+ continueMode
44
+ cueTargetId
45
+ cueTargetNumber
46
+ defaultName
47
+ displayName
48
+ duration
49
+ fileTarget
50
+ flagged
51
+ hardStop
52
+ hasCueTargets
53
+ hasFileTargets
54
+ isBroken
55
+ isLoaded
56
+ isPaused
57
+ isRunning
58
+ listName
59
+ load
31
60
  loadAt
61
+ name
62
+ notes
63
+ number
64
+ panic
65
+ pause
66
+ percentActionElapsed
67
+ percentPostWaitElapsed
68
+ percentPreWaitElapsed
69
+ postWait
70
+ postWaitElapsed
71
+ preWait
72
+ preWaitElapsed
73
+ preview
74
+ reset
75
+ resume
76
+ sliderLevel
77
+ sliderLevels
78
+ start
79
+ stop
80
+ togglePause
81
+ type
82
+ uniqueID
83
+ valuesForKeys
32
84
  )
33
85
 
34
- # commands that always expect a response
35
- CUE_RESPONSE = %w(
36
- uniqueID hasFileTargets hasCueTargets allowsEditingDuration isLoaded
37
- isRunning isPaused isBroken preWaitElapsed actionElapsed
38
- postWaitElapsed percentPreWaitElapsed percentActionElapsed
39
- percentPostWaitElapsed type sliderLevels
40
- basics children
86
+ GROUP_CUE = %w(
87
+ playbackPositionId
41
88
  )
42
89
 
43
- # commands that take args but expect a response if given without args
44
- NO_ARG_CUE_RESPONSE = %w(
45
- number name notes cueTargetNumber cueTargetId preWait duration
46
- postWait continueMode flagged armed colorName
90
+ AUDIO_CUE = %w(
91
+ doFade
92
+ doPitchShift
93
+ endTime
94
+ infiniteLoop
95
+ level
96
+ patch
97
+ playCount
98
+ rate
47
99
  sliderLevel
100
+ sliderLevels
101
+ startTime
48
102
  )
49
103
 
50
- # all cue commands that take arguments
51
- CUE_ARG = CUE_ARG_NO_RESPONSE + NO_ARG_CUE_RESPONSE
52
-
53
- ALL_CUE_COMMANDS = CUE_NO_RESPONSE +
54
- CUE_ARG_NO_RESPONSE +
55
- CUE_RESPONSE +
56
- NO_ARG_CUE_RESPONSE
57
-
58
- class << self
59
- def no_machine_response_matcher
60
- @no_machine_response_matcher ||= %r[(#{NO_MACHINE_RESPONSE.join('|')})]
61
- end
62
- def no_machine_response_match command
63
- !!(no_machine_response_matcher =~ command)
64
- end
65
-
66
- def machine_response_matcher
67
- @machine_response_matcher ||= %r[(#{MACHINE_RESPONSE.join('|')})]
68
- end
69
- def machine_response_match command
70
- !!(machine_response_matcher =~ command)
71
- end
72
-
73
- def workspace_response_matcher
74
- @workspace_response_matcher ||= %r[(#{WORKSPACE_RESPONSE.join('|')})]
75
- end
76
- def workspace_response_match command
77
- !!(workspace_response_matcher =~ command)
78
- end
79
-
80
- def cue_response_matcher
81
- @cue_response_matcher ||= %r[(#{CUE_RESPONSE.join('|') })]
82
- end
83
- def cue_response_match command
84
- !!(cue_response_matcher =~ command)
85
- end
86
-
87
- def cue_no_arg_response_matcher
88
- @cue_no_arg_response_matcher ||= %r[(#{NO_ARG_CUE_RESPONSE.join('|') })]
89
- end
90
- def cue_no_arg_response_match command
91
- !!(cue_no_arg_response_matcher =~ command)
92
- end
93
-
94
- def expects_reply? osc_message
95
- address = osc_message.address
96
-
97
- Qcmd.debug "(check #{address} for reply expectation in connection state #{Qcmd.context.connection_state})"
98
-
99
- # debugger
100
-
101
- case Qcmd.context.connection_state
102
- when :none
103
- # shouldn't be dealing with OSC messages when unconnected to
104
- # machine or workspace
105
- response = no_machine_response_match(address)
106
- when :machine
107
- # could be workspace or machine command
108
- response = machine_response_match(address) ||
109
- workspace_response_match(address)
110
- when :workspace
111
- if is_cue_command?(address)
112
- Qcmd.debug "- (checking cue command)"
113
- if osc_message.has_arguments?
114
- Qcmd.debug "- (with arguments)"
115
- response = cue_response_match address
116
- else
117
- Qcmd.debug "- (without arguments)"
118
- response = cue_no_arg_response_match(address) ||
119
- cue_response_match(address)
120
- end
121
- else
122
- Qcmd.debug "- (checking workspace command)"
123
- response = workspace_response_match(address) ||
124
- machine_response_match(address)
125
- end
126
- end
104
+ FADE_CUE = %w(
105
+ level
106
+ sliderLevel
107
+ sliderLevels
108
+ )
127
109
 
128
- response.tap {|value|
129
- msg = value ? "EXPECT REPLY" : "do not expect reply"
130
- Qcmd.debug "- (#{ msg })"
131
- }
132
- end
110
+ MIC_CUE = %w(
111
+ level
112
+ sliderLevel
113
+ sliderLevels
114
+ )
133
115
 
134
- def is_cue_command? address
135
- /cue/ =~ address && !(/Lists/ =~ address || /Cues/ =~ address)
136
- end
116
+ VIDEO_CUE = %w(
117
+ cueSize
118
+ doEffect
119
+ doFade
120
+ doPitchShift
121
+ effect
122
+ effectSet
123
+ endTime
124
+ fullScreen
125
+ infiniteLoop
126
+ layer
127
+ level
128
+ opacity
129
+ patch
130
+ playCount
131
+ preserveAspectRatio
132
+ quaternion
133
+ rate
134
+ scaleX
135
+ scaleY
136
+ sliderLevel
137
+ sliderLevels
138
+ startTime
139
+ surfaceID
140
+ surfaceList
141
+ surfaceSize
142
+ translationX
143
+ translationY
144
+ )
137
145
 
138
- def is_workspace_command? address
139
- /workspace/ =~ address && !(%r[cue/] =~ address || %r[cue_id/] =~ address)
140
- end
141
- end
146
+ ALL_CUES = (CUE + GROUP_CUE + AUDIO_CUE + FADE_CUE + MIC_CUE + VIDEO_CUE).uniq.sort
142
147
 
143
148
  module Help
144
149
  class << self
@@ -147,43 +152,49 @@ module Qcmd
147
152
  Qcmd.print %[
148
153
  #{Qcmd.centered_text(' Available Commands ', '-')}
149
154
 
155
+
150
156
  exit
151
157
 
152
- close qcmd
158
+ Close qcmd.
153
159
 
154
160
 
155
- connect MACHINE_NAME
161
+ connect MACHINE_ID
156
162
 
157
- connect to the machine with name MACHINE_NAME
163
+ Connect to the machine with id MACHINE_ID. This can either be the name of the
164
+ machine shown in the listing or its number on the list. Once a machine is
165
+ connected its name will appear above the prompt.
158
166
 
159
167
 
160
168
  disconnect
161
169
 
162
- disconnect from the current machine and workspace
170
+ Disconnect from the current machine and workspace.
171
+
172
+
173
+ ..
174
+
175
+ Disconnect cue if one is connected. If not, disconnect the current workspace.
176
+ If one is not connected, disconnect from the machine.
163
177
 
164
178
 
165
179
  use WORKSPACE_NAME [PASSCODE]
166
180
 
167
- connect to the workspace with name WORKSPACE_NAME using passcode PASSCODE. A
168
- passcode is only required if the workspace has one enabled.
181
+ Connect to the workspace with name WORKSPACE_NAME given as a double quoted
182
+ string using passcode PASSCODE. A passcode is only required if the workspace
183
+ has one enabled. Once a workspace is connected its name will appear above the
184
+ prompt.
169
185
 
170
186
 
171
187
  workspaces
172
188
 
173
- show a list of the available workspaces for the currently connected machine.
189
+ Show a list of the available workspaces for the currently connected machine.
174
190
 
175
191
 
176
192
  workspace COMMAND [VALUE]
177
193
 
178
- pass the given COMMAND to the connected workspace. The following commands will
179
- act on a workspace but will not return a value:
194
+ Pass the given COMMAND to the connected workspace. The following commands will
195
+ act on a workspace:
180
196
 
181
- #{Qcmd.wrapped_text(Qcmd::Commands::WORKSPACE_NO_RESPONSE.sort.join(', '), :indent => ' ').join("\n")}
182
-
183
- And these commands will not act on a workspace, but will return a value
184
- (usually a list of cues):
185
-
186
- #{Qcmd.wrapped_text((Qcmd::Commands::WORKSPACE_RESPONSE - ['connect']).sort.join(', '), :indent => ' ').join("\n")}
197
+ #{Qcmd.wrapped_text(Qcmd::Commands::WORKSPACE.sort.join(', '), :indent => ' ').join("\n")}
187
198
 
188
199
  * Pro Tip: once you are connected to a workspace, you can just use COMMAND
189
200
  and leave off "workspace" to quickly send the given COMMAND to the connected
@@ -192,27 +203,72 @@ workspace COMMAND [VALUE]
192
203
 
193
204
  cue NUMBER COMMAND [VALUE [ANOTHER_VALUE ...]]
194
205
 
195
- send a command to the cue with the given NUMBER.
206
+ or
207
+
208
+ cue_id ID COMMAND [VALUE [ANOTHER_VALUE ...]]
209
+
210
+ Send a command to the cue with the given NUMBER or ID depending on the way
211
+ you are addressing the cue.
196
212
 
197
- NUMBER can be a string or a number, depending on the command.
213
+ NUMBER can be a double quoted string or a number, depending on the command.
198
214
 
199
215
  COMMAND can be one of:
200
216
 
201
- #{Qcmd.wrapped_text(Qcmd::Commands::ALL_CUE_COMMANDS.sort.join(', '), :indent => ' ').join("\n")}
217
+ #{Qcmd.wrapped_text(Qcmd::Commands::CUE.sort.join(', '), :indent => ' ').join("\n")}
218
+
219
+ Specific types of cues may have different cues available. Here's the commands
220
+ available for different types of cues:
221
+
222
+ Group Cue:
223
+
224
+ #{Qcmd.wrapped_text(Qcmd::Commands::GROUP_CUE.sort.join(', '), :indent => ' ').join("\n")}
225
+
226
+ Audio Cue:
227
+
228
+ #{Qcmd.wrapped_text(Qcmd::Commands::AUDIO_CUE.sort.join(', '), :indent => ' ').join("\n")}
229
+
230
+ Fade Cue:
231
+
232
+ #{Qcmd.wrapped_text(Qcmd::Commands::FADE_CUE.sort.join(', '), :indent => ' ').join("\n")}
233
+
234
+ Mic Cue:
235
+
236
+ #{Qcmd.wrapped_text(Qcmd::Commands::MIC_CUE.sort.join(', '), :indent => ' ').join("\n")}
237
+
238
+ Video Cue:
239
+
240
+ #{Qcmd.wrapped_text(Qcmd::Commands::VIDEO_CUE.sort.join(', '), :indent => ' ').join("\n")}
241
+
242
+ Once a command has been sent to an existing cue, subsequent cue commands will
243
+ be sent to the same cue with needing to repeat the leading "cue NUMBER". Once
244
+ a cue is connected its name will appear above the prompt.
245
+
246
+
247
+ alias COMMAND ACTION
248
+
249
+ Create a new command to use in qcmd! COMMAND should be a single word,
250
+ starting with made of one or more letters, numbers, underscores, and/or
251
+ hyphens. ACTION should be a legit qcmd program surrounded by parentheses,
252
+ which is anything you can type into qcmd. If you want your command to accept
253
+ arguments, use $1, $2, ... $n in place of the argument.
254
+
255
+ For example:
256
+
257
+ > alias cue-rename (cue $1 name "Hello $2")
258
+
259
+ Would create a new command, "cue-rename", that you could use to rename a cue:
202
260
 
203
- Of those commands, only some accept a VALUE. The following commands, if given
204
- a value, will update the cue:
261
+ > cue-rename 10 World
205
262
 
206
- #{Qcmd.wrapped_text(Qcmd::Commands::CUE_ARG.sort.join(', '), :indent => ' ').join("\n")}
263
+ to rename cue number 10 to "Hello World".
207
264
 
208
- Some cues are Read-Only and will return information about a cue:
265
+ We've included a few custom commands so you can see how it works. Aliases are
266
+ stored in ~/.qcmd/settings.json and can be edited from there.
209
267
 
210
- #{Qcmd.wrapped_text(Qcmd::Commands::CUE_RESPONSE.sort.join(', '), :indent => ' ').join("\n")}
211
268
 
212
- Finally, some commands act on a cue, but don't take a VALUE and don't
213
- respond:
269
+ aliases
214
270
 
215
- #{Qcmd.wrapped_text(Qcmd::Commands::CUE_NO_RESPONSE.sort.join(', '), :indent => ' ').join("\n")}
271
+ See all the aliases.
216
272
 
217
273
  ]
218
274
  end
@@ -0,0 +1,83 @@
1
+ require 'fileutils'
2
+
3
+ module Qcmd
4
+ class Configuration
5
+ class << self
6
+ def qcmd_directory
7
+ ".qcmd"
8
+ end
9
+
10
+ def home_directory
11
+ @home_directory ||= begin
12
+ full_path = File.join(File.expand_path('~'), qcmd_directory)
13
+ begin
14
+ if !File.exists?(full_path)
15
+ FileUtils.mkdir_p(full_path)
16
+ end
17
+ full_path
18
+ rescue => ex
19
+ puts "Failed to create qcmd's home directory at #{ full_path }"
20
+ puts ex.message
21
+
22
+ exit 1
23
+ end
24
+ end
25
+ end
26
+
27
+ def open_file_for_appending(fname)
28
+ f = File.new(fname, 'a')
29
+ f.sync = true
30
+ f
31
+ end
32
+
33
+ def config
34
+ @config ||= begin
35
+ if !File.exists?(config_file)
36
+ File.open(config_file, 'w') {|f|
37
+ default = JSON.pretty_generate({'aliases' => Qcmd::Aliases.defaults})
38
+ Qcmd.debug "[Configuration config] writing defaults: #{ default }"
39
+ f.write default
40
+ }
41
+ end
42
+
43
+ JSON.load(File.open(config_file))
44
+ end
45
+ end
46
+
47
+ def update key, value
48
+ config[key] = value
49
+ save
50
+ end
51
+
52
+ def save
53
+ File.open(config_file, 'w') {|conf_file|
54
+ conf_file.write(JSON.pretty_generate(config))
55
+ }
56
+ end
57
+
58
+ # not really config file things, but related to config & settings storage
59
+
60
+ def history
61
+ @history ||= open_file_for_appending(history_file)
62
+ end
63
+
64
+ def log
65
+ @log ||= open_file_for_appending(log_file)
66
+ end
67
+
68
+ # and the actual files
69
+
70
+ def config_file
71
+ File.join(home_directory, "settings.json")
72
+ end
73
+
74
+ def history_file
75
+ File.join(home_directory, "history.log")
76
+ end
77
+
78
+ def log_file
79
+ File.join(home_directory, "debug.log")
80
+ end
81
+ end
82
+ end
83
+ end