vidibus-recording 1.0.0 → 2.0.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.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Andre Pankratz
1
+ Copyright (c) 2011-2013 Andre Pankratz
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # Vidibus::Recording
2
+
3
+ Allows recording of RTMP video streams. Uses RTMPdump. Requires at least Ruby 1.9.
4
+
5
+ This gem is part of [Vidibus](http://vidibus.org), an open source toolset for building distributed (video) applications.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add `gem 'vidibus-recording'` to the Gemfile of your application. Then call `bundle install` on your console.
11
+
12
+
13
+ ## Usage
14
+
15
+ ### Available methods
16
+
17
+ To control a recording, you may use these methods:
18
+
19
+ ```ruby
20
+ recording.start # starts recording
21
+ recording.stop # stops recording
22
+ recording.resume # continues if recording has been started but is not running
23
+ recording.restart # erases recorded data and restarts recording
24
+ ```
25
+
26
+
27
+ ### Custom class names
28
+
29
+ This gem will set up a model `Recording` if Rails is around. If you want to use the recording logic inside of a custom model, you just have to include the module `Vidibus::Recording::Mongoid`:
30
+
31
+ ```ruby
32
+ class MyCustomRecording
33
+ include Mongoid::Document
34
+ include Vidibus::Recording::Mongoid
35
+ end
36
+ ```
37
+
38
+
39
+ ### Monitoring
40
+
41
+ If the worker process does not receive data, it will halt the recording. To monitor and restart a recording perform `Vidibus::Recording.monitor`. Beware, this method is blocking, so better spawn the daemon.
42
+
43
+
44
+ #### Monitoring daemon
45
+
46
+ To run the monitor as daemon, this gem provides a shell script. Install it with
47
+
48
+ ```
49
+ rails g vidibus:recording
50
+ ```
51
+
52
+ The daemon requires that `gem 'daemons'` is installed. To spawn him, enter
53
+
54
+ ```
55
+ script/recording start
56
+ ```
57
+
58
+ #### Possible caveat
59
+
60
+ To monitor your custom recording classes, `Vidibus::Recording.monitor` requires that all classes that include `Vidibus::Recording::Mongoid` have been loaded.
61
+
62
+ Because Rails is autoloading almost everything in development, this requirement is not met without the help of a little hack: To trigger autoloading, the monitor collects all aforementioned class names from the `app` directory and constantizes them.
63
+
64
+ **So here's the caveat:** If you define custom recording models outside of the `app` directory, you'll have to let the listener know. An initializer is perfect for that:
65
+
66
+ ```ruby
67
+ # Collect all recording models in lib, too
68
+ Vidibus::Recording.autoload_paths << '/lib/**/*.rb'
69
+ ```
70
+
71
+
72
+ ## Deployment
73
+
74
+ A Capistrano configuration is included. Require it in your Capistrano `config.rb`.
75
+
76
+ ```ruby
77
+ require 'vidibus/recording/capistrano'
78
+ ```
79
+
80
+ That will add a bunch of callback hooks.
81
+
82
+ ```ruby
83
+ after 'deploy:stop', 'vidibus:recording:stop'
84
+ after 'deploy:start', 'vidibus:recording:start'
85
+ after 'deploy:restart', 'vidibus:recording:restart'
86
+ ```
87
+
88
+ If you need more control over the callbacks, you may load just the recipes without the hooks.
89
+
90
+ ```ruby
91
+ require 'vidibus/recording/capistrano/recipes'
92
+ ```
93
+
94
+
95
+ ## Testing
96
+
97
+ To test this gem, call `bundle install` and `bundle exec rspec spec` on your console.
98
+
99
+
100
+ ## Copyright
101
+
102
+ &copy; 2011-2013 André Pankratz. See LICENSE for details.
@@ -2,7 +2,7 @@ require 'rails/generators'
2
2
  require 'rails/generators/named_base'
3
3
 
4
4
  module Vidibus
5
- class RecorderGenerator < Rails::Generators::Base
5
+ class RecordingGenerator < Rails::Generators::Base
6
6
 
7
7
  self.source_paths << File.join(File.dirname(__FILE__), 'templates')
8
8
 
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require File.expand_path('../../config/environment', __FILE__)
4
- require 'vidibus/recorder/daemon'
4
+ require 'vidibus/recording/daemon'
5
5
 
6
- Vidibus::WatchFolder::Daemon.new(ARGV).daemonize
6
+ Vidibus::Recording.monitoring_interval = 1 # second
7
+ Vidibus::Recording.autoload_paths << Rails.root.join('app/**/*.rb')
8
+ Vidibus::Recording::Daemon.new(ARGV).daemonize
@@ -79,7 +79,7 @@ module Vidibus::Recording::Backend
79
79
  end
80
80
  end
81
81
 
82
- # Extract metadata from stdout or stderr.
82
+ # Detect error from stdout or stderr.
83
83
  # Output delivered by rtmpdump looks like this:
84
84
  #
85
85
  # RTMPDump v2.4
@@ -88,9 +88,12 @@ module Vidibus::Recording::Backend
88
88
  # ERROR: Problem accessing the DNS. (addr: whatever.domain)
89
89
  #
90
90
  def detect_error(string)
91
- prefix = /(?:ERROR\:\ (.+))/ if string.match(/ERROR\:/)
92
- if string.match(/(?:ERROR\:\ (.+))/)
93
- raise RuntimeError.new($1)
91
+ if error = string[/(?:ERROR\:\ (.+))/,1]
92
+ case error
93
+ when 'rtmp server sent error'
94
+ else
95
+ raise RuntimeError.new($1)
96
+ end
94
97
  end
95
98
  end
96
99
  end
@@ -1,4 +1,4 @@
1
- # Capistrano Recipes for watching folders.
1
+ # Capistrano Recipes for monitoring recordings.
2
2
  #
3
3
  # Load this file from your Capistrano config.rb:
4
4
  # require 'vidibus/recording/capistrano/recipes'
@@ -1,12 +1,12 @@
1
1
  require 'vidibus/recording/capistrano/recipes'
2
2
 
3
- # Run Capistrano Recipes for watching folders.
3
+ # Run Capistrano Recipes for monitoring recordings.
4
4
  #
5
5
  # Load this file from your Capistrano config.rb:
6
- # require 'vidibus/watch_folder/capistrano'
6
+ # require 'vidibus/recording/capistrano'
7
7
  #
8
8
  Capistrano::Configuration.instance.load do
9
- after 'deploy:stop', 'vidibus:watch_folder:stop'
10
- after 'deploy:start', 'vidibus:watch_folder:start'
11
- after 'deploy:restart', 'vidibus:watch_folder:restart'
9
+ after 'deploy:stop', 'vidibus:recording:stop'
10
+ after 'deploy:start', 'vidibus:recording:start'
11
+ after 'deploy:restart', 'vidibus:recording:restart'
12
12
  end
@@ -6,7 +6,7 @@ end
6
6
  require 'optparse'
7
7
 
8
8
  module Vidibus
9
- module Recorder
9
+ module Recording
10
10
  class Daemon
11
11
 
12
12
  def initialize(args)
@@ -34,10 +34,10 @@ module Vidibus
34
34
  def run
35
35
  Dir.chdir(Rails.root)
36
36
  log = File.join(Rails.root, 'log', 'recording.log')
37
- Vidibus::Recorder.logger = ActiveSupport::BufferedLogger.new(log)
38
- Vidibus::Recorder.monitor
37
+ Vidibus::Recording.logger = ActiveSupport::BufferedLogger.new(log)
38
+ Vidibus::Recording.monitor
39
39
  rescue => e
40
- Vidibus::Recorder.logger.fatal(e)
40
+ Vidibus::Recording.logger.fatal(e)
41
41
  STDERR.puts(e.message)
42
42
  exit 1
43
43
  end
@@ -1,3 +1,8 @@
1
+ require 'mongoid'
2
+ require 'vidibus-uuid'
3
+ require 'active_support/core_ext'
4
+ require 'delayed_job_mongoid'
5
+
1
6
  module Vidibus::Recording
2
7
  module Mongoid
3
8
  extend ActiveSupport::Concern
@@ -10,7 +15,6 @@ module Vidibus::Recording
10
15
 
11
16
  field :name
12
17
  field :stream
13
- # field :live, :type => Boolean
14
18
  field :pid, :type => Integer
15
19
  field :info, :type => Hash
16
20
  field :size, :type => Integer
@@ -20,13 +24,17 @@ module Vidibus::Recording
20
24
  field :started_at, :type => DateTime
21
25
  field :stopped_at, :type => DateTime
22
26
  field :failed_at, :type => DateTime
27
+ field :active, :type => Boolean, :default => false
23
28
  field :running, :type => Boolean, :default => false
24
- field :monitoring_job_identifier, :type => String
29
+
30
+ index :active
25
31
 
26
32
  validates :name, :presence => true
27
33
  validates :stream, :format => {:with => /^rtmp.*?:\/\/.+$/}
28
34
 
29
35
  before_destroy :cleanup
36
+
37
+ scope :active, where(active: true)
30
38
  end
31
39
 
32
40
  # Starts a recording worker now, unless it has been done already.
@@ -35,8 +43,8 @@ module Vidibus::Recording
35
43
  return false if done? || started?
36
44
  if time == :now
37
45
  self.started_at = Time.now
46
+ self.active = true
38
47
  start_worker
39
- start_monitoring_job
40
48
  save!
41
49
  else
42
50
  schedule(time)
@@ -48,8 +56,8 @@ module Vidibus::Recording
48
56
  return false if running? || !started?
49
57
  self.stopped_at = nil
50
58
  self.failed_at = nil
59
+ self.active = true
51
60
  start_worker
52
- start_monitoring_job
53
61
  save!
54
62
  end
55
63
 
@@ -67,7 +75,7 @@ module Vidibus::Recording
67
75
  self.pid = nil
68
76
  self.stopped_at = Time.now
69
77
  self.running = false
70
- self.monitoring_job_identifier = nil
78
+ self.active = false
71
79
  postprocess
72
80
  end
73
81
 
@@ -89,6 +97,7 @@ module Vidibus::Recording
89
97
  self.error = msg
90
98
  self.failed_at = Time.now
91
99
  self.running = false
100
+ self.active = false
92
101
  postprocess
93
102
  end
94
103
 
@@ -104,8 +113,7 @@ module Vidibus::Recording
104
113
  :info,
105
114
  :error,
106
115
  :size,
107
- :duration,
108
- :monitoring_job_identifier
116
+ :duration
109
117
  ].map {|a| blank[a] = nil }
