qcmd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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