zencodable 0.0.1
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/MIT-LICENSE +20 -0
- data/README.md +116 -0
- data/Rakefile +37 -0
- data/lib/generators/zencodable/migration_generator.rb +36 -0
- data/lib/generators/zencodable/templates/create_association_table.rb +17 -0
- data/lib/generators/zencodable/templates/create_association_thumbnails_table.rb +12 -0
- data/lib/zencodable/version.rb +3 -0
- data/lib/zencodable.rb +239 -0
- data/test/debug.log +3394 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/video.rb +10 -0
- data/test/dummy/app/models/video_file.rb +3 -0
- data/test/dummy/app/models/video_file_thumbnail.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/amazon_s3.yml +4 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +30 -0
- data/test/dummy/config/environments/production.rb +60 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20111130180309_create_dummy_videos.rb +13 -0
- data/test/dummy/db/migrate/20111130192331_create_video_files.rb +18 -0
- data/test/dummy/db/migrate/20111130193231_create_video_thumbnails.rb +12 -0
- data/test/dummy/db/migrate/20111201135457_rename_video_thumbnails.rb +5 -0
- data/test/dummy/db/schema.rb +52 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +22 -0
- data/test/dummy/log/test.log +42 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/db/migrate/20111202054112_create_encoded_kitteh_vids.rb +17 -0
- data/test/factories.rb +17 -0
- data/test/test_helper.rb +17 -0
- data/test/zencodable_generators_test.rb +28 -0
- data/test/zencodable_test.rb +64 -0
- metadata +226 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2011 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Zencodable
|
2
|
+
|
3
|
+
Gives you `has_video_encodings` method for your models, that sets up jobs to encode multiple video container/codecs using [Zencoder](http://zencoder.com). It tells Zencoder to place the output files in some bucket in your S3 account. From there, they are yours to enjoy forever.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Video < ActiveRecord::Base
|
7
|
+
|
8
|
+
has_video_encodings :video_files, :formats => [:ogg, :mp4, :webm, :flv],
|
9
|
+
:output_dimensions => '852x480',
|
10
|
+
:s3_config => "#{Rails.root}/config/amazon_s3.yml",
|
11
|
+
:path => "videos/zc/:basename/",
|
12
|
+
:thumbnails => { :number => 2, :aspect_mode => 'crop', 'size' => '290x160' },
|
13
|
+
:options => { :device_profile => 'mobile/advanced' }
|
14
|
+
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
## Requirements
|
19
|
+
|
20
|
+
_developed on ruby 1.9.2-p290 and Rails 3.1.3_
|
21
|
+
|
22
|
+
1. A [Zencoder][1] account of course, testing or full.
|
23
|
+
|
24
|
+
2. A working Amazon S3 account with a shiny new bucket ready to receive video files.
|
25
|
+
|
26
|
+
3. this gem (zencodable) in your gemfile, and typhoeus.
|
27
|
+
|
28
|
+
gem 'zencodable'
|
29
|
+
gem 'typhoeus'
|
30
|
+
|
31
|
+
# Setup
|
32
|
+
|
33
|
+
## Zencoder API keys
|
34
|
+
|
35
|
+
the zencoder/zencoder-rb gem expects access to your Zencoder API keys in some fashion. Also, we need to use dbalatero/typhoeus for HTTP stuffs, that's the only way Zencoder will work with S3. So, I like something in `config/initializers` like
|
36
|
+
|
37
|
+
# zencoder setup
|
38
|
+
if Rails.env == 'production'
|
39
|
+
Zencoder.api_key = 'therealdealkey00000000000000000'
|
40
|
+
else
|
41
|
+
Zencoder.api_key = 'keyfortestingonly00000000000000'
|
42
|
+
end
|
43
|
+
|
44
|
+
Zencoder::HTTP.http_backend = Zencoder::HTTP::Typhoeus
|
45
|
+
|
46
|
+
## Bucket policy
|
47
|
+
|
48
|
+
The bucket needs to have a custom policy to allow Zencoder to place the output videos on it. [Here is a guide on Zencoder's site, follow it](https://app.zencoder.com/docs/guides/getting-started/working-with-s3)
|
49
|
+
|
50
|
+
(There is currently a branch where an attempt to create a rake task to auto-install this policy was made. Unfortunately it seems the marcel/aws-s3 gem doesn't know how to update a bucket policy after all, it just manages the ACLs. It seems fog can't do that either. Oh well, you'll have to paste in the policy.)
|
51
|
+
|
52
|
+
## Run the generator
|
53
|
+
|
54
|
+
rails g zencodable:migrations <Model> <association_name>
|
55
|
+
|
56
|
+
e.g.,
|
57
|
+
|
58
|
+
rails g zencodable:migrations KittehVideo kitteh_video_files
|
59
|
+
|
60
|
+
This will actually create two `has_many` associations for your model - the `kitteh_video_files` for the output files themselves (one for each format), and the `kitteh_video_file_thumbnails` for the framegrab thumbnails that Zencoder can create (if configured).
|
61
|
+
|
62
|
+
you can add a `--skip-thumbnails` option if you don't want to use the auto-generated thumbnails.
|
63
|
+
|
64
|
+
now do a `rake db:migrate`
|
65
|
+
|
66
|
+
## How to use
|
67
|
+
|
68
|
+
### Configure model and encoding options
|
69
|
+
|
70
|
+
add something like the above `has_video_encodings` class method to your model (the generator does not try to do this for you).
|
71
|
+
|
72
|
+
The options should include a `:s3_config` key that gives a location of a YAML file containing your S3 credentials, which should contain a 'bucket' key (you already have one, right?) Actually, all we need from that is the bucket name, so you can instead use a `:bucket` key to give the name of the bucket where the output files should be placed.
|
73
|
+
|
74
|
+
The `:path` option can be any path within that bucket. It can contain a `:basename` token, which will be replaced with a sanitized, URL-encoded version of the original filename as uploaded.
|
75
|
+
|
76
|
+
`:formats` is a list of output formats you'd like. [Supported formats and codecs](https://app.zencoder.com/docs/api/encoding/format-and-codecs/format)
|
77
|
+
|
78
|
+
The other options are all those that can be handled by Zencoder. More info can be found on [:thumbnails](https://app.zencoder.com/docs/api/encoding/thumbnails), [:output_dimensions](https://app.zencoder.com/docs/api/encoding/resolution/size) and other output settings [:options](https://app.zencoder.com/docs/api/encoding)
|
79
|
+
|
80
|
+
### Give it a source URL
|
81
|
+
|
82
|
+
All that's needed to trigger the Zencoder job is to change the `origin_url` value of your model, and then save. That will be picked up, sent to Zencoder, and your job will be started with your desired settings.
|
83
|
+
|
84
|
+
As the job runs, you can check `Model.job_status` as you see fit, if the job is neither failed nor finished, it will request an update from Zencoder for that Job.
|
85
|
+
|
86
|
+
Individual files will complete at different times, so you can also check the `state` of each associated output file.
|
87
|
+
|
88
|
+
vid = Video.new :title => 'Hilarious Kitteh Antics!'
|
89
|
+
vid.origin_url = 'http://sourcebucket.s3.amazonaws.com/largevideos/funny_kittehs[HD].mov'
|
90
|
+
vid.save
|
91
|
+
vid.job_status # "new"
|
92
|
+
...
|
93
|
+
vid.job_status # "waiting"
|
94
|
+
...
|
95
|
+
vid.job_status # "processing"
|
96
|
+
vid.video_encoded_files.collect { |v| [v.format, v.state] }
|
97
|
+
...
|
98
|
+
vid.job_status # "finished"
|
99
|
+
vid.video_encoded_files.size # 4
|
100
|
+
vid.video_encoded_file_thumbnails.size # 2
|
101
|
+
|
102
|
+
|
103
|
+
## TODO
|
104
|
+
|
105
|
+
* change name of `origin_url` column and make sure its added in the migrations
|
106
|
+
* rake task to generate a working bucket policy (even if it has to be pasted in)
|
107
|
+
* background jobs to update the ZC job progress, with events/notifications
|
108
|
+
* is s3_url basename sanitization going to be good for non-ASCII filenames? no.
|
109
|
+
|
110
|
+
## License
|
111
|
+
|
112
|
+
Uses MIT-LICENSE. You are free to use this as you like, but don't expect anything.
|
113
|
+
|
114
|
+
Forking and pull requests would be much appreciated.
|
115
|
+
|
116
|
+
[1]:http://zencoder.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Zencodable'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task :default => :test
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
|
4
|
+
module Zencodable
|
5
|
+
module Generators
|
6
|
+
class Migration < ::Rails::Generators::NamedBase
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
desc "creates migrations to create tables for the models that hold the encoded video files and thumbnails"
|
10
|
+
|
11
|
+
argument :association_name, :type => :string, :default => 'video_files'
|
12
|
+
class_option :skip_thumbnails, :type => :boolean, :default => false
|
13
|
+
|
14
|
+
source_root File.expand_path("../templates", __FILE__)
|
15
|
+
|
16
|
+
def self.next_migration_number(dirname)
|
17
|
+
if ActiveRecord::Base.timestamped_migrations
|
18
|
+
migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
19
|
+
migration_number += 1
|
20
|
+
migration_number.to_s
|
21
|
+
else
|
22
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_migration_files
|
27
|
+
migration_template 'create_association_table.rb', "db/migrate/create_#{association_name}"
|
28
|
+
unless options.skip_thumbnails?
|
29
|
+
migration_template 'create_association_thumbnails_table.rb', "db/migrate/create_#{association_name}_thumbnails"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateZencodableOutputFilesAssociationTable < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table "<%= association_name %>" do |t|
|
4
|
+
t.string "url"
|
5
|
+
t.string "format"
|
6
|
+
t.integer "zencoder_file_id"
|
7
|
+
t.integer "<%= name.foreign_key %>"
|
8
|
+
t.datetime "created_at"
|
9
|
+
t.integer "width"
|
10
|
+
t.integer "height"
|
11
|
+
t.integer "file_size"
|
12
|
+
t.string "error_message"
|
13
|
+
t.string "state"
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateZencodableOutputFilesAssociationTable < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table "<%= association_name.singularize %>_thumbnails" do |t|
|
4
|
+
t.string "thumbnail_file_name"
|
5
|
+
t.string "thumbnail_content_type"
|
6
|
+
t.integer "thumbnail_file_size"
|
7
|
+
t.datetime "thumbnail_updated_at"
|
8
|
+
t.integer "<%= name.foreign_key %>"
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/zencodable.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'zencoder'
|
2
|
+
|
3
|
+
module Zencodable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :encoder_definitions
|
8
|
+
class_attribute :encoder_output_files_association
|
9
|
+
class_attribute :encoder_thumbnails_association
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def has_video_encodings target_association, options = {}
|
15
|
+
self.encoder_definitions = options
|
16
|
+
self.encoder_output_files_association = target_association
|
17
|
+
|
18
|
+
has_many self.encoder_output_files_association, :dependent => :destroy
|
19
|
+
|
20
|
+
unless options[:thumbnails].blank?
|
21
|
+
self.encoder_thumbnails_association = "#{target_association.to_s.singularize}_thumbnails".to_sym
|
22
|
+
has_many self.encoder_thumbnails_association, :dependent => :destroy
|
23
|
+
end
|
24
|
+
|
25
|
+
before_save :create_job
|
26
|
+
|
27
|
+
# TODO cleanup
|
28
|
+
#before_destroy :prepare_for_destroy
|
29
|
+
#after_destroy :destroy_attached_files
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
|
36
|
+
def job_status
|
37
|
+
unless ['finished','failed'].include? zencoder_job_status
|
38
|
+
logger.debug "Unfinished job found. Updating details."
|
39
|
+
update_job
|
40
|
+
end
|
41
|
+
self.zencoder_job_status
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_job
|
45
|
+
if self.origin_url_changed?
|
46
|
+
logger.debug "Origin URL changed. Creating new ZenCoder job."
|
47
|
+
if @job = Encoder::Job.create(origin_url, self.class.encoder_definitions)
|
48
|
+
logger.debug "ZenCoder job created, ID = #{@job.id}"
|
49
|
+
self.zencoder_job_id = @job.id
|
50
|
+
self.zencoder_job_status = 'new'
|
51
|
+
self.zencoder_job_created = Time.now
|
52
|
+
self.zencoder_job_finished = nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_job
|
58
|
+
self.zencoder_job_status = encoder_job.status
|
59
|
+
self.zencoder_job_finished = encoder_job.finished_at
|
60
|
+
self.video_files = encoder_job.files.collect{ |file| video_files_class.new(file) } rescue []
|
61
|
+
self.video_thumbnails = encoder_job.thumbnails.collect{ |file| video_thumbnails_class.new(file) } rescue []
|
62
|
+
save
|
63
|
+
end
|
64
|
+
|
65
|
+
def source_file_for(fmt)
|
66
|
+
self.video_files.where(:format => fmt).first
|
67
|
+
end
|
68
|
+
|
69
|
+
def video_files
|
70
|
+
self.send(video_files_method)
|
71
|
+
end
|
72
|
+
|
73
|
+
def video_thumbnails
|
74
|
+
self.send(video_files_thumbnails_method)
|
75
|
+
end
|
76
|
+
|
77
|
+
def video_files= *args
|
78
|
+
self.send "#{video_files_method}=", *args
|
79
|
+
end
|
80
|
+
|
81
|
+
def video_thumbnails= *args
|
82
|
+
self.send("#{video_files_thumbnails_method}=", *args) if self.respond_to?(video_files_thumbnails_method)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
private
|
87
|
+
def encoder_job
|
88
|
+
@job ||= Encoder::Job.new(self.zencoder_job_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def video_files_method
|
92
|
+
self.class.encoder_output_files_association
|
93
|
+
end
|
94
|
+
|
95
|
+
def video_files_thumbnails_method
|
96
|
+
self.class.encoder_thumbnails_association
|
97
|
+
end
|
98
|
+
|
99
|
+
# need to know the Class of the associations so we can instantiate some when job is complete.
|
100
|
+
def video_files_class
|
101
|
+
self.class.reflect_on_all_associations(:has_many).detect{ |reflection| reflection.name == self.class.encoder_output_files_association }.klass
|
102
|
+
end
|
103
|
+
|
104
|
+
def video_thumbnails_class
|
105
|
+
self.class.reflect_on_all_associations(:has_many).detect{ |reflection| reflection.name == self.class.encoder_thumbnails_association }.klass
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
module Encoder
|
113
|
+
include Zencoder
|
114
|
+
|
115
|
+
class Job < Zencoder::Job
|
116
|
+
|
117
|
+
attr_accessor :id
|
118
|
+
|
119
|
+
class << self
|
120
|
+
|
121
|
+
def create(origin, encoder_definitions)
|
122
|
+
response = super(:input => origin,
|
123
|
+
:outputs => build_encoder_output_options(origin, encoder_definitions))
|
124
|
+
if response.code == 201
|
125
|
+
job_id = response.body['id']
|
126
|
+
self.new(job_id)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def build_encoder_output_options(origin, definitions)
|
131
|
+
|
132
|
+
formats = definitions[:formats] || [:ogg]
|
133
|
+
size = definitions[:output_dimensions] || '400x300'
|
134
|
+
base_url = s3_url(origin, definitions[:s3_config], definitions[:path])
|
135
|
+
|
136
|
+
defaults = { :public => true,
|
137
|
+
:device_profile => "mobile/advanced",
|
138
|
+
:size => size
|
139
|
+
}
|
140
|
+
defaults = defaults.merge(definitions[:options]) if definitions[:options]
|
141
|
+
|
142
|
+
if definitions[:thumbnails]
|
143
|
+
defaults[:thumbnails] = {:aspect_mode => 'crop',
|
144
|
+
:base_url => base_url,
|
145
|
+
:size => size
|
146
|
+
}.merge(definitions[:thumbnails])
|
147
|
+
end
|
148
|
+
|
149
|
+
formats.collect{ |f| defaults.merge( :format => f.to_s, :label => f.to_s, :base_url => base_url ) }
|
150
|
+
end
|
151
|
+
|
152
|
+
def s3_url origin_url, s3_config_file, path
|
153
|
+
basename = origin_url.match( %r|([^/][^/\?]+)[^/]*\.[^.]+\z| )[1] # matches filename without extension
|
154
|
+
basename = basename.downcase.squish.gsub(/\s+/, '-').gsub(/[^\w\d_.-]/, '') # cheap/ugly to_url
|
155
|
+
path = path.gsub(%r|:basename\b|, basename)
|
156
|
+
"s3://#{s3_bucket_name(s3_config_file)}.s3.amazonaws.com/#{path}/"
|
157
|
+
end
|
158
|
+
|
159
|
+
def s3_bucket_name s3_config_file
|
160
|
+
s3_config_file ||= "#{Rails.root}/config/s3.yml"
|
161
|
+
@s3_config ||= YAML.load_file(s3_config_file)[Rails.env].symbolize_keys
|
162
|
+
@s3_config[:bucket_name]
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def initialize(job_id)
|
168
|
+
@id = job_id
|
169
|
+
@job_detail = {}
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def details
|
174
|
+
if @job_detail.empty? and @id
|
175
|
+
response = self.class.details @id
|
176
|
+
if response.code == 200
|
177
|
+
@job_detail = response.body['job']
|
178
|
+
end
|
179
|
+
end
|
180
|
+
@job_detail
|
181
|
+
end
|
182
|
+
|
183
|
+
def status
|
184
|
+
self.details['state']
|
185
|
+
end
|
186
|
+
|
187
|
+
def finished_at
|
188
|
+
self.details['finished_at']
|
189
|
+
end
|
190
|
+
|
191
|
+
def files
|
192
|
+
if outfiles = self.details['output_media_files']
|
193
|
+
outfiles.collect { |f| { :url => f['url'],
|
194
|
+
:format => f['label'],
|
195
|
+
:zencoder_file_id => f['id'],
|
196
|
+
:created_at => f['finished_at'],
|
197
|
+
:duration_sec => f['duration_in_ms'],
|
198
|
+
:width => f['width'],
|
199
|
+
:height => f['height'],
|
200
|
+
:file_size => f['file_size_bytes'],
|
201
|
+
:error_message => f['error_message'],
|
202
|
+
:state => f['state'] }
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# ZC gives thumbnails for each output file format, but gives them the same
|
208
|
+
# name and overwrites them at the same S3 location. So if we have f
|
209
|
+
# formats, and ask for x thumbnails, we get x*f files described in the
|
210
|
+
# details['thumbnails'] API, but there are actually only x on the S3
|
211
|
+
# server. So, the inject() here is done to pare that down to unique URLs,
|
212
|
+
# and give us the cols/vals that paperclip in VideoThumbnail is going to want
|
213
|
+
def thumbnails
|
214
|
+
if thumbs = self.details['thumbnails']
|
215
|
+
|
216
|
+
thumbs.inject([]) do |res,th|
|
217
|
+
unless res.map{ |r| r[:thumbnail_file_name] }.include?(th['url'])
|
218
|
+
res << { :thumbnail_file_name => th['url'],
|
219
|
+
:thumbnail_content_type =>th['format'],
|
220
|
+
:thumbnail_file_size => th['file_size_bytes'],
|
221
|
+
:thumbnail_updated_at => th['created_at']
|
222
|
+
}
|
223
|
+
end
|
224
|
+
res
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
class ActiveRecord::Base
|
237
|
+
include Zencodable
|
238
|
+
end
|
239
|
+
|