110
118
  update_attributes!(blank)
111
119
  destroy_all_parts
@@ -222,16 +230,6 @@ module Vidibus::Recording
222
230
  self.pid = worker.pid
223
231
  end
224
232
 
225
- # Start a new monitoring job
226
- def start_monitoring_job
227
- self.monitoring_job_identifier = Vidibus::Uuid.generate
228
- Vidibus::Recording::MonitoringJob.create({
229
- :class_name => self.class.to_s,
230
- :uuid => uuid,
231
- :identifier => monitoring_job_identifier
232
- })
233
- end
234
-
235
233
  def setup_next_part
236
234
  number = nil
237
235
  if current_part
@@ -1,3 +1,7 @@
1
+ require 'yaml'
2
+ require 'mongoid'
3
+ require 'vidibus-uuid'
4
+
1
5
  module Vidibus::Recording
2
6
  class Part
3
7
  include Mongoid::Document
@@ -1 +1,9 @@
1
- railtie.rb
1
+ require 'rails'
2
+
3
+ module Vidibus
4
+ module Recording
5
+ if defined?(Rails)
6
+ class Engine < ::Rails::Engine; end
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module Vidibus
2
2
  module Recording
3
- VERSION = '1.0.0'
3
+ VERSION = '2.0.0'
4
4
  end
