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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93e191ea69ebabc46da9431bff74b33ccbf646c9
4
- data.tar.gz: 68b29362c47b8e776153638f187c62f467fdd422
3
+ metadata.gz: 64adcb425e2e63bfed70d55fc99365e60c87887d
4
+ data.tar.gz: e3eeace8449b30b55cfee239b666bef2dedbd96d
5
5
  SHA512:
6
- metadata.gz: 757f8e09da108796a51bc6b823fe8b4ab67e8d4f900ff11dcdccbfe1434a0ca5bb5cda1a28c4cdc98a49664081512fbe5c57ca54ddc21e0a797b53b1b30eca40
7
- data.tar.gz: cde1f7f928582f1f6c6f90a0b06a5c1389b2ca66f682ad78a141294bfbfa4df9666ee87c2df2be1283c9fdeb3d8a3fe2f0e7a6dceadb9638b206df5198c60b12
6
+ metadata.gz: 66c57e7f5ce63679504a43cfa9018e956a609cf1d1e6298285bfa1626397fa293779f95070d7267fb3f41418a3c9ef3b1919cd1d78a7e7fa6bfdc93beebc36b2
7
+ data.tar.gz: 9d86ad8c3bd3ccf27b914943ab6ffac5fc942c13fdb458cef3e9b2728c743826390fac433b882609cd98cb8800ec1e1b67237bf2ce98128992b8d27861f2f3e2
@@ -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.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)
@@ -3,7 +3,7 @@ Gem::Specification.new do |s|
3
3
  s.email = 'leonid@shevtsov.me'
4
4
 
5
5
  s.name = 'headless'
6
- s.version = '2.2.3'
6
+ s.version = '2.3.0'
7
7
  s.summary = 'Ruby headless display interface'
8
8
  s.license = 'MIT'
9
9
 
@@ -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 is display number isn't explicitly set) - if
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+ (default true) - if a display is started but not
72
- # stopped, should it be destroyed when the script finishes?
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
- @destroy_at_exit = options.fetch(:destroy_at_exit, true)
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 @destroy_at_exit
250
+ destroy if destroy_at_exit?
242
251
  exit exit_status if exit_status
243
252
  end
244
253
  end
@@ -1,7 +1,9 @@
1
+ require 'mkmf'
2
+
1
3
  class Headless
2
4
  class CliUtil
3
5
  def self.application_exists?(app)
4
- `which #{app}`.strip != ""
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
- `which #{app}`.strip
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
- CliUtil.path_to(provider_binary_path),
81
- "-y",
82
- "-r #{@frame_rate}",
83
- "-s #{dimensions}",
84
- "-f x11grab",
85
- "-i :#{@display}",
86
- group_of_pic_size_option,
87
- "-vcodec #{@codec}"
88
- ].compact + @extra + [@tmp_file_path]).join(' ')
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
@@ -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
@@ -16,9 +16,7 @@ describe 'Integration test' do
16
16
  expect(File.exist?("test.jpg")).to eq true
17
17
  end
18
18
 
19
- # Unfortunately the libav version that ships with Ubuntu 12.04 has
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")
@@ -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.2.3
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-03-17 00:00:00.000000000 Z
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: