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