5
5
  end
@@ -1,11 +1,12 @@
1
+ require 'open3'
1
2
  require 'timeout'
2
3
 
3
4
  module Vidibus::Recording
4
5
  class Worker
5
6
  class ProcessError < StandardError; end
6
7
 
7
- # START_TIMEOUT = 20
8
- STOP_TIMEOUT = 10
8
+ START_TIMEOUT = 20
9
+ STOP_TIMEOUT = 2
9
10
 
10
11
  attr_accessor :recording, :pid, :metadata
11
12
 
@@ -33,8 +34,7 @@ module Vidibus::Recording
33
34
  Timeout::timeout(STOP_TIMEOUT) do
34
35
  begin
35
36
  log("Stopping process #{pid}...")
36
- # Use SIGQUIT to terminate because DelayedJob traps INT and TERM
37
- Process.kill('SIGQUIT', pid)
37
+ Process.kill('SIGTERM', pid)
38
38
  Process.wait(pid)
39
39
  log('STOPPED')
40
40
  rescue Errno::ECHILD
@@ -73,8 +73,8 @@ module Vidibus::Recording
73
73
  def record
74
74
  cmd = recording.backend.command
75
75
  log("START: #{recording.stream}", true)
76
+ timeout = Time.now + START_TIMEOUT
76
77
  Open3::popen3(cmd) do |stdin, stdout, stderr|
