headless 2.2.3 → 2.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -3
- data/CHANGELOG +6 -0
- data/headless.gemspec +1 -1
- data/lib/headless.rb +17 -8
- data/lib/headless/cli_util.rb +12 -2
- data/lib/headless/video/video_recorder.rb +29 -14
- data/spec/headless_spec.rb +4 -0
- data/spec/integration_spec.rb +1 -3
- data/spec/video_recorder_spec.rb +13 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64adcb425e2e63bfed70d55fc99365e60c87887d
|
4
|
+
data.tar.gz: e3eeace8449b30b55cfee239b666bef2dedbd96d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66c57e7f5ce63679504a43cfa9018e956a609cf1d1e6298285bfa1626397fa293779f95070d7267fb3f41418a3c9ef3b1919cd1d78a7e7fa6bfdc93beebc36b2
|
7
|
+
data.tar.gz: 9d86ad8c3bd3ccf27b914943ab6ffac5fc942c13fdb458cef3e9b2728c743826390fac433b882609cd98cb8800ec1e1b67237bf2ce98128992b8d27861f2f3e2
|
data/.travis.yml
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
+
sudo: required
|
2
|
+
dist: trusty
|
1
3
|
language: ruby
|
4
|
+
cache: bundler
|
2
5
|
rvm:
|
3
6
|
- 1.9.3
|
4
|
-
- 2.0
|
5
|
-
- 2.
|
7
|
+
- 2.0.0-p648
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.5
|
10
|
+
- 2.3.1
|
6
11
|
before_install:
|
7
12
|
- "sudo apt-get update"
|
8
13
|
- "sudo apt-get install -y firefox xvfb libav-tools"
|
9
14
|
- "gem install bundler"
|
10
|
-
script: "rspec"
|
15
|
+
script: "bundle exec rspec"
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 2.3.0 (2016-08-24)
|
2
|
+
|
3
|
+
* Remove dependency on the `which` command (from @skateman)
|
4
|
+
* Add `devices` options to video recorder (from @atzorvas)
|
5
|
+
* By default, do not destroy Headless started by a different process (from @marxarelli)
|
6
|
+
|
1
7
|
## 2.2.3 (2016-03-17)
|
2
8
|
|
3
9
|
* Fix race condition when starting Xvfb [#75] (from @NfNitLoop)
|
data/headless.gemspec
CHANGED
data/lib/headless.rb
CHANGED
@@ -63,16 +63,17 @@ class Headless
|
|
63
63
|
# * +display+ (default 99) - what display number to listen to;
|
64
64
|
# * +reuse+ (default true) - if given display server already exists,
|
65
65
|
# should we use it or try another?
|
66
|
-
# * +autopick+ (default true
|
66
|
+
# * +autopick+ (default true if display number isn't explicitly set) - if
|
67
67
|
# Headless should automatically pick a display, or fail if the given one is
|
68
68
|
# not available.
|
69
69
|
# * +dimensions+ (default 1280x1024x24) - display dimensions and depth. Not
|
70
70
|
# all combinations are possible, refer to +man Xvfb+.
|
71
|
-
# * +destroy_at_exit+
|
72
|
-
#
|
71
|
+
# * +destroy_at_exit+ - if a display is started but not stopped, should it
|
72
|
+
# be destroyed when the script finishes?
|
73
|
+
# (default true unless reuse is true and a server is already running)
|
73
74
|
# * +xvfb_launch_timeout+ - how long should we wait for Xvfb to open a
|
74
75
|
# display, before assuming that it is frozen (in seconds, default is 10)
|
75
|
-
# * +video+ - options to be passed to the ffmpeg video recorder
|
76
|
+
# * +video+ - options to be passed to the ffmpeg video recorder. See Headless::VideoRecorder#initialize for documentation
|
76
77
|
def initialize(options = {})
|
77
78
|
CliUtil.ensure_application_exists!('Xvfb', 'Xvfb not found on your system')
|
78
79
|
|
@@ -82,7 +83,10 @@ class Headless
|
|
82
83
|
@reuse_display = options.fetch(:reuse, true)
|
83
84
|
@dimensions = options.fetch(:dimensions, DEFAULT_DISPLAY_DIMENSIONS)
|
84
85
|
@video_capture_options = options.fetch(:video, {})
|
85
|
-
|
86
|
+
|
87
|
+
already_running = xvfb_running? rescue false
|
88
|
+
@destroy_at_exit = options.fetch(:destroy_at_exit, !(@reuse_display && already_running))
|
89
|
+
|
86
90
|
@pid = nil # the pid of the running Xvfb process
|
87
91
|
|
88
92
|
# FIXME Xvfb launch should not happen inside the constructor
|
@@ -110,18 +114,23 @@ class Headless
|
|
110
114
|
|
111
115
|
# Deprecated.
|
112
116
|
# Same as destroy.
|
113
|
-
# Kept for backward compatibility in June 2015.
|
117
|
+
# Kept for backward compatibility in June 2015.
|
114
118
|
def destroy_sync
|
115
119
|
destroy
|
116
120
|
end
|
117
121
|
|
118
|
-
# Same as the old destroy function -- doesn't wait for Xvfb to die.
|
122
|
+
# Same as the old destroy function -- doesn't wait for Xvfb to die.
|
119
123
|
# Can cause zombies: http://stackoverflow.com/a/31003621/1651458
|
120
124
|
def destroy_without_sync
|
121
125
|
stop
|
122
126
|
CliUtil.kill_process(pid_filename, preserve_pid_file: true)
|
123
127
|
end
|
124
128
|
|
129
|
+
# Whether the headless display will be destroyed when the script finishes.
|
130
|
+
def destroy_at_exit?
|
131
|
+
@destroy_at_exit
|
132
|
+
end
|
133
|
+
|
125
134
|
# Block syntax:
|
126
135
|
#
|
127
136
|
# Headless.run do
|
@@ -238,7 +247,7 @@ private
|
|
238
247
|
@at_exit_hook_installed = true
|
239
248
|
at_exit do
|
240
249
|
exit_status = $!.status if $!.is_a?(SystemExit)
|
241
|
-
destroy if
|
250
|
+
destroy if destroy_at_exit?
|
242
251
|
exit exit_status if exit_status
|
243
252
|
end
|
244
253
|
end
|
data/lib/headless/cli_util.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
1
3
|
class Headless
|
2
4
|
class CliUtil
|
3
5
|
def self.application_exists?(app)
|
4
|
-
|
6
|
+
!!path_to(app)
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.ensure_application_exists!(app, error_message)
|
@@ -10,8 +12,16 @@ class Headless
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
15
|
+
# Credit: http://stackoverflow.com/a/5471032/6678
|
13
16
|
def self.path_to(app)
|
14
|
-
|
17
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
18
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
19
|
+
exts.each { |ext|
|
20
|
+
exe = File.join(path, "#{app}#{ext}")
|
21
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
return nil
|
15
25
|
end
|
16
26
|
|
17
27
|
def self.process_mine?(pid)
|
@@ -2,11 +2,22 @@ require 'tempfile'
|
|
2
2
|
|
3
3
|
class Headless
|
4
4
|
class VideoRecorder
|
5
|
-
attr_accessor :pid_file_path, :tmp_file_path, :log_file_path
|
6
|
-
|
7
|
-
# Allow end-users to override the path to the binary
|
8
|
-
attr_accessor :provider_binary_path
|
5
|
+
attr_accessor :pid_file_path, :tmp_file_path, :log_file_path, :provider_binary_path
|
9
6
|
|
7
|
+
# Construct a new Video Recorder instance. Typically done from inside Headless, but can be also created manually,
|
8
|
+
# and even used separately from Headless' Xvfb features.
|
9
|
+
# * display - display number to capture
|
10
|
+
# * dimensions - dimensions of the captured video
|
11
|
+
# * options - available options:
|
12
|
+
# * provider - either :ffmpeg or :libav; default is :libav - switch if your system is provisioned with FFMpeg
|
13
|
+
# * provider_binary_path - override path to ffmpeg / libav binary
|
14
|
+
# * pid_file_path - override path to PID file, default is placed in /tmp
|
15
|
+
# * tmp_file_path - override path to temp file, default is placed in /tmp
|
16
|
+
# * log_file_path - set log file path, default is /dev/null
|
17
|
+
# * codec - change ffmpeg codec, default is qtrle
|
18
|
+
# * frame_rate - change frame rate, default is 30
|
19
|
+
# * devices - array of device options - see https://www.ffmpeg.org/ffmpeg-devices.html
|
20
|
+
# * extra - array of extra options to append to the FFMpeg command line
|
10
21
|
def initialize(display, dimensions, options = {})
|
11
22
|
@display = display
|
12
23
|
@dimensions = dimensions[/.+(?=x)/]
|
@@ -23,6 +34,7 @@ class Headless
|
|
23
34
|
@provider_binary_path = options.fetch(:provider_binary_path, guess_the_provider_binary_path)
|
24
35
|
|
25
36
|
@extra = Array(options.fetch(:extra, []))
|
37
|
+
@devices = Array(options.fetch(:devices, []))
|
26
38
|
|
27
39
|
CliUtil.ensure_application_exists!(provider_binary_path, "#{provider_binary_path} not found on your system. Install it or change video recorder provider")
|
28
40
|
end
|
@@ -76,16 +88,19 @@ class Headless
|
|
76
88
|
dimensions = @dimensions.match(/^(\d+x\d+)/)[0]
|
77
89
|
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
91
|
+
[
|
92
|
+
CliUtil.path_to(provider_binary_path),
|
93
|
+
"-y",
|
94
|
+
"-r #{@frame_rate}",
|
95
|
+
"-s #{dimensions}",
|
96
|
+
"-f x11grab",
|
97
|
+
@devices,
|
98
|
+
"-i :#{@display}",
|
99
|
+
group_of_pic_size_option,
|
100
|
+
"-vcodec #{@codec}",
|
101
|
+
@extra,
|
102
|
+
@tmp_file_path
|
103
|
+
].flatten.compact.join(' ')
|
89
104
|
end
|
90
105
|
end
|
91
106
|
end
|
data/spec/headless_spec.rb
CHANGED
@@ -53,6 +53,10 @@ describe Headless do
|
|
53
53
|
it "should reuse the existing Xvfb" do
|
54
54
|
expect(Headless.new(options).display).to eq 99
|
55
55
|
end
|
56
|
+
|
57
|
+
it "should not be destroyed at exit by default" do
|
58
|
+
expect(Headless.new(options).destroy_at_exit?).to eq false
|
59
|
+
end
|
56
60
|
end
|
57
61
|
|
58
62
|
context "and display reuse is not allowed" do
|
data/spec/integration_spec.rb
CHANGED
@@ -16,9 +16,7 @@ describe 'Integration test' do
|
|
16
16
|
expect(File.exist?("test.jpg")).to eq true
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
# buggy X11 capture.
|
21
|
-
it 'should record video with ffmpeg', pending: ENV['TRAVIS'] do
|
19
|
+
it 'should record video with ffmpeg' do
|
22
20
|
headless.video.start_capture
|
23
21
|
work_with_browser
|
24
22
|
headless.video.stop_and_save("test.mov")
|
data/spec/video_recorder_spec.rb
CHANGED
@@ -14,27 +14,27 @@ describe Headless::VideoRecorder do
|
|
14
14
|
expect { Headless::VideoRecorder.new(99, "1024x768x32") }.to raise_error(Headless::Exception)
|
15
15
|
end
|
16
16
|
|
17
|
-
it "allows provider_binary_path to be specified" do
|
17
|
+
it "allows provider_binary_path to be specified" do
|
18
18
|
Tempfile.open('some_provider') do |f|
|
19
19
|
v = Headless::VideoRecorder.new(99, "1024x768x32", provider: :ffmpeg, provider_binary_path: f.path)
|
20
20
|
expect(v.provider_binary_path).to eq(f.path)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
it "allows provider_binary_path to be specified" do
|
24
|
+
it "allows provider_binary_path to be specified" do
|
25
25
|
Tempfile.open('some_provider') do |f|
|
26
26
|
v = Headless::VideoRecorder.new(99, "1024x768x32", provider: :ffmpeg, provider_binary_path: f.path)
|
27
27
|
expect(v.provider_binary_path).to eq(f.path)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
context "provider_binary_path not specified" do
|
32
|
-
it "assumes the provider binary is 'ffmpeg' if the provider is :ffmpeg" do
|
31
|
+
context "provider_binary_path not specified" do
|
32
|
+
it "assumes the provider binary is 'ffmpeg' if the provider is :ffmpeg" do
|
33
33
|
v = Headless::VideoRecorder.new(99, "1024x768x32", provider: :ffmpeg)
|
34
34
|
expect(v.provider_binary_path).to eq("ffmpeg")
|
35
35
|
end
|
36
36
|
|
37
|
-
it "assumes the provider binary is 'avconv' if the provider is :libav" do
|
37
|
+
it "assumes the provider binary is 'avconv' if the provider is :libav" do
|
38
38
|
v = Headless::VideoRecorder.new(99, "1024x768x32", provider: :libav)
|
39
39
|
expect(v.provider_binary_path).to eq("avconv")
|
40
40
|
end
|
@@ -67,6 +67,13 @@ describe Headless::VideoRecorder do
|
|
67
67
|
recorder = Headless::VideoRecorder.new(99, "1024x768x32", {:provider => :ffmpeg})
|
68
68
|
recorder.start_capture
|
69
69
|
end
|
70
|
+
|
71
|
+
it "starts ffmpeg with specified extra device options" do
|
72
|
+
expect(Headless::CliUtil).to receive(:fork_process).with(/^ffmpeg -y -r 30 -s 1024x768 -f x11grab -draw_mouse 0 -i :99 -g 600 -vcodec qtrle [^ ]+$/, "/tmp/.headless_ffmpeg_99.pid", '/dev/null')
|
73
|
+
|
74
|
+
recorder = Headless::VideoRecorder.new(99, "1024x768x32", {:devices => ["-draw_mouse 0"]})
|
75
|
+
recorder.start_capture
|
76
|
+
end
|
70
77
|
end
|
71
78
|
|
72
79
|
context "stopping video recording" do
|
@@ -100,7 +107,7 @@ describe Headless::VideoRecorder do
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
|
-
private
|
110
|
+
private
|
104
111
|
|
105
112
|
def stub_environment
|
106
113
|
allow(Headless::CliUtil).to receive(:application_exists?).and_return(true)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: headless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leonid Shevtsov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -99,3 +99,4 @@ signing_key:
|
|
99
99
|
specification_version: 4
|
100
100
|
summary: Ruby headless display interface
|
101
101
|
test_files: []
|
102
|
+
has_rdoc:
|