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