qcmd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in qcmd.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Adam Bachman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ # Qcmd
2
+
3
+ `qcmd` is intended to be a simple command line client for QLab utilizing
4
+ QLab 3's new OSC interface. `qcmd` should be useable from any machine on
5
+ the same local network as the QLab workspace you intend to work with.
6
+
7
+ **This project should be considered experimental. DO NOT RUN SHOWS WITH
8
+ IT.**
9
+
10
+
11
+ ## Installation
12
+
13
+ Install this gem to your machine by running the following command:
14
+
15
+ $ gem install qcmd
16
+
17
+ That should do ya.
18
+
19
+ ## Starting the `qcmd` console.
20
+
21
+ Run the following command in a terminal window:
22
+
23
+ $ qcmd
24
+
25
+ From there, you can connect to a machine, connect to a workspace, and then
26
+ send commands to cues and the workspace.
27
+
28
+ `qcmd` supports tab completion for commands in case you get stuck or are
29
+ wondering what you can do from the console.
30
+
31
+ Run `qcmd` with the -v option to get full debugging output. Use the main
32
+ project repository (https://github.com/abachman/qcmd) to report any issues.
33
+
34
+ An example session might look like this:
35
+
36
+ $ qcmd
37
+ .:::: .:: .::
38
+ .:: .:: .:: .::
39
+ .:: .::.:: .:: .::
40
+ .:: .::.:: .:: .:: .:: .::
41
+ .:: .::.:: .:: .:: .:: .::
42
+ .:: .: .:: .:: .:: .:: .:: .::
43
+ .:: :: .:::::::: .:: .:::.:: .::
44
+ .:
45
+
46
+ qcmd 0.1.0 (c) 2012 Figure 53, Baltimore, MD.
47
+
48
+ Found 2 QLab machines
49
+
50
+ 1. adam-retina
51
+ 2. f53zwimac
52
+
53
+ type `connect MACHINE` to connect to a machine
54
+
55
+ > connect adam-retina
56
+ connecting to machine: adam-retina
57
+ -------------------------------- Workspaces --------------------------------
58
+
59
+ 1. Untitled Workspace
60
+
61
+ Type `use "WORKSPACE_NAME" PASSCODE` to load a workspace. Only enter a
62
+ passcode if your workspace uses one
63
+
64
+ adam-retina> use "Untitled Workspace"
65
+ connecting to workspace: Untitled Workspace
66
+ connected to workspace
67
+ loaded 2 cues
68
+ adam-retina:Untitled Workspace> cues
69
+
70
+ ----------------------------------- Cues -----------------------------------
71
+
72
+ Number Id Name Type
73
+
74
+ 1 2 Nope Wait
75
+ 2 3 Nipe Audio
76
+
77
+
78
+ adam-retina:Untitled Workspace> cue 2 start
79
+ adam-retina:Untitled Workspace> cue 2 isRunning
80
+ true
81
+ adam-retina:Untitled Workspace> workspace runningCues
82
+
83
+ ------------------------------- Running Cues -------------------------------
84
+
85
+ Number Id Name Type
86
+
87
+ 2 3 Nipe Audio
88
+
89
+
90
+ adam-retina:Untitled Workspace> cue 2
91
+ 1 2 actionElapsed
92
+ allowsEditingDuration armed basics
93
+ children colorName connect
94
+ continueMode cue cueLists
95
+ cueTargetId cueTargetNumber disconnect
96
+ duration exit flagged
97
+ hasCueTargets hasFileTargets isBroken
98
+ isLoaded isPaused isRunning
99
+ load loadAt name
100
+ notes number panic
101
+ pause percentActionElapsed percentPostWaitElapsed
102
+ percentPreWaitElapsed postWait postWaitElapsed
103
+ preview preWait preWaitElapsed
104
+ reset resume runningCues
105
+ runningOrPausedCues selectedCues stop
106
+ thump type uniqueID
107
+ workspace workspaces
108
+ adam-retina:Untitled Workspace> cue 2 pause
109
+ adam-retina:Untitled Workspace> cue 2 isRunning
110
+ false
111
+ adam-retina:Untitled Workspace> cue 2 percentActionElapsed
112
+ 0.109189204871655
113
+ adam-retina:Untitled Workspace> disconnect
114
+
115
+ Found 2 QLab machines
116
+
117
+ 1. adam-retina
118
+ 2. f53zwimac
119
+
120
+ type `connect MACHINE` to connect to a machine
121
+
122
+ > exit
123
+ exiting...
124
+
125
+ ## Contributing
126
+
127
+ 1. Fork it
128
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
129
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
130
+ 4. Push to the branch (`git push origin my-new-feature`)
131
+ 5. Create new Pull Request
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'cucumber'
4
+ require 'cucumber/rake/task'
5
+
6
+ Cucumber::Rake::Task.new(:features) do |t|
7
+ t.cucumber_opts = "features --format pretty"
8
+ end
9
+
10
+ require 'rspec/core/rake_task'
11
+
12
+ RSpec::Core::RakeTask.new(:spec)
13
+
14
+ task :default => [:spec, :features]
data/TODO.md ADDED
@@ -0,0 +1,3 @@
1
+ * make sure we can connect to workspaces
2
+ * parse arguments with more finesse than String#split
3
+ * make sure we can disconnect from workspaces / machines
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'qcmd'
4
+ require 'trollop'
5
+
6
+ VERSION_STRING = "qcmd #{ Qcmd::VERSION } (c) 2012 Figure 53, Baltimore, MD."
7
+
8
+ opts = Trollop::options do
9
+ version VERSION_STRING
10
+ opt :verbose, 'Use verbose mode', :default => false
11
+ opt :debug, "Show full debug output, don't make changes to workspaces", :default => false
12
+ end
13
+
14
+ if opts[:verbose]
15
+ Qcmd.log_level = :debug
16
+ end
17
+
18
+ if opts[:debug]
19
+ Qcmd.log_level = :debug
20
+ Qcmd.debug_mode = true
21
+ end
22
+
23
+ # browse local network and check for qlab + qlab workspaces
24
+
25
+ Qcmd.ascii_qlab
26
+ Qcmd.print
27
+ Qcmd.print Qcmd.centered_text(VERSION_STRING)
28
+
29
+ Qcmd::Network.browse_and_display
30
+
31
+ Qcmd::CLI.launch opts
@@ -0,0 +1,13 @@
1
+ Feature: Hello
2
+ In order to render hello
3
+ As a CLI
4
+ I want to do the right thing
5
+
6
+ # Scenario: Phrase is default
7
+ # When I run `qcmd`
8
+ # Then the output should contain "hello world"
9
+
10
+ # Scenario: Phrase is given
11
+ # When I run `qcmd Tomato`
12
+ # Then the output should contain "Tomato"
13
+
@@ -0,0 +1,2 @@
1
+ require 'aruba/cucumber'
2
+
@@ -0,0 +1,48 @@
1
+ require 'qcmd/version'
2
+ require 'qcmd/input_completer'
3
+
4
+ require 'qcmd/core_ext/array'
5
+ require 'qcmd/core_ext/osc/message'
6
+
7
+ module Qcmd
8
+ # Your code goes here...
9
+ autoload :Handler, 'qcmd/handler'
10
+ autoload :Server, 'qcmd/server'
11
+ autoload :Context, 'qcmd/context'
12
+ autoload :Parser, 'qcmd/parser'
13
+ autoload :CLI, 'qcmd/cli'
14
+ autoload :Machine, 'qcmd/machine'
15
+ autoload :Network, 'qcmd/network'
16
+ autoload :QLab, 'qcmd/qlab'
17
+ autoload :Plaintext, 'qcmd/plaintext'
18
+ autoload :Commands, 'qcmd/commands'
19
+ autoload :VERSION, 'qcmd/version'
20
+
21
+ class << self
22
+ include Qcmd::Plaintext
23
+
24
+ attr_accessor :log_level
25
+ attr_accessor :debug_mode
26
+ attr_accessor :context
27
+
28
+ def verbose!
29
+ self.log_level = :debug
30
+ end
31
+
32
+ def quiet!
33
+ self.log_level = :warning
34
+ end
35
+
36
+ def debug?
37
+ !!debug_mode
38
+ end
39
+
40
+ def debug message
41
+ log(message) if log_level == :debug
42
+ end
43
+
44
+ def connected?
45
+ !!context && !!context.machine && !context.machine.nil?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,167 @@
1
+ require 'qcmd/server'
2
+
3
+ require 'readline'
4
+
5
+ require 'osc-ruby'
6
+ require 'osc-ruby/em_server'
7
+
8
+ module Qcmd
9
+ class CLI
10
+ include Qcmd::Plaintext
11
+
12
+ attr_accessor :server, :prompt
13
+
14
+ def self.launch options={}
15
+ new options
16
+ end
17
+
18
+ def initialize options={}
19
+ # start local listening port
20
+ Qcmd.context = Qcmd::Context.new
21
+
22
+ self.prompt = '> '
23
+
24
+ start
25
+
26
+ # if local machines have already been detected and only one is available,
27
+ # use it.
28
+ if Qcmd::Network.machines
29
+ if Qcmd::Network.machines.size == 1 && !Qcmd::Network.machines.first.passcode?
30
+ puts "AUTOCONNECT"
31
+ connect Qcmd::Network.machines.first, nil
32
+ end
33
+ end
34
+ end
35
+
36
+ def connect machine, passcode
37
+ if machine.nil?
38
+ print "A valid machine is needed to connect!"
39
+ return
40
+ end
41
+
42
+ Qcmd.context.machine = machine
43
+ Qcmd.context.workspace = nil
44
+
45
+ if server.nil?
46
+ # set client connection and start listening port
47
+ self.server = Qcmd::Server.new :receive => 53001
48
+ else
49
+ # change client connection
50
+ server.connect_to_client
51
+ end
52
+ server.run
53
+
54
+ server.load_workspaces
55
+
56
+ self.prompt = "#{ machine.name }> "
57
+ end
58
+
59
+ def use_workspace workspace
60
+ Qcmd.debug %[(connecting to workspace: "#{workspace.name}")]
61
+ # set workspace in context. Will unset later if there's a problem.
62
+ Qcmd.context.workspace = workspace
63
+ self.prompt = "#{ Qcmd.context.machine.name }:#{ workspace.name }> "
64
+
65
+ server.connect_to_workspace workspace
66
+ end
67
+
68
+ def reset
69
+ Qcmd.context.reset
70
+ server.stop
71
+ self.prompt = "> "
72
+ end
73
+
74
+ def start
75
+ loop do
76
+ # blocks the whole Ruby VM
77
+ message = Readline.readline(prompt, true)
78
+
79
+ if message.nil? || message.size == 0
80
+ Qcmd.debug "(got: #{ message.inspect })"
81
+ next
82
+ end
83
+
84
+ handle_message(message)
85
+ end
86
+ end
87
+
88
+ def handle_message message
89
+ args = Qcmd::Parser.parse(message)
90
+ command = args.shift
91
+
92
+ case command
93
+ when 'exit'
94
+ print 'exiting...'
95
+ exit 0
96
+ when 'connect'
97
+ Qcmd.debug "(connect command received args: #{ args.inspect })"
98
+
99
+ machine_name = args.shift
100
+ passcode = args.shift
101
+
102
+ if machine = Qcmd::Network.find(machine_name)
103
+ print "connecting to machine: #{machine_name}"
104
+ connect machine, passcode
105
+ else
106
+ print 'sorry, that machine could not be found'
107
+ end
108
+ when 'disconnect'
109
+ reset
110
+ Qcmd::Network.browse_and_display
111
+ when 'use'
112
+ Qcmd.debug "(use command received args: #{ args.inspect })"
113
+
114
+ workspace_name = args.shift.gsub(/['"]/, '')
115
+ passcode = args.shift
116
+
117
+ Qcmd.debug "(using workspace: #{ workspace_name.inspect })"
118
+
119
+ if workspace = Qcmd.context.machine.find_workspace(workspace_name)
120
+ workspace.passcode = passcode
121
+ print "connecting to workspace: #{workspace_name}"
122
+ use_workspace workspace
123
+ end
124
+ when 'cues'
125
+ if !Qcmd.context.workspace_connected?
126
+ print "You must be connected to a workspace before you can view a cue list."
127
+ elsif Qcmd.context.workspace.cues
128
+ print
129
+ print centered_text(" Cues ", '-')
130
+ table ['Number', 'Id', 'Name', 'Type'], Qcmd.context.workspace.cues.map {|cue|
131
+ [cue.number, cue.id, cue.name, cue.type]
132
+ }
133
+ print
134
+ end
135
+ when 'cue'
136
+ # pull off cue number
137
+ cue_number = args.shift
138
+ cue_action = args.shift
139
+ args = args.map {|a| a.gsub(/^"/, '').gsub(/"$/, '')}
140
+
141
+ if cue_number.nil?
142
+ print "no cue command given. cue commands should be in the form:"
143
+ print
144
+ print " > cue NUMBER COMMAND ARGUMENTS"
145
+ print
146
+ print wrapped_text("available cue commands are: #{Qcmd::InputCompleter::ReservedCueWords.inspect}")
147
+ elsif cue_action.nil?
148
+ server.send_workspace_command(cue_number)
149
+ else
150
+ server.send_cue_command(cue_number, cue_action, *args)
151
+ end
152
+ when 'workspace'
153
+ workspace_command = args.shift
154
+
155
+ if workspace_command.nil?
156
+ print wrapped_text("no workspace command given. available workspace commands are: #{Qcmd::InputCompleter::ReservedWorkspaceWords.inspect}")
157
+ else
158
+ server.send_workspace_command(workspace_command, *args)
159
+ end
160
+
161
+ else
162
+ server.send_command(command, *args)
163
+ end
164
+ end
165
+
166
+ end
167
+ end