roku_builder 4.25.5 → 4.26.1
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.
- 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
|
|