roku_builder 4.25.5 → 4.26.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/intergration/roku_builder/test_helper.rb +2 -3
- data/intergration/roku_builder/test_profiler.rb +1 -1
- data/lib/roku_builder/config_parser.rb +4 -5
- data/lib/roku_builder/device_manager.rb +100 -0
- data/lib/roku_builder/plugins/core.rb +3 -1
- data/lib/roku_builder/plugins/inspector.rb +36 -26
- data/lib/roku_builder/plugins/line_inspector.rb +27 -6
- data/lib/roku_builder/plugins/linker.rb +29 -22
- data/lib/roku_builder/plugins/loader.rb +36 -15
- data/lib/roku_builder/plugins/monitor.rb +19 -17
- data/lib/roku_builder/plugins/navigator.rb +40 -34
- data/lib/roku_builder/plugins/packager.rb +109 -92
- data/lib/roku_builder/plugins/profiler.rb +24 -15
- data/lib/roku_builder/plugins/tester.rb +15 -13
- data/lib/roku_builder/util.rb +39 -17
- data/lib/roku_builder/version.rb +1 -1
- data/lib/roku_builder.rb +3 -19
- data/roku_builder.gemspec +3 -2
- data/test/roku_builder/plugins/test_analyzer.rb +19 -0
- data/test/roku_builder/plugins/test_core.rb +2 -3
- data/test/roku_builder/plugins/test_inspector.rb +48 -8
- data/test/roku_builder/plugins/test_linker.rb +66 -10
- data/test/roku_builder/plugins/test_loader.rb +44 -15
- data/test/roku_builder/plugins/test_monitor.rb +33 -16
- data/test/roku_builder/plugins/test_navigator.rb +55 -14
- data/test/roku_builder/plugins/test_packager.rb +118 -34
- data/test/roku_builder/plugins/test_profiler.rb +153 -78
- data/test/roku_builder/plugins/test_tester.rb +20 -5
- data/test/roku_builder/test_device_manager.rb +187 -0
- data/test/roku_builder/test_files/analyzer_test/dont_use_hello_world.json +11 -0
- data/test/roku_builder/test_files/analyzer_test/linter_positive_match.json +13 -0
- data/test/roku_builder/test_helper.rb +6 -0
- data/test/roku_builder/test_roku_builder.rb +5 -42
- metadata +27 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26fe47ba62ff48e37fe3bd5dda06ff687b79c30dc67bd7dd216747363cf6efbd
|
4
|
+
data.tar.gz: cc5480949806270e823d1a177219a48f541abbdde571c949022d9ae16a3cf11a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7dd09cf68f00832afb02e2797643d6babcb81c6cb943523c883b77b0f2d361bb03fe642c8fe54f685e9565dea175d23974fd1fa9f4ccbbb79be53e559471eb5a
|
7
|
+
data.tar.gz: b847c41f764a41db7421c0cba806e7ed264695ec0283f8af28a0a8dad1fdeda731c66c6757bb67faa5ef74e19a45488c33c61f97e3df837d5b9eb1903be12fe3
|
@@ -15,7 +15,7 @@ require "minitest/autorun"
|
|
15
15
|
require "minitest/utils"
|
16
16
|
require "securerandom"
|
17
17
|
|
18
|
-
ROKU_IP = "192.168.
|
18
|
+
ROKU_IP = "192.168.11.114"
|
19
19
|
|
20
20
|
module Minitest
|
21
21
|
module Assertions
|
@@ -114,8 +114,7 @@ module Helpers
|
|
114
114
|
default: :project1,
|
115
115
|
project1: {
|
116
116
|
directory: root_dir,
|
117
|
-
|
118
|
-
files: ["manifest","file.tmp"],
|
117
|
+
source_files: ["images","source","components", "manifest","file.tmp"],
|
119
118
|
app_name: "App Name",
|
120
119
|
stage_method: :script,
|
121
120
|
stages:{
|
@@ -48,7 +48,7 @@ module RokuBuilder
|
|
48
48
|
`#{roku} --sideload --working`
|
49
49
|
assert_log @uuid
|
50
50
|
output = `#{roku} --profile images`
|
51
|
-
assert_match(/Available memory/, output)
|
51
|
+
assert_match(/Available texture memory/, output)
|
52
52
|
end
|
53
53
|
def test_profile_textures
|
54
54
|
`#{roku} --sideload --working`
|
@@ -21,7 +21,7 @@ module RokuBuilder
|
|
21
21
|
|
22
22
|
def parse_config
|
23
23
|
process_in_argument
|
24
|
-
|
24
|
+
setup_devices
|
25
25
|
setup_project
|
26
26
|
setup_in_out_file
|
27
27
|
setup_project_config
|
@@ -35,10 +35,9 @@ module RokuBuilder
|
|
35
35
|
@options[:in] = File.expand_path(@options[:in]) if @options[:in]
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
@
|
40
|
-
@parsed[:
|
41
|
-
raise ArgumentError, "Unknown device: #{@options[:device]}" unless @parsed[:device_config]
|
38
|
+
def setup_devices
|
39
|
+
@parsed[:device_default] = @config[:devices][:default]
|
40
|
+
@parsed[:devices] = @config[:devices].select{|key, value| :default != key}
|
42
41
|
end
|
43
42
|
|
44
43
|
def setup_project
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# ********** Copyright Viacom, Inc. Apache 2.0 **********
|
2
|
+
|
3
|
+
module RokuBuilder
|
4
|
+
|
5
|
+
class DeviceManager
|
6
|
+
|
7
|
+
def initialize(options:, config:)
|
8
|
+
@config = config
|
9
|
+
@options = options
|
10
|
+
@timeout_duration = 3600
|
11
|
+
end
|
12
|
+
|
13
|
+
def reserve_device(no_lock: false)
|
14
|
+
message = "No Devices Found"
|
15
|
+
if @options[:device]
|
16
|
+
device = Device.new(@options[:device], @config.raw[:devices][@options[:device].to_sym])
|
17
|
+
return device if device_available!(device: device, no_lock: no_lock)
|
18
|
+
message = "Device #{@options[:device]} not found"
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
Timeout::timeout(@timeout_duration) do
|
22
|
+
loop do
|
23
|
+
device = reserve_any(no_lock: no_lock)
|
24
|
+
if device
|
25
|
+
Logger.instance.info "Using Device: #{device.to_s}"
|
26
|
+
return device
|
27
|
+
end
|
28
|
+
break unless @options[:device_blocking]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Timeout::Error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
raise DeviceError, message
|
35
|
+
end
|
36
|
+
|
37
|
+
def release_device(device)
|
38
|
+
lock = lock_file(device)
|
39
|
+
File.delete(lock) if File.exist?(lock)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def reserve_any(no_lock: false)
|
45
|
+
default = @config.device_default
|
46
|
+
all_devices = @config.devices.keys.reject{|key, value| default == key}
|
47
|
+
all_devices.unshift(default)
|
48
|
+
all_devices.each do |device_name|
|
49
|
+
device = Device.new(device_name, @config.devices[device_name])
|
50
|
+
if device_available!(device: device, no_lock: no_lock)
|
51
|
+
return device
|
52
|
+
end
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def device_available!(device:, no_lock: false)
|
58
|
+
return false unless device_ping?(device)
|
59
|
+
return true if no_lock
|
60
|
+
file = lock_file(device)
|
61
|
+
lock = file.flock(File::LOCK_EX|File::LOCK_NB)
|
62
|
+
return false if lock == false
|
63
|
+
text = file.readlines
|
64
|
+
return false if text.count > 0
|
65
|
+
file.write($$)
|
66
|
+
file.pos = 0
|
67
|
+
text = file.readlines
|
68
|
+
if text[0] != $$.to_s
|
69
|
+
file.flock(File::LOCK_UN)
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def device_ping?(device)
|
76
|
+
ping = Net::Ping::External.new
|
77
|
+
ping.ping? device.ip, 1, 0.2, 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def lock_file(device)
|
81
|
+
File.open(File.join(Dir.tmpdir, device.name), File::RDWR | File::APPEND | File::CREAT | File::BINARY | File::SHARE_DELETE) #"a+")
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class Device
|
87
|
+
attr_accessor :name, :ip, :user, :password
|
88
|
+
|
89
|
+
def initialize(name, device_config)
|
90
|
+
@name = name.to_s
|
91
|
+
@ip = device_config[:ip]
|
92
|
+
@user = device_config[:user]
|
93
|
+
@password = device_config[:password]
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"#{name}:#{ip}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -67,7 +67,9 @@ module RokuBuilder
|
|
67
67
|
end
|
68
68
|
parser.on("-D", "--device ID", "Use a different device corresponding to the given ID") do |d|
|
69
69
|
options[:device] = d
|
70
|
-
|
70
|
+
end
|
71
|
+
parser.on("--device-blocking", "Block and wait for a device if none is ready. Does not work with --device") do
|
72
|
+
options[:device_blocking] = true
|
71
73
|
end
|
72
74
|
parser.on("-V", "--verbose", "Print Info message") do
|
73
75
|
options[:verbose] = true
|
@@ -33,13 +33,15 @@ module RokuBuilder
|
|
33
33
|
pkg = pkg+".pkg" unless pkg.end_with?(".pkg")
|
34
34
|
# upload new key with password
|
35
35
|
path = "/plugin_inspect"
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
response = nil
|
37
|
+
multipart_connection do |conn|
|
38
|
+
payload = {
|
39
|
+
mysubmit: "Inspect",
|
40
|
+
passwd: options[:password],
|
41
|
+
archive: Faraday::UploadIO.new(pkg, 'application/octet-stream')
|
42
|
+
}
|
43
|
+
response = conn.post path, payload
|
44
|
+
end
|
43
45
|
|
44
46
|
app_name = /App Name:\s*<\/td>\s*<td>\s*<font[^>]*>([^<]*)<\/font>\s*<\/td>/.match(response.body)
|
45
47
|
dev_id = nil
|
@@ -75,30 +77,38 @@ module RokuBuilder
|
|
75
77
|
# Capture a screencapture for the currently sideloaded app
|
76
78
|
# @return [Boolean] Success
|
77
79
|
def screencapture(options:)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
get_device do |device|
|
81
|
+
out = @config.out
|
82
|
+
payload = {
|
83
|
+
mysubmit: "Screenshot",
|
84
|
+
passwd: @dev_password,
|
85
|
+
archive: Faraday::UploadIO.new(File::NULL, 'application/octet-stream')
|
86
|
+
}
|
87
|
+
response = nil
|
88
|
+
multipart_connection(device: device) do |conn|
|
89
|
+
response = conn.post "/plugin_inspect", payload
|
90
|
+
end
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
92
|
+
path = /<img src="([^"]*)">/.match(response.body)
|
93
|
+
raise ExecutionError, "Failed to capture screen" unless path
|
94
|
+
path = path[1]
|
89
95
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
96
|
+
unless out[:file]
|
97
|
+
out[:file] = /time=([^"]*)">/.match(response.body)
|
98
|
+
out_ext = /dev.([^"]*)\?/.match(response.body)
|
99
|
+
out[:file] = "dev_#{out[:file][1]}.#{out_ext[1]}" if out[:file]
|
100
|
+
end
|
95
101
|
|
96
|
-
|
102
|
+
response = nil
|
103
|
+
simple_connection(device: device) do |conn|
|
104
|
+
response = conn.get path
|
105
|
+
end
|
97
106
|
|
98
|
-
|
99
|
-
|
107
|
+
File.open(File.join(out[:folder], out[:file]), "wb") do |io|
|
108
|
+
io.write(response.body)
|
109
|
+
end
|
110
|
+
@logger.info "Screen captured to #{File.join(out[:folder], out[:file])}"
|
100
111
|
end
|
101
|
-
@logger.info "Screen captured to #{File.join(out[:folder], out[:file])}"
|
102
112
|
end
|
103
113
|
end
|
104
114
|
RokuBuilder.register_plugin(Inspector)
|
@@ -47,17 +47,38 @@ module RokuBuilder
|
|
47
47
|
match = nil
|
48
48
|
start = 0
|
49
49
|
loop do
|
50
|
+
stop = to_check.length-1
|
51
|
+
pass_match = nil
|
52
|
+
if line_inspector[:pass_if_match]
|
53
|
+
if line_inspector[:case_sensitive]
|
54
|
+
pass_match = /#{line_inspector[:pass_test_regexp]}/.match(to_check[start..stop])
|
55
|
+
else
|
56
|
+
pass_match = /#{line_inspector[:pass_test_regexp]}/i.match(to_check[start..stop])
|
57
|
+
end
|
58
|
+
break unless pass_match
|
59
|
+
stop = to_check.index("\n", start)
|
60
|
+
stop ||= to_check.length-1
|
61
|
+
end
|
50
62
|
if line_inspector[:case_sensitive]
|
51
|
-
match = /#{line_inspector[:regex]}/.match(to_check
|
63
|
+
match = /#{line_inspector[:regex]}/.match(to_check[start..stop])
|
52
64
|
else
|
53
|
-
match = /#{line_inspector[:regex]}/i.match(to_check
|
65
|
+
match = /#{line_inspector[:regex]}/i.match(to_check[start..stop])
|
54
66
|
end
|
55
|
-
if match
|
56
|
-
|
57
|
-
|
67
|
+
if (not line_inspector[:pass_if_match] and match) or (line_inspector[:pass_if_match] and not match)
|
68
|
+
error_match = match
|
69
|
+
if match
|
70
|
+
start = match.end(0)
|
71
|
+
line_number = to_check[0..match.begin(0)].split("\n", -1).count - 1
|
72
|
+
else
|
73
|
+
error_match = pass_match
|
74
|
+
line_number = to_check[0..start].split("\n", -1).count - 1
|
75
|
+
start = stop
|
76
|
+
end
|
58
77
|
unless lines_to_ignore.include?(line_number)
|
59
|
-
add_warning(inspector: line_inspector, file: file_path, line: line_number, match:
|
78
|
+
add_warning(inspector: line_inspector, file: file_path, line: line_number, match: error_match)
|
60
79
|
end
|
80
|
+
elsif line_inspector[:pass_if_match]
|
81
|
+
start = stop +1
|
61
82
|
else
|
62
83
|
break
|
63
84
|
end
|
@@ -36,14 +36,16 @@ module RokuBuilder
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Deeplink to an app
|
39
|
-
def deeplink(options:)
|
40
|
-
|
41
|
-
|
39
|
+
def deeplink(options:, device: nil)
|
40
|
+
get_device(device: device) do |device|
|
41
|
+
if options.has_source?
|
42
|
+
Loader.new(config: @config).sideload(options: options, device: device)
|
43
|
+
end
|
44
|
+
app_id = options[:app_id]
|
45
|
+
app_id ||= "dev"
|
46
|
+
path = "/launch/#{app_id}"
|
47
|
+
send_options(path: path, options: options[:deeplink], device: device)
|
42
48
|
end
|
43
|
-
app_id = options[:app_id]
|
44
|
-
app_id ||= "dev"
|
45
|
-
path = "/launch/#{app_id}"
|
46
|
-
send_options(path: path, options: options[:deeplink])
|
47
49
|
end
|
48
50
|
|
49
51
|
def input(options:)
|
@@ -54,8 +56,10 @@ module RokuBuilder
|
|
54
56
|
# @param logger [Logger] System Logger
|
55
57
|
def applist(options:)
|
56
58
|
path = "/query/apps"
|
57
|
-
|
58
|
-
|
59
|
+
response = nil
|
60
|
+
multipart_connection(port: 8060) do |conn|
|
61
|
+
response = conn.get path
|
62
|
+
end
|
59
63
|
|
60
64
|
if response.success?
|
61
65
|
regexp = /id="([^"]*)"\stype="([^"]*)"\sversion="([^"]*)">([^<]*)</
|
@@ -70,22 +74,25 @@ module RokuBuilder
|
|
70
74
|
|
71
75
|
private
|
72
76
|
|
73
|
-
def send_options(path:, options:)
|
77
|
+
def send_options(path:, options:, device: nil)
|
74
78
|
payload = RokuBuilder.options_parse(options: options)
|
79
|
+
get_device(device: device) do |device|
|
80
|
+
unless payload.keys.count > 0
|
81
|
+
@logger.warn "No options sent to launched app"
|
82
|
+
else
|
83
|
+
payload = parameterize(payload)
|
84
|
+
path = "#{path}?#{payload}"
|
85
|
+
@logger.info "Deeplink:"
|
86
|
+
@logger.info payload
|
87
|
+
@logger.info "CURL:"
|
88
|
+
@logger.info "curl -d '' 'http://#{device.ip}:8060#{path}'"
|
89
|
+
end
|
75
90
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
path = "#{path}?#{payload}"
|
81
|
-
@logger.info "Deeplink:"
|
82
|
-
@logger.info payload
|
83
|
-
@logger.info "CURL:"
|
84
|
-
@logger.info "curl -d '' '#{@url}:8060#{path}'"
|
91
|
+
multipart_connection(port: 8060, device: device) do |conn|
|
92
|
+
response = conn.post path
|
93
|
+
@logger.fatal("Failed Deeplinking") unless response.success?
|
94
|
+
end
|
85
95
|
end
|
86
|
-
|
87
|
-
response = multipart_connection(port: 8060).post path
|
88
|
-
@logger.fatal("Failed Deeplinking") unless response.success?
|
89
96
|
end
|
90
97
|
|
91
98
|
# Parameterize options to be sent to the app
|
@@ -50,16 +50,20 @@ module RokuBuilder
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# Sideload an app onto a roku device
|
53
|
-
def sideload(options:)
|
53
|
+
def sideload(options:, device: nil)
|
54
54
|
did_build = false
|
55
55
|
unless options[:in]
|
56
56
|
did_build = true
|
57
57
|
build(options: options)
|
58
58
|
end
|
59
59
|
keep_build_file = is_build_command(options) and options[:out]
|
60
|
-
upload(options)
|
60
|
+
upload(options: options, device: device)
|
61
61
|
# Cleanup
|
62
|
-
|
62
|
+
begin
|
63
|
+
File.delete(file_path(:in)) if did_build and not keep_build_file
|
64
|
+
rescue Errno::EACCES
|
65
|
+
@logger.warn "Unable to delete: " + file_path(:in)
|
66
|
+
end
|
63
67
|
end
|
64
68
|
|
65
69
|
|
@@ -76,18 +80,30 @@ module RokuBuilder
|
|
76
80
|
|
77
81
|
# Remove the currently sideloaded app
|
78
82
|
def delete(options:, ignoreFailure: false)
|
79
|
-
payload = {
|
80
|
-
|
83
|
+
payload = {
|
84
|
+
mysubmit: make_param("Delete"),
|
85
|
+
archive: make_param("", "application/octet-stream")
|
86
|
+
}
|
87
|
+
response = nil
|
88
|
+
multipart_connection do |conn|
|
89
|
+
response = conn.post "/plugin_install", payload
|
90
|
+
end
|
81
91
|
unless response.status == 200 and response.body =~ /Delete Succeeded/ or ignoreFailure
|
82
92
|
raise ExecutionError, "Failed Unloading"
|
83
93
|
end
|
84
94
|
end
|
85
95
|
|
86
96
|
# Convert sideloaded app to squashfs
|
87
|
-
def squash(options:, ignoreFailure: false)
|
88
|
-
payload = {
|
89
|
-
|
90
|
-
|
97
|
+
def squash(options:, device: nil, ignoreFailure: false)
|
98
|
+
payload = {
|
99
|
+
mysubmit: "Convert to squashfs",
|
100
|
+
archive: make_param("", "application/octet-stream")
|
101
|
+
}
|
102
|
+
response = nil
|
103
|
+
multipart_connection(device: device) do |conn|
|
104
|
+
response = conn.post "/plugin_install", payload
|
105
|
+
end
|
106
|
+
unless response.status == 200 and response.body =~ /squashfs file in internal memory/ or ignoreFailure
|
91
107
|
raise ExecutionError, "Failed Converting to Squashfs"
|
92
108
|
end
|
93
109
|
end
|
@@ -104,16 +120,19 @@ module RokuBuilder
|
|
104
120
|
[:sideload, :build].include? options.command
|
105
121
|
end
|
106
122
|
|
107
|
-
def upload(options)
|
123
|
+
def upload(options:, device: nil)
|
108
124
|
payload = {
|
109
125
|
mysubmit: "Replace",
|
110
126
|
archive: Faraday::UploadIO.new(file_path(:in), 'application/zip'),
|
111
127
|
}
|
112
128
|
payload["remotedebug"] = "1" if options[:remoteDebug]
|
113
|
-
response =
|
129
|
+
response = nil
|
130
|
+
multipart_connection(device: device) do |conn|
|
131
|
+
response = conn.post "/plugin_install", payload
|
132
|
+
end
|
114
133
|
@logger.debug("Status: #{response.status}, Body: #{response.body}")
|
115
134
|
if response.status==200 and response.body=~/Identical to previous version/
|
116
|
-
@logger.warn("Sideload
|
135
|
+
@logger.warn("Sideload identical to previous version")
|
117
136
|
elsif not (response.status==200 and response.body=~/Install Success/)
|
118
137
|
raise ExecutionError, "Failed Sideloading"
|
119
138
|
end
|
@@ -141,11 +160,13 @@ module RokuBuilder
|
|
141
160
|
end
|
142
161
|
|
143
162
|
def build_zip(content)
|
144
|
-
|
145
|
-
File.
|
146
|
-
io = Zip::File.open(path, Zip::File::CREATE)
|
163
|
+
tmp_path = File.join(build_dir, SecureRandom.uuid+".zip")
|
164
|
+
io = Zip::File.open(tmp_path, Zip::File::CREATE)
|
147
165
|
writeEntries(build_dir, content[:source_files], "", content[:excludes], io)
|
148
166
|
io.close()
|
167
|
+
path = file_path(:out)
|
168
|
+
File.delete(path) if File.exist?(path)
|
169
|
+
FileUtils.mv(tmp_path, path)
|
149
170
|
end
|
150
171
|
|
151
172
|
# Recursively write directory contents to a zip archive
|
@@ -32,26 +32,28 @@ module RokuBuilder
|
|
32
32
|
|
33
33
|
# Monitor a development log on the Roku device
|
34
34
|
def monitor(options:)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
35
|
+
get_device(no_lock: true) do |device|
|
36
|
+
type = options[:monitor].to_sym
|
37
|
+
telnet_config = { 'Host' => device.ip, 'Port' => @ports[type] }
|
38
|
+
waitfor_config = { 'Match' => /./, 'Timeout' => false }
|
39
|
+
|
40
|
+
thread = Thread.new(telnet_config, waitfor_config) {|telnet,waitfor|
|
41
|
+
@logger.info "Monitoring #{type} console(#{telnet['Port']}) on #{telnet['Host'] }"
|
42
|
+
connection = Net::Telnet.new(telnet)
|
43
|
+
Thread.current[:connection] = connection
|
44
|
+
all_text = ""
|
45
|
+
while true
|
46
|
+
connection.waitfor(waitfor) do |txt|
|
47
|
+
all_text = manage_text(all_text: all_text, txt: txt, regexp: options[:regexp])
|
48
|
+
end
|
47
49
|
end
|
48
|
-
|
49
|
-
|
50
|
-
thread.abort_on_exception = true
|
50
|
+
}
|
51
|
+
thread.abort_on_exception = true
|
51
52
|
|
52
|
-
|
53
|
+
init_readline()
|
53
54
|
|
54
|
-
|
55
|
+
run_prompt(thread: thread)
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
private
|
@@ -70,48 +70,54 @@ module RokuBuilder
|
|
70
70
|
# Send a navigation command to the roku device
|
71
71
|
# @param command [Symbol] The smbol of the command to send
|
72
72
|
# @return [Boolean] Success
|
73
|
-
def nav(options:)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
def nav(options:, device: nil)
|
74
|
+
get_device(device: device, no_lock: true) do |device|
|
75
|
+
commands = options[:nav].split(/, */).map{|c| c.to_sym}
|
76
|
+
commands.each do |command|
|
77
|
+
unless @commands.has_key?(command)
|
78
|
+
raise ExecutionError, "Unknown Navigation Command"
|
79
|
+
end
|
80
|
+
multipart_connection(port: 8060, device: device) do |conn|
|
81
|
+
path = "/keypress/#{@commands[command]}"
|
82
|
+
@logger.debug("Send Command: "+path)
|
83
|
+
response = conn.post path
|
84
|
+
raise ExecutionError, "Navigation Failed" unless response.success?
|
85
|
+
end
|
78
86
|
end
|
79
|
-
conn = multipart_connection(port: 8060)
|
80
|
-
path = "/keypress/#{@commands[command]}"
|
81
|
-
@logger.debug("Send Command: "+path)
|
82
|
-
response = conn.post path
|
83
|
-
raise ExecutionError, "Navigation Failed" unless response.success?
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
87
90
|
# Type text on the roku device
|
88
91
|
# @param text [String] The text to type on the device
|
89
92
|
# @return [Boolean] Success
|
90
|
-
def type(options:)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
93
|
+
def type(options:, device: nil)
|
94
|
+
multipart_connection(port: 8060, device: device, no_lock: true) do |conn|
|
95
|
+
options[:type].split(//).each do |c|
|
96
|
+
path = "/keypress/LIT_#{CGI::escape(c)}"
|
97
|
+
@logger.debug("Send Letter: "+path)
|
98
|
+
response = conn.post path
|
99
|
+
return false unless response.success?
|
100
|
+
end
|
101
|
+
return true
|
97
102
|
end
|
98
|
-
return true
|
99
103
|
end
|
100
104
|
|
101
105
|
def navigate(options:)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
106
|
+
get_device(no_lock: true) do |device|
|
107
|
+
running = true
|
108
|
+
@logger.info("Key Mappings:")
|
109
|
+
@mappings.each_value {|key|
|
110
|
+
@logger.info(sprintf("%13s -> %s", key[1], @commands[key[0].to_sym]))
|
111
|
+
}
|
112
|
+
@logger.info(sprintf("%13s -> %s", "Ctrl + c", "Exit"))
|
113
|
+
while running
|
114
|
+
char = read_char
|
115
|
+
@logger.debug("Char: #{char.inspect}")
|
116
|
+
if char == "\u0003"
|
117
|
+
running = false
|
118
|
+
else
|
119
|
+
Thread.new(char, device) {|character,device| handle_navigate_input(character, device)}
|
120
|
+
end
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
@@ -208,11 +214,11 @@ module RokuBuilder
|
|
208
214
|
end
|
209
215
|
end
|
210
216
|
|
211
|
-
def handle_navigate_input(char)
|
217
|
+
def handle_navigate_input(char, device)
|
212
218
|
if @mappings[char.to_sym] != nil
|
213
|
-
nav(options: {nav: @mappings[char.to_sym][0]})
|
219
|
+
nav(options: {nav: @mappings[char.to_sym][0]}, device: device)
|
214
220
|
elsif char.inspect.force_encoding("UTF-8").ascii_only?
|
215
|
-
type(options: {type: char})
|
221
|
+
type(options: {type: char}, device: device)
|
216
222
|
end
|
217
223
|
end
|
218
224
|
|