headless 2.2.3 → 2.3.0

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