77
- maxloops = 10
78
78
  loop do
79
79
  begin
80
80
  string = stdout.read_nonblock(1024).force_encoding('UTF-8')
@@ -90,12 +90,11 @@ module Vidibus::Recording
90
90
  fail(e.message) && break
91
91
  end
92
92
  unless metadata
93
- maxloops -= 1
94
- if maxloops == 0
95
- halt('No Metadata has been received so far.') && break
93
+ if Time.now > timeout
94
+ halt('No Metadata has been received so far, exiting.') && break
96
95
  end
97
96
  end
98
- sleep 2
97
+ sleep(2)
99
98
  end
100
99
  end
101
100
  end
@@ -1,6 +1,65 @@
1
1
  require 'vidibus/recording/worker'
2
- require 'vidibus/recording/monitoring_job'
3
2
  require 'vidibus/recording/backend'
4
3
  require 'vidibus/recording/helpers'
5
4
  require 'vidibus/recording/part'
6
5
  require 'vidibus/recording/mongoid'
6
+ require 'vidibus/recording/railtie' if defined?(Rails::Railtie)
7
+
8
+ module Vidibus
9
+ module Recording
10
+ extend self
11
+
12
+ class Error < StandardError; end
13
+
14
+ INTERVAL = 1
15
+
16
+ attr_accessor :logger, :autoload_paths, :classes, :monitoring_interval
17
+ @logger = Logger.new(STDOUT)
18
+ @autoload_paths = []
19
+ @classes = []
20
+ @monitoring_interval = 1
21
+
22
+ # Monitor all started recordings
23
+ def monitor
24
+ autoload
25
+ unless classes.any?
26
+ logger.error("[#{Time.now.utc}] - No recording classes given")
27
+ else
28
+ logger.info("[#{Time.now.utc}] - Watching recordings")
29
+ run
30
+ end
31
+ end
32
+
33
+ # Obtain all classes that include the Mongoid module
34
+ def autoload
35
+ return [] unless autoload_paths.any?
36
+ regexp = /class ([^<\n]+).+include Vidibus::Recording::Mongoid/m
37
+ names = Dir[*autoload_paths].map do |f|
38
+ File.read(f)[regexp, 1]
39
+ end.compact
40
+ self.classes = names.map { |k| k.constantize }
41
+ end
42
+
43
+ private
44
+
45
+ def run
46
+ loop do
47
+ classes.each do |klass|
48
+ klass.active.each do |recording|
49
+ begin
50
+ if recording.worker_running?
51
+ recording.track_progress
52
+ else
53
+ logger.info("[#{Time.now.utc}] - Resuming #{recording.class.name} #{recording.uuid}")
54
+ recording.resume
55
+ end
56
+ rescue => e
57
+ logger.error("[#{Time.now.utc}] - ERROR:\n#{e.inspect}\n---\n#{e.backtrace.join("\n")}")
58
+ end
59
+ end
60
+ end
61
+ sleep(monitoring_interval)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,15 +1 @@
1
- require 'open3'
2
- require 'yaml'
3
- require 'delayed_job_mongoid'
4
- require 'active_support/core_ext'
5
- require 'vidibus-uuid'
6
-
7
- module Vidibus
8
- module Recording
9
- if defined?(Rails)
10
- class Engine < ::Rails::Engine; end
11
- end
12
- end
13
- end
14
-
15
1
  require 'vidibus/recording'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vidibus-recording
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-30 00:00:00.000000000 Z
12
+ date: 2013-08-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: '2'
37
+ version: '2.5'
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: '2'
45
+ version: '2.5'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: delayed_job_mongoid
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -179,7 +179,6 @@ extra_rdoc_files: []
179
179
  files:
