enigmamachine 0.5.0 → 0.6.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/.autotest +4 -0
- data/README.rdoc +6 -7
- data/Rakefile +4 -1
- data/VERSION +1 -1
- data/enigmamachine.gemspec +21 -10
- data/lib/enigmamachine.rb +20 -8
- data/lib/enigmamachine/download_queue.rb +31 -0
- data/lib/enigmamachine/encoding_queue.rb +3 -4
- data/lib/enigmamachine/models/encoder.rb +0 -34
- data/lib/enigmamachine/models/video.rb +184 -24
- data/lib/enigmamachine/views/videos/index.erb +14 -0
- data/lib/generators/config.yml +3 -0
- data/test/helper.rb +4 -0
- data/test/support/blueprints.rb +17 -12
- data/test/test_encoder.rb +11 -17
- data/test/test_enigmamachine.rb +1 -1
- data/test/test_video.rb +94 -32
- metadata +92 -23
data/.autotest
ADDED
data/README.rdoc
CHANGED
@@ -45,8 +45,8 @@ Then check it out in your browser, at http://localhost:2002. This web interface
|
|
45
45
|
exists so that you can configure your enigmamachine easily, and check its status.
|
46
46
|
|
47
47
|
Most of the time, though, your enigmamachine will be accessed not through a
|
48
|
-
browser, but by your program's code. Let's say you want your website to be able
|
49
|
-
|
48
|
+
browser, but by your program's code. Let's say you want your website to be able
|
49
|
+
to encode video. You write the code for the video upload, so your web app can
|
50
50
|
receive the video. When the upload is complete, make an HTTP call something like this in your code:
|
51
51
|
|
52
52
|
POST http://localhost:2002/videos
|
@@ -214,11 +214,10 @@ system).
|
|
214
214
|
|
215
215
|
== Status
|
216
216
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
copy of ffmpeg available on your path.
|
217
|
+
Enigmamachine should be considered beta quality code. At the same time,
|
218
|
+
crashes and bugs have been infrequent for us, and it's a simple system which
|
219
|
+
is working well. Please let us know of any bugs by filing an issue on the
|
220
|
+
GitHub tracker.
|
222
221
|
|
223
222
|
|
224
223
|
== Note on Patches/Pull Requests
|
data/Rakefile
CHANGED
@@ -10,15 +10,18 @@ begin
|
|
10
10
|
gem.email = "dave@caprica"
|
11
11
|
gem.homepage = "http://github.com/futurechimp/enigmamachine"
|
12
12
|
gem.authors = ["Dave Hrycyszyn", "Dmitry Brazhkin"]
|
13
|
-
gem.add_development_dependency "thoughtbot-shoulda"
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
14
|
+
gem.add_development_dependency "rack-test"
|
14
15
|
gem.add_dependency "data_mapper", ">=1.0.2"
|
15
16
|
gem.add_dependency "dm-sqlite-adapter", ">=1.0.2"
|
17
|
+
gem.add_dependency "state_machine", ">=0.9.4"
|
16
18
|
gem.add_dependency "eventmachine", ">=0.12.10"
|
17
19
|
gem.add_dependency "rack-flash"
|
18
20
|
gem.add_dependency "ruby-debug"
|
19
21
|
gem.add_dependency "sinatra", ">=1.0.0"
|
20
22
|
gem.add_dependency "streamio-ffmpeg", ">=0.7.3"
|
21
23
|
gem.add_dependency "thin"
|
24
|
+
gem.add_dependency "em-http-request"
|
22
25
|
|
23
26
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
27
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/enigmamachine.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{enigmamachine}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Dave Hrycyszyn", "Dmitry Brazhkin"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-11-01}
|
13
13
|
s.default_executable = %q{enigmamachine}
|
14
14
|
s.description = %q{A RESTful video encoder which you can use as either a front-end to ffmpeg or headless on a server.}
|
15
15
|
s.email = %q{dave@caprica}
|
@@ -19,7 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
"README.rdoc"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
|
-
".
|
22
|
+
".autotest",
|
23
|
+
".document",
|
23
24
|
".gitignore",
|
24
25
|
"LICENSE",
|
25
26
|
"README.rdoc",
|
@@ -30,6 +31,7 @@ Gem::Specification.new do |s|
|
|
30
31
|
"lib/enigmamachine.rb",
|
31
32
|
"lib/enigmamachine.sqlite3",
|
32
33
|
"lib/enigmamachine/config.ru",
|
34
|
+
"lib/enigmamachine/download_queue.rb",
|
33
35
|
"lib/enigmamachine/encoding_queue.rb",
|
34
36
|
"lib/enigmamachine/models/encoder.rb",
|
35
37
|
"lib/enigmamachine/models/encoding_task.rb",
|
@@ -76,52 +78,61 @@ Gem::Specification.new do |s|
|
|
76
78
|
s.homepage = %q{http://github.com/futurechimp/enigmamachine}
|
77
79
|
s.rdoc_options = ["--charset=UTF-8"]
|
78
80
|
s.require_paths = ["lib"]
|
79
|
-
s.rubygems_version = %q{1.3.
|
81
|
+
s.rubygems_version = %q{1.3.7}
|
80
82
|
s.summary = %q{A RESTful video encoder.}
|
81
83
|
s.test_files = [
|
82
|
-
"test/
|
83
|
-
"test/helper.rb",
|
84
|
-
"test/test_encoding_queue.rb",
|
85
|
-
"test/test_video.rb",
|
84
|
+
"test/test_encoding_queue.rb",
|
86
85
|
"test/test_encoder.rb",
|
87
|
-
"test/
|
86
|
+
"test/test_video.rb",
|
87
|
+
"test/test_enigmamachine.rb",
|
88
|
+
"test/helper.rb",
|
89
|
+
"test/support/blueprints.rb"
|
88
90
|
]
|
89
91
|
|
90
92
|
if s.respond_to? :specification_version then
|
91
93
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
92
94
|
s.specification_version = 3
|
93
95
|
|
94
|
-
if Gem::Version.new(Gem::
|
96
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
95
97
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
98
|
+
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
96
99
|
s.add_runtime_dependency(%q<data_mapper>, [">= 1.0.2"])
|
97
100
|
s.add_runtime_dependency(%q<dm-sqlite-adapter>, [">= 1.0.2"])
|
101
|
+
s.add_runtime_dependency(%q<state_machine>, [">= 0.9.4"])
|
98
102
|
s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.10"])
|
99
103
|
s.add_runtime_dependency(%q<rack-flash>, [">= 0"])
|
100
104
|
s.add_runtime_dependency(%q<ruby-debug>, [">= 0"])
|
101
105
|
s.add_runtime_dependency(%q<sinatra>, [">= 1.0.0"])
|
102
106
|
s.add_runtime_dependency(%q<streamio-ffmpeg>, [">= 0.7.3"])
|
103
107
|
s.add_runtime_dependency(%q<thin>, [">= 0"])
|
108
|
+
s.add_runtime_dependency(%q<em-http-request>, [">= 0"])
|
104
109
|
else
|
105
110
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
111
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
106
112
|
s.add_dependency(%q<data_mapper>, [">= 1.0.2"])
|
107
113
|
s.add_dependency(%q<dm-sqlite-adapter>, [">= 1.0.2"])
|
114
|
+
s.add_dependency(%q<state_machine>, [">= 0.9.4"])
|
108
115
|
s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
|
109
116
|
s.add_dependency(%q<rack-flash>, [">= 0"])
|
110
117
|
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
111
118
|
s.add_dependency(%q<sinatra>, [">= 1.0.0"])
|
112
119
|
s.add_dependency(%q<streamio-ffmpeg>, [">= 0.7.3"])
|
113
120
|
s.add_dependency(%q<thin>, [">= 0"])
|
121
|
+
s.add_dependency(%q<em-http-request>, [">= 0"])
|
114
122
|
end
|
115
123
|
else
|
116
124
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
125
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
117
126
|
s.add_dependency(%q<data_mapper>, [">= 1.0.2"])
|
118
127
|
s.add_dependency(%q<dm-sqlite-adapter>, [">= 1.0.2"])
|
128
|
+
s.add_dependency(%q<state_machine>, [">= 0.9.4"])
|
119
129
|
s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
|
120
130
|
s.add_dependency(%q<rack-flash>, [">= 0"])
|
121
131
|
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
122
132
|
s.add_dependency(%q<sinatra>, [">= 1.0.0"])
|
123
133
|
s.add_dependency(%q<streamio-ffmpeg>, [">= 0.7.3"])
|
124
134
|
s.add_dependency(%q<thin>, [">= 0"])
|
135
|
+
s.add_dependency(%q<em-http-request>, [">= 0"])
|
125
136
|
end
|
126
137
|
end
|
127
138
|
|
data/lib/enigmamachine.rb
CHANGED
@@ -8,9 +8,10 @@ require 'eventmachine'
|
|
8
8
|
require 'rack-flash'
|
9
9
|
require 'dm-validations'
|
10
10
|
require 'dm-migrations'
|
11
|
-
require '
|
11
|
+
require 'state_machine'
|
12
12
|
require 'logger'
|
13
13
|
require 'streamio-ffmpeg'
|
14
|
+
require 'em-http-request'
|
14
15
|
|
15
16
|
# Extensions to Sinatra
|
16
17
|
#
|
@@ -24,6 +25,7 @@ require File.dirname(__FILE__) + '/enigmamachine/models/encoder'
|
|
24
25
|
require File.dirname(__FILE__) + '/enigmamachine/models/encoding_task'
|
25
26
|
require File.dirname(__FILE__) + '/enigmamachine/models/video'
|
26
27
|
require File.dirname(__FILE__) + '/enigmamachine/encoding_queue'
|
28
|
+
require File.dirname(__FILE__) + '/enigmamachine/download_queue'
|
27
29
|
|
28
30
|
|
29
31
|
|
@@ -38,7 +40,6 @@ class EnigmaMachine < Sinatra::Base
|
|
38
40
|
|
39
41
|
configure :development do
|
40
42
|
db = "sqlite3:///#{Dir.pwd}/enigmamachine.sqlite3"
|
41
|
-
DataMapper::Logger.new(STDOUT, :debug)
|
42
43
|
DataMapper.setup(:default, db)
|
43
44
|
end
|
44
45
|
|
@@ -60,9 +61,12 @@ class EnigmaMachine < Sinatra::Base
|
|
60
61
|
unless File.exist? File.join(Dir.getwd, 'config.yml')
|
61
62
|
FileUtils.cp(File.dirname(__FILE__) + '/generators/config.yml', Dir.getwd)
|
62
63
|
end
|
63
|
-
|
64
|
-
@@username =
|
65
|
-
@@password =
|
64
|
+
config = YAML.load(File.read(Dir.getwd + "/config.yml"))
|
65
|
+
@@username = config['username']
|
66
|
+
@@password = config['password']
|
67
|
+
@@threads = config['threads']
|
68
|
+
@@enable_http_downloads = config['enable_http_downloads']
|
69
|
+
@@download_storage_path = config['download_storage_path']
|
66
70
|
end
|
67
71
|
|
68
72
|
# Set the views to the proper path inside the gem
|
@@ -93,12 +97,14 @@ class EnigmaMachine < Sinatra::Base
|
|
93
97
|
# main Sinatra/thin thread once the periodic timer is added.
|
94
98
|
#
|
95
99
|
configure do
|
96
|
-
Video.reset_encoding_videos
|
97
100
|
Thread.new do
|
98
101
|
until EM.reactor_running?
|
99
102
|
sleep 1
|
100
103
|
end
|
101
|
-
|
104
|
+
Video.reset_encoding_videos
|
105
|
+
Video.reset_downloading_videos
|
106
|
+
encode_queue = EncodingQueue.new
|
107
|
+
download_queue = DownloadQueue.new
|
102
108
|
end
|
103
109
|
end
|
104
110
|
|
@@ -110,6 +116,10 @@ class EnigmaMachine < Sinatra::Base
|
|
110
116
|
[username, password] == [@@username, @@password]
|
111
117
|
end
|
112
118
|
|
119
|
+
# Some accessors for config variables
|
120
|
+
#
|
121
|
+
cattr_accessor :download_storage_path, :enable_http_downloads, :threads
|
122
|
+
|
113
123
|
|
114
124
|
# Shows the enigma status page.
|
115
125
|
#
|
@@ -242,8 +252,10 @@ class EnigmaMachine < Sinatra::Base
|
|
242
252
|
get '/videos' do
|
243
253
|
@completed_videos = Video.complete
|
244
254
|
@encoding_videos = Video.encoding
|
245
|
-
@videos_with_errors = Video.
|
255
|
+
@videos_with_errors = Video.with_encode_errors
|
246
256
|
@unencoded_videos = Video.unencoded
|
257
|
+
@downloading_videos = Video.downloading
|
258
|
+
@waiting_for_download_videos = Video.waiting_for_download
|
247
259
|
erb :'videos/index'
|
248
260
|
end
|
249
261
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class DownloadQueue
|
2
|
+
|
3
|
+
# Adds a periodic timer to the Eventmachine reactor loop and immediately
|
4
|
+
# starts looking for videos to download.
|
5
|
+
#
|
6
|
+
def initialize
|
7
|
+
if EnigmaMachine.enable_http_downloads
|
8
|
+
EM.add_periodic_timer(5) do
|
9
|
+
download_next_video
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Gets the next waiting_for_download Video from the database and
|
16
|
+
# starts downloading it.
|
17
|
+
#
|
18
|
+
def download_next_video
|
19
|
+
if Video.waiting_for_download.count > 0 && Video.downloading.count == 0
|
20
|
+
video = Video.waiting_for_download.first
|
21
|
+
begin
|
22
|
+
puts "downloading video #{video.id}"
|
23
|
+
video.download!
|
24
|
+
rescue Exception => ex
|
25
|
+
# don't do anything just yet, until we set up logging properly.
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -6,20 +6,19 @@ class EncodingQueue
|
|
6
6
|
# starts looking for unencoded videos.
|
7
7
|
#
|
8
8
|
def initialize
|
9
|
-
@threads = YAML.load_file(Dir.getwd + '/config.yml')['threads'] if @threads.nil?
|
10
9
|
EM.add_periodic_timer(5) {
|
11
10
|
encode_next_video
|
12
11
|
}
|
13
12
|
end
|
14
13
|
|
15
14
|
|
16
|
-
# Gets the next unencoded Video from the database and starts encoding
|
15
|
+
# Gets the next unencoded Video from the database and starts encoding its file.
|
17
16
|
#
|
18
17
|
def encode_next_video
|
19
|
-
if Video.unencoded.count > 0 &&
|
18
|
+
if Video.unencoded.count > 0 && Video.encoding.count < EnigmaMachine.threads
|
20
19
|
video = Video.unencoded.first
|
21
20
|
begin
|
22
|
-
video.
|
21
|
+
video.encode!
|
23
22
|
rescue Exception => ex
|
24
23
|
# don't do anything just yet, until we set up logging properly.
|
25
24
|
end
|
@@ -19,39 +19,5 @@ class Encoder
|
|
19
19
|
#
|
20
20
|
has n, :encoding_tasks
|
21
21
|
|
22
|
-
# Kicks off an FFMpeg encode on a given video.
|
23
|
-
#
|
24
|
-
def encode(video)
|
25
|
-
ffmpeg(encoding_tasks.first, video)
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
# Shells out to ffmpeg and hits the given video with the parameters in the
|
31
|
-
# given task. Will call itself recursively until all tasks in this encoder's
|
32
|
-
# encoding_tasks are completed.
|
33
|
-
#
|
34
|
-
def ffmpeg(task, video)
|
35
|
-
current_task_index = encoding_tasks.index(task)
|
36
|
-
movie = FFMPEG::Movie.new(video.file)
|
37
|
-
encoding_operation = proc {
|
38
|
-
video.update(:state => 'encoding')
|
39
|
-
movie.transcode(video.file + task.output_file_suffix, task.command) do |p|
|
40
|
-
puts p*100
|
41
|
-
end
|
42
|
-
}
|
43
|
-
completion_callback = proc {|result|
|
44
|
-
if task == encoding_tasks.last
|
45
|
-
video.update(:state => 'complete')
|
46
|
-
video.notify_complete
|
47
|
-
else
|
48
|
-
next_task_index = current_task_index + 1
|
49
|
-
next_task = encoding_tasks[next_task_index]
|
50
|
-
ffmpeg(next_task, video)
|
51
|
-
end
|
52
|
-
}
|
53
|
-
EventMachine.defer(encoding_operation, completion_callback)
|
54
|
-
end
|
55
|
-
|
56
22
|
end
|
57
23
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/encoder'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
2
4
|
|
3
5
|
# A video which we want to encode.
|
4
6
|
#
|
@@ -9,32 +11,74 @@ class Video
|
|
9
11
|
#
|
10
12
|
property :id, Serial
|
11
13
|
property :file, String, :required => true, :length => (1..510)
|
12
|
-
property :state, String, :required => true,
|
13
|
-
:length => (1..10), :default => 'unencoded'
|
14
14
|
property :created_at, DateTime
|
15
15
|
property :updated_at, DateTime
|
16
16
|
property :encoder_id, Integer, :required => true
|
17
17
|
property :callback_url, String
|
18
|
+
property :state, String
|
18
19
|
|
19
|
-
|
20
|
+
# State machine transitions
|
21
|
+
#
|
22
|
+
state_machine do
|
23
|
+
|
24
|
+
# State transitions for HTTP-hosted videos
|
25
|
+
after_transition :on => :download, :do => :do_download
|
26
|
+
|
27
|
+
# States for videos on the local filesystem
|
28
|
+
after_transition :on => :encode, :do => :do_encode
|
29
|
+
after_transition :on => :complete, :do => :notify_complete
|
30
|
+
|
31
|
+
event :download do
|
32
|
+
transition :waiting_for_download => :downloading
|
33
|
+
end
|
34
|
+
|
35
|
+
event :download_complete do
|
36
|
+
transition :downloading => :unencoded
|
37
|
+
end
|
38
|
+
|
39
|
+
event :download_error do
|
40
|
+
transition :downloading => :download_error
|
41
|
+
end
|
42
|
+
|
43
|
+
event :reset_download do
|
44
|
+
transition :downloading => :waiting_for_download
|
45
|
+
end
|
46
|
+
|
47
|
+
event :encode do
|
48
|
+
transition :unencoded => :encoding
|
49
|
+
end
|
50
|
+
|
51
|
+
event :complete do
|
52
|
+
transition :encoding => :complete
|
53
|
+
end
|
54
|
+
|
55
|
+
event :reset do
|
56
|
+
transition :encoding => :unencoded
|
57
|
+
end
|
58
|
+
|
59
|
+
event :encode_error do
|
60
|
+
transition :encoding => :encode_error
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# Validations
|
66
|
+
#
|
20
67
|
validates_uniqueness_of :file, :scope => :encoder_id,
|
21
68
|
:message => "Same file with same encoder already exists"
|
69
|
+
validates_with_method :file, :method => :check_file
|
70
|
+
|
71
|
+
# Associations
|
72
|
+
#
|
22
73
|
belongs_to :encoder
|
23
74
|
|
75
|
+
# Filters
|
76
|
+
#
|
24
77
|
before :destroy, :check_destroy
|
78
|
+
before :create, :set_initial_state
|
25
79
|
|
26
80
|
default_scope(:default).update(:order => [:created_at.asc])
|
27
81
|
|
28
|
-
# Notifies a calling application that processing has completed by sending
|
29
|
-
# a GET request to the video's callback_url.
|
30
|
-
#
|
31
|
-
def notify_complete
|
32
|
-
begin
|
33
|
-
Net::HTTP.get(URI.parse(video.callback_url)) unless callback_url.nil?
|
34
|
-
rescue
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
82
|
|
39
83
|
# Named scope for all videos which are waiting to start encoding.
|
40
84
|
#
|
@@ -51,8 +95,8 @@ class Video
|
|
51
95
|
|
52
96
|
# Named scope giving back all videos with encoding errors.
|
53
97
|
#
|
54
|
-
def self.
|
55
|
-
all(:state => '
|
98
|
+
def self.with_encode_errors
|
99
|
+
all(:state => 'encode_error')
|
56
100
|
end
|
57
101
|
|
58
102
|
# Named scope giving back all videos which have completed encoding.
|
@@ -61,6 +105,18 @@ class Video
|
|
61
105
|
all(:state => 'complete', :order => [:updated_at.desc])
|
62
106
|
end
|
63
107
|
|
108
|
+
# Named scope returning all videos which are not yet downloaded.
|
109
|
+
#
|
110
|
+
def self.waiting_for_download
|
111
|
+
all(:state => 'waiting_for_download')
|
112
|
+
end
|
113
|
+
|
114
|
+
# Named scope returning all videos which currently downloading.
|
115
|
+
#
|
116
|
+
def self.downloading
|
117
|
+
all(:state => 'downloading')
|
118
|
+
end
|
119
|
+
|
64
120
|
# Resets all videos currently marked as "encoding" to state "unencoded"
|
65
121
|
# which is the initial state.
|
66
122
|
#
|
@@ -70,34 +126,138 @@ class Video
|
|
70
126
|
#
|
71
127
|
def self.reset_encoding_videos
|
72
128
|
Video.encoding.each do |video|
|
73
|
-
video.
|
74
|
-
|
129
|
+
video.reset!
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.reset_downloading_videos
|
134
|
+
Video.downloading.each do |video|
|
135
|
+
puts "resetting video #{video.id}"
|
136
|
+
video.reset_download!
|
75
137
|
end
|
76
138
|
end
|
77
139
|
|
78
140
|
private
|
79
141
|
|
142
|
+
# Validation checks for files - we want to ensure that the video file exists,
|
143
|
+
# and that it can be encoded by ffmpeg.
|
144
|
+
#
|
80
145
|
def check_file
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
146
|
+
if local?
|
147
|
+
return [false, "Give a file name, not nil"] if self.file.nil?
|
148
|
+
return [false, "Give a file name, not a blank string"] if self.file.to_s.empty?
|
149
|
+
return [false, "#{self.file} does not exist"] unless File.exist? self.file
|
150
|
+
return [false, "#{self.file} is a directory"] if File.directory? self.file
|
151
|
+
movie = FFMPEG::Movie.new(self.file)
|
152
|
+
return [false, "#{self.file} is not a media file"] unless movie.valid?
|
153
|
+
end
|
87
154
|
return true
|
88
155
|
end
|
89
156
|
|
157
|
+
# Returns true unless the video's file starts with 'http://'
|
158
|
+
#
|
159
|
+
def set_initial_state
|
160
|
+
if local?
|
161
|
+
self.state = "unencoded"
|
162
|
+
else
|
163
|
+
self.state = "waiting_for_download"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Checks whether a video object can be destroyed - videos cannot be destroyed
|
168
|
+
# if they are currently encoding.
|
169
|
+
#
|
90
170
|
def check_destroy
|
91
171
|
return true if (self.state != 'encoding')
|
92
|
-
encoder = Encoder.get(self.encoder_id)
|
93
172
|
return true if stop_encode
|
94
173
|
throw :halt
|
95
174
|
end
|
96
175
|
|
176
|
+
# Would stop the encoder process if it was implemented. Currently does nothing
|
177
|
+
# and returns false.
|
178
|
+
#
|
97
179
|
def stop_encode
|
98
180
|
return false
|
99
181
|
#TODO Kill the encoder process
|
100
182
|
end
|
101
183
|
|
184
|
+
# Tells this video's encoder to start encoding tasks.
|
185
|
+
#
|
186
|
+
def do_encode
|
187
|
+
ffmpeg(encoder.encoding_tasks.first)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Shells out to ffmpeg and hits the given video with the parameters in the
|
191
|
+
# given task. Will call itself recursively until all tasks in the encoder's
|
192
|
+
# encoding_tasks are completed.
|
193
|
+
#
|
194
|
+
def ffmpeg(task)
|
195
|
+
current_task_index = encoder.encoding_tasks.index(task)
|
196
|
+
movie = FFMPEG::Movie.new(file_to_encode)
|
197
|
+
encoding_operation = proc do
|
198
|
+
movie.transcode(file_to_encode + task.output_file_suffix, task.command)
|
199
|
+
end
|
200
|
+
|
201
|
+
completion_callback = proc do |result|
|
202
|
+
if task == encoder.encoding_tasks.last
|
203
|
+
self.complete!
|
204
|
+
else
|
205
|
+
next_task_index = current_task_index + 1
|
206
|
+
next_task = encoder.encoding_tasks[next_task_index]
|
207
|
+
ffmpeg(next_task)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
EventMachine.defer(encoding_operation, completion_callback)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Notifies a calling application that processing has completed by sending
|
215
|
+
# a GET request to the video's callback_url.
|
216
|
+
#
|
217
|
+
def notify_complete
|
218
|
+
EventMachine::HttpRequest.new(callback_url).get :timeout => 10 unless callback_url.nil?
|
219
|
+
end
|
220
|
+
|
221
|
+
# Downloads a video from a remote location via HTTP
|
222
|
+
#
|
223
|
+
def do_download
|
224
|
+
FileUtils.rm(file_to_encode, :force => true)
|
225
|
+
FileUtils.mkdir_p(File.dirname(file_to_encode))
|
226
|
+
http = EventMachine::HttpRequest.new(file).get :timeout => 10
|
227
|
+
|
228
|
+
http.stream do |data|
|
229
|
+
puts "what is status?: " + http.response_header.status.to_s
|
230
|
+
File.open(file_to_encode, 'a') {|f| f.write(data) }
|
231
|
+
end
|
232
|
+
|
233
|
+
http.callback do
|
234
|
+
download_complete!
|
235
|
+
end
|
236
|
+
|
237
|
+
http.errback do
|
238
|
+
download_error!
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Returns false if the video is available via http
|
243
|
+
#
|
244
|
+
def local?
|
245
|
+
return false if self.file =~ /^http:\/\//
|
246
|
+
return true
|
247
|
+
end
|
248
|
+
|
249
|
+
# If the file is local, this just returns its location. If it's not local,
|
250
|
+
# it builds a path to the file based on your config.yml + the video's id and
|
251
|
+
# returns that.
|
252
|
+
#
|
253
|
+
def file_to_encode
|
254
|
+
if local?
|
255
|
+
return file
|
256
|
+
else
|
257
|
+
filename = File.basename(URI.parse(file).path)
|
258
|
+
return File.join(EnigmaMachine.download_storage_path, self.id.to_s, filename)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
102
262
|
end
|
103
263
|
|
@@ -34,6 +34,20 @@
|
|
34
34
|
</ul>
|
35
35
|
<% end %>
|
36
36
|
|
37
|
+
<% unless @downloading_videos.empty? %>
|
38
|
+
<h2>Failed to encode</h2>
|
39
|
+
<ul>
|
40
|
+
<%= partial :"videos/video", :collection => @downloading_videos %>
|
41
|
+
</ul>
|
42
|
+
<% end %>
|
43
|
+
|
44
|
+
<% unless @waiting_for_download_videos.empty? %>
|
45
|
+
<h2>Failed to encode</h2>
|
46
|
+
<ul>
|
47
|
+
<%= partial :"videos/video", :collection => @waiting_for_download_videos %>
|
48
|
+
</ul>
|
49
|
+
<% end %>
|
50
|
+
|
37
51
|
</div>
|
38
52
|
</div>
|
39
53
|
|
data/lib/generators/config.yml
CHANGED
data/test/helper.rb
CHANGED
data/test/support/blueprints.rb
CHANGED
@@ -2,22 +2,11 @@ require 'machinist/data_mapper'
|
|
2
2
|
require 'faker'
|
3
3
|
require 'sham'
|
4
4
|
|
5
|
+
|
5
6
|
Encoder.blueprint do
|
6
7
|
name { Faker::Name.name }
|
7
8
|
end
|
8
9
|
|
9
|
-
Video.blueprint do
|
10
|
-
encoder
|
11
|
-
file { File.dirname(__FILE__) + "/afile.mpg" }
|
12
|
-
state { "unencoded" }
|
13
|
-
created_at DateTime.now
|
14
|
-
updated_at DateTime.now
|
15
|
-
end
|
16
|
-
|
17
|
-
Video.blueprint(:with_callback) do
|
18
|
-
callback_url { "http://example.com/call/back/id" }
|
19
|
-
end
|
20
|
-
|
21
10
|
EncodingTask.blueprint do
|
22
11
|
name { "320x240 flv"}
|
23
12
|
output_file_suffix { ".foo.flv" }
|
@@ -31,3 +20,19 @@ EncodingTask.blueprint(:with_encoder) do
|
|
31
20
|
encoder
|
32
21
|
end
|
33
22
|
|
23
|
+
Video.blueprint do
|
24
|
+
encoder
|
25
|
+
file { File.dirname(__FILE__) + "/afile.mpg" }
|
26
|
+
state { "unencoded" }
|
27
|
+
created_at DateTime.now
|
28
|
+
updated_at DateTime.now
|
29
|
+
end
|
30
|
+
|
31
|
+
Video.blueprint(:http) do
|
32
|
+
file { http_file_location }
|
33
|
+
end
|
34
|
+
|
35
|
+
Video.blueprint(:with_callback) do
|
36
|
+
callback_url { "http://example.com/call/back/id" }
|
37
|
+
end
|
38
|
+
|
data/test/test_encoder.rb
CHANGED
@@ -5,7 +5,7 @@ class TestEncoder < Test::Unit::TestCase
|
|
5
5
|
context "An Encoder instance" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@encoder =
|
8
|
+
@encoder = Encoder.make
|
9
9
|
end
|
10
10
|
|
11
11
|
should "be invalid without a name" do
|
@@ -28,18 +28,19 @@ class TestEncoder < Test::Unit::TestCase
|
|
28
28
|
context "hitting the ffmpeg method" do
|
29
29
|
context "for an encoder with 1 task" do
|
30
30
|
setup do
|
31
|
-
video = Video.make
|
31
|
+
@video = Video.make
|
32
32
|
task = EncodingTask.make
|
33
|
-
video.encoder.encoding_tasks << task
|
34
|
-
video.encoder.save
|
35
|
-
video.
|
36
|
-
@id = video.id
|
33
|
+
@video.encoder.encoding_tasks << task
|
34
|
+
@video.encoder.save
|
35
|
+
@video.encode!
|
36
|
+
@id = @video.id
|
37
37
|
sleep 1
|
38
38
|
end
|
39
39
|
|
40
|
-
should "
|
41
|
-
|
42
|
-
|
40
|
+
should "create the file specified in the encoder's first encoding task" do
|
41
|
+
suffix = @video.encoder.encoding_tasks.first.output_file_suffix
|
42
|
+
file = @video.file + suffix
|
43
|
+
assert File.exist? file
|
43
44
|
end
|
44
45
|
|
45
46
|
should_eventually "mark the video as complete" do
|
@@ -56,22 +57,15 @@ class TestEncoder < Test::Unit::TestCase
|
|
56
57
|
task = EncodingTask.make
|
57
58
|
2.times { video.encoder.encoding_tasks << EncodingTask.make }
|
58
59
|
video.encoder.save
|
59
|
-
video.
|
60
|
+
video.encode!
|
60
61
|
@id = video.id
|
61
62
|
end
|
62
63
|
|
63
|
-
should "mark the video as encoding" do
|
64
|
-
sleep 1
|
65
|
-
video = Video.get(@id)
|
66
|
-
assert_equal "encoding", video.state
|
67
|
-
end
|
68
|
-
|
69
64
|
should_eventually "mark the video as complete" do
|
70
65
|
sleep 1
|
71
66
|
video = Video.get(@id)
|
72
67
|
assert_equal "complete", video.state
|
73
68
|
end
|
74
|
-
|
75
69
|
end
|
76
70
|
|
77
71
|
end
|
data/test/test_enigmamachine.rb
CHANGED
data/test/test_video.rb
CHANGED
@@ -6,36 +6,36 @@ class TestVideo < Test::Unit::TestCase
|
|
6
6
|
context "A Video instance" do
|
7
7
|
|
8
8
|
should "be invalid with a bad file path" do
|
9
|
-
|
10
|
-
|
11
|
-
assert(!
|
12
|
-
|
13
|
-
assert(!
|
14
|
-
|
15
|
-
assert(!
|
16
|
-
|
17
|
-
assert(!
|
18
|
-
|
19
|
-
assert(!
|
9
|
+
video = Video.make
|
10
|
+
video.file = ""
|
11
|
+
assert(!video.valid?, "must not be empty")
|
12
|
+
video.file = nil
|
13
|
+
assert(!video.valid?, "must not be nil")
|
14
|
+
video.file = "/fdfdf/sfdsdfsd/fse.gfr"
|
15
|
+
assert(!video.valid?, "must exist")
|
16
|
+
video.file = File.dirname(__FILE__)
|
17
|
+
assert(!video.valid?, "must not be a directory")
|
18
|
+
video.file = __FILE__
|
19
|
+
assert(!video.valid?, "must be media file")
|
20
20
|
end
|
21
21
|
|
22
22
|
should "be valid without a callback_url" do
|
23
|
-
|
24
|
-
|
25
|
-
assert
|
26
|
-
|
27
|
-
assert
|
23
|
+
video = Video.make
|
24
|
+
video.callback_url = ""
|
25
|
+
assert video.valid?
|
26
|
+
video.callback_url = nil
|
27
|
+
assert video.valid?
|
28
28
|
end
|
29
29
|
|
30
30
|
should "be valid with a callback_url" do
|
31
|
-
|
32
|
-
|
33
|
-
assert
|
31
|
+
video = Video.make
|
32
|
+
video.callback_url = "blah"
|
33
|
+
assert video.valid?
|
34
34
|
end
|
35
35
|
|
36
36
|
should "be valid with a correct file path" do
|
37
|
-
|
38
|
-
assert
|
37
|
+
video = ::Video.make
|
38
|
+
assert video.valid?
|
39
39
|
end
|
40
40
|
|
41
41
|
should "belong to an Encoder" do
|
@@ -51,6 +51,60 @@ class TestVideo < Test::Unit::TestCase
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
context "on a local filesystem" do
|
55
|
+
setup do
|
56
|
+
@video = Video.make
|
57
|
+
end
|
58
|
+
|
59
|
+
should "have an initial state of 'unencoded'" do
|
60
|
+
assert_equal("unencoded", @video.state)
|
61
|
+
end
|
62
|
+
|
63
|
+
should "transition to state 'encoding' on 'encode!' command" do
|
64
|
+
@video.encode!
|
65
|
+
assert_equal("encoding", @video.state)
|
66
|
+
end
|
67
|
+
|
68
|
+
should "transition to state 'unencoded' on 'reset!' command" do
|
69
|
+
@video.encode!
|
70
|
+
@video.reset!
|
71
|
+
assert_equal("unencoded", @video.state)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "available via http" do
|
77
|
+
setup do
|
78
|
+
@video = Video.make(:http)
|
79
|
+
end
|
80
|
+
|
81
|
+
should "have an initial state of 'waiting_for_download'" do
|
82
|
+
assert_equal("waiting_for_download", @video.state)
|
83
|
+
end
|
84
|
+
|
85
|
+
context "for the download! event" do
|
86
|
+
setup do
|
87
|
+
EventMachine.run do
|
88
|
+
EventMachine::MockHttpRequest.use {
|
89
|
+
EventMachine::HttpRequest.register_file(http_file_location, :get, '/home/dave/workspace/enigmamachine/test/support/afile.mpg')
|
90
|
+
}
|
91
|
+
@video.download!
|
92
|
+
EventMachine.stop
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
should_eventually "transition to state 'downloading'" do
|
97
|
+
assert_equal("downloading", @video.state)
|
98
|
+
end
|
99
|
+
|
100
|
+
should_eventually "hit the download URL once" do
|
101
|
+
EventMachine::MockHttpRequest.activate!
|
102
|
+
assert_equal(1, EventMachine::HttpRequest.count(http_file_location, :get))
|
103
|
+
EventMachine::MockHttpRequest.deactivate!
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
54
108
|
end
|
55
109
|
|
56
110
|
context "The Video class" do
|
@@ -81,40 +135,40 @@ class TestVideo < Test::Unit::TestCase
|
|
81
135
|
end
|
82
136
|
end
|
83
137
|
|
84
|
-
context "
|
138
|
+
context "deleting videos" do
|
85
139
|
setup do
|
86
140
|
clear_videos
|
87
141
|
5.times { Video.make }
|
88
142
|
end
|
89
143
|
|
90
|
-
should "
|
144
|
+
should "delete an unencoded video" do
|
91
145
|
count = Video.unencoded.count
|
92
146
|
2.times { Video.unencoded.first.destroy }
|
93
147
|
assert_equal count - 2, Video.unencoded.count
|
94
148
|
end
|
95
149
|
|
96
|
-
should "
|
150
|
+
should "delete a completed video" do
|
97
151
|
3.times { Video.unencoded.first.update(:state => "complete") }
|
98
152
|
count = Video.complete.count
|
99
153
|
2.times { Video.complete.first.destroy }
|
100
154
|
assert_equal count - 2, Video.complete.count
|
101
155
|
end
|
102
156
|
|
103
|
-
should "
|
104
|
-
3.times { Video.unencoded.first.update(:state => "
|
105
|
-
count = Video.
|
106
|
-
2.times { Video.
|
107
|
-
assert_equal count - 2, Video.
|
157
|
+
should "delete videos with errors" do
|
158
|
+
3.times { Video.unencoded.first.update(:state => "encode_error") }
|
159
|
+
count = Video.with_encode_errors.count
|
160
|
+
2.times { Video.with_encode_errors.first.destroy }
|
161
|
+
assert_equal count - 2, Video.with_encode_errors.count
|
108
162
|
end
|
109
163
|
|
110
|
-
should "not
|
164
|
+
should "not delete an encoding video" do
|
111
165
|
3.times { Video.unencoded.first.update(:state => "encoding") }
|
112
166
|
count = Video.encoding.count
|
113
167
|
2.times { Video.encoding.first.destroy }
|
114
168
|
assert_equal count, Video.encoding.count
|
115
169
|
end
|
116
170
|
|
117
|
-
should "
|
171
|
+
should "allow force destroy of an encoding video" do
|
118
172
|
3.times { Video.unencoded.first.update(:state => "encoding") }
|
119
173
|
count = Video.encoding.count
|
120
174
|
2.times { Video.encoding.first.destroy! }
|
@@ -128,13 +182,21 @@ class TestVideo < Test::Unit::TestCase
|
|
128
182
|
end
|
129
183
|
|
130
184
|
should "be able to grab all videos with errors" do
|
131
|
-
assert Video.respond_to? "
|
185
|
+
assert Video.respond_to? "with_encode_errors"
|
132
186
|
end
|
133
187
|
|
134
188
|
should "be able to grab all videos that are encoding" do
|
135
189
|
assert Video.respond_to? "encoding"
|
136
190
|
end
|
137
191
|
|
192
|
+
should "be able to grab all videos that are not yet downloaded" do
|
193
|
+
assert Video.respond_to? "waiting_for_download"
|
194
|
+
end
|
195
|
+
|
196
|
+
should "be able to grab all videos that are downloading" do
|
197
|
+
assert Video.respond_to? "downloading"
|
198
|
+
end
|
199
|
+
|
138
200
|
end
|
139
201
|
|
140
202
|
def clear_videos
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enigmamachine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
+
- 6
|
8
9
|
- 0
|
9
|
-
version: 0.
|
10
|
+
version: 0.6.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Dave Hrycyszyn
|
@@ -15,127 +16,189 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-
|
19
|
+
date: 2010-11-01 00:00:00 +00:00
|
19
20
|
default_executable: enigmamachine
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
22
23
|
name: thoughtbot-shoulda
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ">="
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
28
31
|
segments:
|
29
32
|
- 0
|
30
33
|
version: "0"
|
31
34
|
type: :development
|
32
35
|
version_requirements: *id001
|
33
36
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
37
|
+
name: rack-test
|
35
38
|
prerelease: false
|
36
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: data_mapper
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
37
55
|
requirements:
|
38
56
|
- - ">="
|
39
57
|
- !ruby/object:Gem::Version
|
58
|
+
hash: 19
|
40
59
|
segments:
|
41
60
|
- 1
|
42
61
|
- 0
|
43
62
|
- 2
|
44
63
|
version: 1.0.2
|
45
64
|
type: :runtime
|
46
|
-
version_requirements: *
|
65
|
+
version_requirements: *id003
|
47
66
|
- !ruby/object:Gem::Dependency
|
48
67
|
name: dm-sqlite-adapter
|
49
68
|
prerelease: false
|
50
|
-
requirement: &
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
51
71
|
requirements:
|
52
72
|
- - ">="
|
53
73
|
- !ruby/object:Gem::Version
|
74
|
+
hash: 19
|
54
75
|
segments:
|
55
76
|
- 1
|
56
77
|
- 0
|
57
78
|
- 2
|
58
79
|
version: 1.0.2
|
59
80
|
type: :runtime
|
60
|
-
version_requirements: *
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: state_machine
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 51
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
- 9
|
94
|
+
- 4
|
95
|
+
version: 0.9.4
|
96
|
+
type: :runtime
|
97
|
+
version_requirements: *id005
|
61
98
|
- !ruby/object:Gem::Dependency
|
62
99
|
name: eventmachine
|
63
100
|
prerelease: false
|
64
|
-
requirement: &
|
101
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
65
103
|
requirements:
|
66
104
|
- - ">="
|
67
105
|
- !ruby/object:Gem::Version
|
106
|
+
hash: 59
|
68
107
|
segments:
|
69
108
|
- 0
|
70
109
|
- 12
|
71
110
|
- 10
|
72
111
|
version: 0.12.10
|
73
112
|
type: :runtime
|
74
|
-
version_requirements: *
|
113
|
+
version_requirements: *id006
|
75
114
|
- !ruby/object:Gem::Dependency
|
76
115
|
name: rack-flash
|
77
116
|
prerelease: false
|
78
|
-
requirement: &
|
117
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
79
119
|
requirements:
|
80
120
|
- - ">="
|
81
121
|
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
82
123
|
segments:
|
83
124
|
- 0
|
84
125
|
version: "0"
|
85
126
|
type: :runtime
|
86
|
-
version_requirements: *
|
127
|
+
version_requirements: *id007
|
87
128
|
- !ruby/object:Gem::Dependency
|
88
129
|
name: ruby-debug
|
89
130
|
prerelease: false
|
90
|
-
requirement: &
|
131
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
91
133
|
requirements:
|
92
134
|
- - ">="
|
93
135
|
- !ruby/object:Gem::Version
|
136
|
+
hash: 3
|
94
137
|
segments:
|
95
138
|
- 0
|
96
139
|
version: "0"
|
97
140
|
type: :runtime
|
98
|
-
version_requirements: *
|
141
|
+
version_requirements: *id008
|
99
142
|
- !ruby/object:Gem::Dependency
|
100
143
|
name: sinatra
|
101
144
|
prerelease: false
|
102
|
-
requirement: &
|
145
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
103
147
|
requirements:
|
104
148
|
- - ">="
|
105
149
|
- !ruby/object:Gem::Version
|
150
|
+
hash: 23
|
106
151
|
segments:
|
107
152
|
- 1
|
108
153
|
- 0
|
109
154
|
- 0
|
110
155
|
version: 1.0.0
|
111
156
|
type: :runtime
|
112
|
-
version_requirements: *
|
157
|
+
version_requirements: *id009
|
113
158
|
- !ruby/object:Gem::Dependency
|
114
159
|
name: streamio-ffmpeg
|
115
160
|
prerelease: false
|
116
|
-
requirement: &
|
161
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
117
163
|
requirements:
|
118
164
|
- - ">="
|
119
165
|
- !ruby/object:Gem::Version
|
166
|
+
hash: 5
|
120
167
|
segments:
|
121
168
|
- 0
|
122
169
|
- 7
|
123
170
|
- 3
|
124
171
|
version: 0.7.3
|
125
172
|
type: :runtime
|
126
|
-
version_requirements: *
|
173
|
+
version_requirements: *id010
|
127
174
|
- !ruby/object:Gem::Dependency
|
128
175
|
name: thin
|
129
176
|
prerelease: false
|
130
|
-
requirement: &
|
177
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
131
179
|
requirements:
|
132
180
|
- - ">="
|
133
181
|
- !ruby/object:Gem::Version
|
182
|
+
hash: 3
|
134
183
|
segments:
|
135
184
|
- 0
|
136
185
|
version: "0"
|
137
186
|
type: :runtime
|
138
|
-
version_requirements: *
|
187
|
+
version_requirements: *id011
|
188
|
+
- !ruby/object:Gem::Dependency
|
189
|
+
name: em-http-request
|
190
|
+
prerelease: false
|
191
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
192
|
+
none: false
|
193
|
+
requirements:
|
194
|
+
- - ">="
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
hash: 3
|
197
|
+
segments:
|
198
|
+
- 0
|
199
|
+
version: "0"
|
200
|
+
type: :runtime
|
201
|
+
version_requirements: *id012
|
139
202
|
description: A RESTful video encoder which you can use as either a front-end to ffmpeg or headless on a server.
|
140
203
|
email: dave@caprica
|
141
204
|
executables:
|
@@ -146,6 +209,7 @@ extra_rdoc_files:
|
|
146
209
|
- LICENSE
|
147
210
|
- README.rdoc
|
148
211
|
files:
|
212
|
+
- .autotest
|
149
213
|
- .document
|
150
214
|
- .gitignore
|
151
215
|
- LICENSE
|
@@ -157,6 +221,7 @@ files:
|
|
157
221
|
- lib/enigmamachine.rb
|
158
222
|
- lib/enigmamachine.sqlite3
|
159
223
|
- lib/enigmamachine/config.ru
|
224
|
+
- lib/enigmamachine/download_queue.rb
|
160
225
|
- lib/enigmamachine/encoding_queue.rb
|
161
226
|
- lib/enigmamachine/models/encoder.rb
|
162
227
|
- lib/enigmamachine/models/encoding_task.rb
|
@@ -209,30 +274,34 @@ rdoc_options:
|
|
209
274
|
require_paths:
|
210
275
|
- lib
|
211
276
|
required_ruby_version: !ruby/object:Gem::Requirement
|
277
|
+
none: false
|
212
278
|
requirements:
|
213
279
|
- - ">="
|
214
280
|
- !ruby/object:Gem::Version
|
281
|
+
hash: 3
|
215
282
|
segments:
|
216
283
|
- 0
|
217
284
|
version: "0"
|
218
285
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
286
|
+
none: false
|
219
287
|
requirements:
|
220
288
|
- - ">="
|
221
289
|
- !ruby/object:Gem::Version
|
290
|
+
hash: 3
|
222
291
|
segments:
|
223
292
|
- 0
|
224
293
|
version: "0"
|
225
294
|
requirements: []
|
226
295
|
|
227
296
|
rubyforge_project:
|
228
|
-
rubygems_version: 1.3.
|
297
|
+
rubygems_version: 1.3.7
|
229
298
|
signing_key:
|
230
299
|
specification_version: 3
|
231
300
|
summary: A RESTful video encoder.
|
232
301
|
test_files:
|
233
|
-
- test/support/blueprints.rb
|
234
|
-
- test/helper.rb
|
235
302
|
- test/test_encoding_queue.rb
|
236
|
-
- test/test_video.rb
|
237
303
|
- test/test_encoder.rb
|
304
|
+
- test/test_video.rb
|
238
305
|
- test/test_enigmamachine.rb
|
306
|
+
- test/helper.rb
|
307
|
+
- test/support/blueprints.rb
|