180
180
  - lib/generators/vidibus/recording_generator.rb
181
181
  - lib/generators/vidibus/templates/script
182
- - lib/vidibus/recording/backend/railtie.rb
183
182
  - lib/vidibus/recording/backend/rtmpdump.rb
184
183
  - lib/vidibus/recording/backend.rb
185
184
  - lib/vidibus/recording/capistrano/recipes.rb
@@ -187,7 +186,6 @@ files:
187
186
  - lib/vidibus/recording/daemon.rb
188
187
  - lib/vidibus/recording/helpers.rb
189
188
  - lib/vidibus/recording/mongoid.rb
190
- - lib/vidibus/recording/monitoring_job.rb
191
189
  - lib/vidibus/recording/part.rb
192
190
  - lib/vidibus/recording/railtie.rb
193
191
  - lib/vidibus/recording/version.rb
@@ -196,7 +194,7 @@ files:
196
194
  - lib/vidibus-recording.rb
197
195
  - app/models/recording.rb
198
196
  - LICENSE
199
- - README.rdoc
197
+ - README.md
200
198
  - Rakefile
201
199
  homepage: https://github.com/vidibus/vidibus-recording
202
200
  licenses: []
@@ -212,7 +210,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
210
  version: '0'
213
211
  segments:
214
212
  - 0
215
- hash: 766807753862642645
213
+ hash: 507116490268825761
216
214
  required_rubygems_version: !ruby/object:Gem::Requirement
217
215
  none: false
218
216
  requirements:
data/README.rdoc DELETED
@@ -1,11 +0,0 @@
1
- = Vidibus::Recording
2
-
3
- Allows recording of RTMP video streams. Uses RTMPdump.
4
- Further description goes here.
5
-
6
- Requires Ruby 1.9
7
-
8
-
9
- == Copyright
10
-
11
- Copyright (c) 2013 Andre Pankratz. See LICENSE for details.
@@ -1 +0,0 @@
1
- railtie.rb
@@ -1,52 +0,0 @@
1
- module Vidibus::Recording
2
- class MonitoringJob
3
- INTERVAL = 10.seconds
4
-
5
- def initialize(args)
6
- unless @uuid = args[:uuid]
7
- raise(ArgumentError, 'No recording UUID given')
8
- end
9
- unless @class_name = args[:class_name]
10
- raise(ArgumentError, 'Must provide class name of recording')
11
- end
12
- unless @identifier = args[:identifier]
13
- raise(ArgumentError, 'Must provide identifier of monitoring job')
14
- end
15
- ensure_recording
16
- end
17
-
18
- def perform
19
- r = recording.reload
20
- return unless r.monitoring_job_identifier == @identifier
21
- if r.worker_running?
22
- r.track_progress
23
- run_again
24
- elsif !r.stopped?
25
- r.resume
26
- end
27
- end
28
-
29
- # Returns job
30
- def self.create(args)
31
- job = new(args)
32
- Delayed::Job.enqueue(job)
33
- end
34
-
35
- private
36
-
37
- def recording
38
- @class_name.constantize.where(:uuid => @uuid).first
39
- end
40
-
41
- def ensure_recording
42
- recording || raise(ArgumentError, 'No valid recording UUID given')
43
- end
44
-
45
- def run_again
46
- obj = self.class.new({
47
- :uuid => @uuid, :class_name => @class_name, :identifier => @identifier
48
- })
49
- Delayed::Job.enqueue(obj, 0, INTERVAL.from_now)
50
- end
51
- end
52
- end