phocoder-rails 0.0.33

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.
Files changed (116) hide show
  1. data/.autotest +46 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +44 -0
  5. data/LICENSE.txt +20 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.rdoc +3 -0
  8. data/Rakefile +56 -0
  9. data/VERSION +5 -0
  10. data/app/controllers/phocoder_controller.rb +118 -0
  11. data/app/helpers/phocoder_helper.rb +320 -0
  12. data/app/models/encodable_job.rb +91 -0
  13. data/app/views/phocoder/_offline_video_embed.html.erb +19 -0
  14. data/app/views/phocoder/_thumbnail_update.html.erb +3 -0
  15. data/app/views/phocoder/_video_embed.html.erb +23 -0
  16. data/app/views/phocoder/multi_thumbnail_update.json.erb +7 -0
  17. data/app/views/phocoder/thumbnail_update.js.erb +9 -0
  18. data/config/routes.rb +8 -0
  19. data/lib/generators/phocoder_rails/model_update_generator.rb +52 -0
  20. data/lib/generators/phocoder_rails/scaffold_generator.rb +94 -0
  21. data/lib/generators/phocoder_rails/setup_generator.rb +33 -0
  22. data/lib/generators/phocoder_rails/templates/controller.rb +71 -0
  23. data/lib/generators/phocoder_rails/templates/helper.rb +5 -0
  24. data/lib/generators/phocoder_rails/templates/migration.rb +24 -0
  25. data/lib/generators/phocoder_rails/templates/model.rb +20 -0
  26. data/lib/generators/phocoder_rails/templates/model_migration.rb +56 -0
  27. data/lib/generators/phocoder_rails/templates/model_thumbnail.rb +5 -0
  28. data/lib/generators/phocoder_rails/templates/model_update_migration.rb +64 -0
  29. data/lib/generators/phocoder_rails/templates/phocodable.yml +28 -0
  30. data/lib/generators/phocoder_rails/templates/views/_form.html.erb.tt +23 -0
  31. data/lib/generators/phocoder_rails/templates/views/index.html.erb.tt +26 -0
  32. data/lib/generators/phocoder_rails/templates/views/new.html.erb.tt +5 -0
  33. data/lib/generators/phocoder_rails/templates/views/show.html.erb.tt +12 -0
  34. data/lib/phocoder_rails.rb +12 -0
  35. data/lib/phocoder_rails/acts_as_phocodable.rb +1153 -0
  36. data/lib/phocoder_rails/engine.rb +24 -0
  37. data/lib/phocoder_rails/errors.rb +46 -0
  38. data/phocoder-rails.gemspec +219 -0
  39. data/public/images/building.gif +0 -0
  40. data/public/images/error.png +0 -0
  41. data/public/images/play_small.png +0 -0
  42. data/public/images/storing.gif +0 -0
  43. data/public/images/waiting.gif +0 -0
  44. data/public/javascripts/phocodable.js +110 -0
  45. data/public/javascripts/video-js-2.0.2/.DS_Store +0 -0
  46. data/public/javascripts/video-js-2.0.2/LICENSE.txt +165 -0
  47. data/public/javascripts/video-js-2.0.2/README.markdown +202 -0
  48. data/public/javascripts/video-js-2.0.2/demo-subtitles.srt +13 -0
  49. data/public/javascripts/video-js-2.0.2/demo.html +101 -0
  50. data/public/javascripts/video-js-2.0.2/skins/hu.css +116 -0
  51. data/public/javascripts/video-js-2.0.2/skins/tube.css +111 -0
  52. data/public/javascripts/video-js-2.0.2/skins/vim.css +89 -0
  53. data/public/javascripts/video-js-2.0.2/video-js.css +242 -0
  54. data/public/javascripts/video-js-2.0.2/video.js +1758 -0
  55. data/public/stylesheets/phocodable.css +19 -0
  56. data/spec/controllers/phocoder_controller_spec.rb +123 -0
  57. data/spec/dummy/Rakefile +7 -0
  58. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  59. data/spec/dummy/app/controllers/images_controller.rb +72 -0
  60. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  61. data/spec/dummy/app/helpers/images_helper.rb +5 -0
  62. data/spec/dummy/app/models/image.rb +20 -0
  63. data/spec/dummy/app/models/image_thumbnail.rb +5 -0
  64. data/spec/dummy/app/models/image_upload.rb +11 -0
  65. data/spec/dummy/app/views/images/_form.html.erb +23 -0
  66. data/spec/dummy/app/views/images/index.html.erb +26 -0
  67. data/spec/dummy/app/views/images/new.html.erb +5 -0
  68. data/spec/dummy/app/views/images/show.html.erb +12 -0
  69. data/spec/dummy/app/views/layouts/application.html.erb +18 -0
  70. data/spec/dummy/config.ru +4 -0
  71. data/spec/dummy/config/application.rb +45 -0
  72. data/spec/dummy/config/boot.rb +10 -0
  73. data/spec/dummy/config/database.yml +25 -0
  74. data/spec/dummy/config/environment.rb +8 -0
  75. data/spec/dummy/config/environments/development.rb +26 -0
  76. data/spec/dummy/config/environments/production.rb +49 -0
  77. data/spec/dummy/config/environments/test.rb +40 -0
  78. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/spec/dummy/config/initializers/inflections.rb +10 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  81. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  82. data/spec/dummy/config/initializers/session_store.rb +8 -0
  83. data/spec/dummy/config/locales/en.yml +5 -0
  84. data/spec/dummy/config/routes.rb +60 -0
  85. data/spec/dummy/db/migrate/001_create_image_uploads.rb +37 -0
  86. data/spec/dummy/db/migrate/20110523165213_add_parent_type_to_image_uploads.rb +11 -0
  87. data/spec/dummy/db/migrate/20110523165522_create_encodable_jobs.rb +24 -0
  88. data/spec/dummy/db/migrate/20111101024507_create_images.rb +56 -0
  89. data/spec/dummy/db/schema.rb +99 -0
  90. data/spec/dummy/public/404.html +26 -0
  91. data/spec/dummy/public/422.html +26 -0
  92. data/spec/dummy/public/500.html +26 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/dummy/public/index.html +239 -0
  95. data/spec/dummy/public/javascripts/application.js +2 -0
  96. data/spec/dummy/public/javascripts/controls.js +965 -0
  97. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  98. data/spec/dummy/public/javascripts/effects.js +1123 -0
  99. data/spec/dummy/public/javascripts/jquery-1.6.4.js +9046 -0
  100. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  101. data/spec/dummy/public/javascripts/rails.js +175 -0
  102. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  103. data/spec/dummy/script/rails +6 -0
  104. data/spec/engine_spec.rb +12 -0
  105. data/spec/fixtures/big_eye_tiny.jpg +0 -0
  106. data/spec/fixtures/octologo.png +0 -0
  107. data/spec/fixtures/test.txt +2 -0
  108. data/spec/fixtures/video-test.mov +0 -0
  109. data/spec/helpers/phocoder_helper_spec.rb +421 -0
  110. data/spec/integration/navigation_spec.rb +10 -0
  111. data/spec/models/acts_as_phocodable_spec.rb +664 -0
  112. data/spec/models/encodable_job_spec.rb +50 -0
  113. data/spec/phocoder_rails_spec.rb +8 -0
  114. data/spec/routing/phocoder_routing_spec.rb +19 -0
  115. data/spec/spec_helper.rb +75 -0
  116. metadata +375 -0
@@ -0,0 +1,20 @@
1
+ class <%= name.classify %> < ActiveRecord::Base
2
+
3
+ acts_as_phocodable :thumbnail_class => "<%= name.classify %>Thumbnail",
4
+ :thumbnails => [
5
+ {:label=>"small",:width=>100,:height=>100 },
6
+ {:label=>"medium",:width=>400,:height=>400,
7
+ :frame=>{ :width=>20, :bottom=>50, :color=>'003' },
8
+ :annotations=>[
9
+ {:text=>"Annotation Testing",:pointsize=>30,:fill_color=>'fff',:gravity=>"South",:y=>10},
10
+ {:text=>"Howdy!",:pointsize=>10,:fill_color=>'ccc',:gravity=>"North",:y=>5}
11
+ ]
12
+ }
13
+ ],
14
+
15
+ :videos => [ {:label => 'mp4',:video_codec=>"h264" }, #, :thumbnails=>{ :number=>1, :start_at_first_frame=>1 }
16
+ {:label => 'webm', :video_codec=>"vp8" },
17
+ {:label => 'ogv', :video_codec=>"theora" }
18
+ ]
19
+
20
+ end
@@ -0,0 +1,56 @@
1
+ class Create<%= name.classify.pluralize %> < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ create_table :<%= name.pluralize %> do |t|
5
+ t.string "filename"
6
+ t.string "content_type"
7
+ t.integer "duration_in_ms"
8
+ t.integer "width"
9
+ t.integer "height"
10
+ t.integer "file_size"
11
+ t.string "upload_host"
12
+ t.datetime "created_at"
13
+ t.datetime "updated_at"
14
+ t.datetime "taken_at"
15
+ t.float "lat"
16
+ t.float "lng"
17
+ t.string "encodable_status"
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :<%= name.pluralize %>, :id
23
+
24
+
25
+ create_table :<%= name.singularize %>_thumbnails do |t|
26
+ t.string "filename"
27
+ t.string "content_type"
28
+ t.integer "duration_in_ms"
29
+ t.integer "width"
30
+ t.integer "height"
31
+ t.integer "file_size"
32
+ t.string "upload_host"
33
+ t.datetime "created_at"
34
+ t.datetime "updated_at"
35
+ t.datetime "taken_at"
36
+ t.float "lat"
37
+ t.float "lng"
38
+ t.string "encodable_status"
39
+
40
+ t.string "thumbnail"
41
+ t.integer "parent_id"
42
+ t.string "parent_type"
43
+
44
+ t.timestamps
45
+ end
46
+
47
+ add_index :<%= name.singularize %>_thumbnails, :id
48
+ add_index :<%= name.singularize %>_thumbnails, :parent_id
49
+
50
+ end
51
+
52
+ def self.down
53
+ drop_table :<%= name.pluralize %>
54
+ drop_table :<%= name.singularize %>_thumbnails
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ class <%= name.classify %>Thumbnail < ActiveRecord::Base
2
+
3
+ acts_as_phocodable :parent_class => "<%= name.classify %>"
4
+
5
+ end
@@ -0,0 +1,64 @@
1
+ class Make<%= name.classify.pluralize %>Encodable < ActiveRecord::Migration
2
+ def self.up
3
+
4
+
5
+ add_column :<%= name.pluralize %>, :filename, :string
6
+ add_column :<%= name.pluralize %>, :content_type, :string
7
+ add_column :<%= name.pluralize %>, :duration_in_ms, :integer
8
+ add_column :<%= name.pluralize %>, :width, :integer
9
+ add_column :<%= name.pluralize %>, :height, :integer
10
+ add_column :<%= name.pluralize %>, :file_size, :integer
11
+ add_column :<%= name.pluralize %>, :upload_host, :string
12
+ add_column :<%= name.pluralize %>, :taken_at, :datetime
13
+ add_column :<%= name.pluralize %>, :lat, :float
14
+ add_column :<%= name.pluralize %>, :lng, :float
15
+ add_column :<%= name.pluralize %>, :encodable_status, :string
16
+
17
+
18
+
19
+ create_table :<%= name.singularize %>_thumbnails do |t|
20
+ t.string "filename"
21
+ t.string "content_type"
22
+ t.integer "duration_in_ms"
23
+ t.integer "width"
24
+ t.integer "height"
25
+ t.integer "file_size"
26
+ t.string "upload_host"
27
+ t.datetime "created_at"
28
+ t.datetime "updated_at"
29
+ t.datetime "taken_at"
30
+ t.float "lat"
31
+ t.float "lng"
32
+ t.string "encodable_status"
33
+
34
+ t.string "thumbnail"
35
+ t.integer "parent_id"
36
+ t.string "parent_type"
37
+
38
+ t.timestamps
39
+ end
40
+
41
+ add_index :<%= name.singularize %>_thumbnails, :id
42
+ add_index :<%= name.singularize %>_thumbnails, :parent_id
43
+
44
+ end
45
+
46
+ def self.down
47
+
48
+ remove_column :<%= name.pluralize %>, :filename
49
+ remove_column :<%= name.pluralize %>, :content_type
50
+ remove_column :<%= name.pluralize %>, :duration_in_ms
51
+ remove_column :<%= name.pluralize %>, :width
52
+ remove_column :<%= name.pluralize %>, :height
53
+ remove_column :<%= name.pluralize %>, :file_size
54
+ remove_column :<%= name.pluralize %>, :upload_host
55
+ remove_column :<%= name.pluralize %>, :created_at
56
+ remove_column :<%= name.pluralize %>, :updated_at
57
+ remove_column :<%= name.pluralize %>, :taken_at
58
+ remove_column :<%= name.pluralize %>, :lat
59
+ remove_column :<%= name.pluralize %>, :lng
60
+ remove_column :<%= name.pluralize %>, :encodable_status
61
+
62
+ drop_table :<%= name.singularize %>_thumbnails
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ development:
2
+ phocoder_api_key: asdf
3
+ zencoder_api_key: asdf
4
+ base_url: http://your.host.test.com
5
+ storeage_mode : s3
6
+ s3_bucket_name: bucket.name
7
+ s3_access_key_id: asdf
8
+ s3_secret_access_key: asdf
9
+
10
+ test:
11
+ phocoder_api_key: asdf
12
+ zencoder_api_key: asdf
13
+ phocoder_url: http://photoapi.chaos.webapeel.com
14
+ base_url: http://your.host.test.com
15
+ storeage_mode : local
16
+ s3_bucket_name: bucket.name
17
+ s3_access_key_id: asdf
18
+ s3_secret_access_key: asdf
19
+
20
+ production:
21
+ phocoder_api_key: asdf
22
+ zencoder_api_key: asdf
23
+ base_url: http://your.host.test.com
24
+ storeage_mode : s3
25
+ processing_mode : resque
26
+ s3_bucket_name: bucket.name
27
+ s3_access_key_id: asdf
28
+ s3_secret_access_key: asdf
@@ -0,0 +1,23 @@
1
+ <%%= form_for( @<%= file_name.singularize %>, :html => { :multipart => true } ) do |f| %>
2
+
3
+ <%% if @<%= file_name.singularize %>.errors.any? %>
4
+ <div id="error_explanation">
5
+ <h2><%%= pluralize(@<%= file_name.singularize %>.errors.count, "error") %> prohibited this encodable from being saved:</h2>
6
+ <ul>
7
+ <%% @<%= file_name.singularize %>.errors.full_messages.each do |msg| %>
8
+ <li><%%= msg %></li>
9
+ <%% end %>
10
+ </ul>
11
+ </div>
12
+ <%% end %>
13
+
14
+ <div class="field">
15
+ <%%= f.label :file, "Upload image file" %>
16
+ <%%= f.file_field :file %>
17
+ </div>
18
+
19
+ <div class="actions">
20
+ <%%= f.submit %>
21
+ </div>
22
+
23
+ <%% end %>
@@ -0,0 +1,26 @@
1
+ <h1>Listing <%= file_name %></h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Filename</th>
6
+ <th></th>
7
+ <th></th>
8
+
9
+ </tr>
10
+
11
+ <%% @<%= file_name %>.each do |<%= file_name.singularize %>| %>
12
+ <tr>
13
+ <td>
14
+ <%%= phocoder_thumbnail <%= file_name.singularize %>,"small",false %><br/>
15
+ <%%= <%= file_name.singularize %>.filename %>
16
+ </td>
17
+ <td><%%= link_to 'Show', <%= file_name.singularize %> %></td>
18
+
19
+ <td><%%= link_to 'Destroy', <%= file_name.singularize %>, :confirm => 'Are you sure?', :method => :delete %></td>
20
+ </tr>
21
+ <%% end %>
22
+ </table>
23
+
24
+ <br />
25
+
26
+ <%%= link_to 'New <%= file_name.singularize %>', new_<%= file_name.singularize %>_path %>
@@ -0,0 +1,5 @@
1
+ <h1>New <%= file_name.singularize %></h1>
2
+
3
+ <%%= render 'form' %>
4
+
5
+ <%%= link_to 'Back', <%= file_name %>_path %>
@@ -0,0 +1,12 @@
1
+ <p id="notice"><%%= notice %></p>
2
+
3
+ <p>
4
+ <b>Filename:</b>
5
+ <%%= @<%= file_name.singularize %>.filename %>
6
+ </p>
7
+
8
+ <%%= phocoder_thumbnail @<%= file_name.singularize %>,"medium" %>
9
+
10
+ <br/>
11
+
12
+ <%%= link_to 'Back', <%= file_name %>_path %>
@@ -0,0 +1,12 @@
1
+ module PhocoderRails
2
+ require 'phocoder_rails/engine' if defined?(Rails)
3
+
4
+ require 'active_record'
5
+ #require 'action_pack'
6
+ #require 'active_support'
7
+ require 'aws/s3'
8
+
9
+ require 'phocoder_rails/acts_as_phocodable'
10
+ require 'phocoder_rails/errors'
11
+ #require 'phocoder_rails/base'
12
+ end
@@ -0,0 +1,1153 @@
1
+ module ActsAsPhocodable
2
+
3
+ require 'phocoder'
4
+ require 'zencoder'
5
+ require 'open-uri'
6
+ # Storeage mode controls how uploads are handled.
7
+ # Valid options are:
8
+ # offline : For development mode with no net connection. No processing.
9
+ # local : To store images locally but use Phocoder for processing.
10
+ # s3 : Store image in S3 and use Phocoder for processing.
11
+ # Set this options either in evnironment.rb or
12
+ # in environments/development.rb etc...
13
+
14
+ mattr_accessor :storeage_mode
15
+ self.storeage_mode = "local"
16
+
17
+
18
+ # Processing mode controls when images get sent to phocoder
19
+ # Valid options are:
20
+ # automatic : Send to phocoder as soon as the file is stored.
21
+ # With 'local' storage mode, this submits the job
22
+ # to phocoder while the user is still waiting.
23
+ # delayed : Handle storage/phocoding in a background process.
24
+ # spawn : Use the Spawn library to fork a new process to store/phocode.
25
+ mattr_accessor :processing_mode
26
+ self.processing_mode = "automatic"
27
+
28
+ # This is used as the base address for phocoder notifications.
29
+ # When storeage_mode == "local" this is also used to point
30
+ # phocoder at the file.
31
+ # It should only be the host.domain portion of the URL
32
+ # no path components.
33
+ mattr_accessor :base_url
34
+ self.base_url = "http://your-domain.com"
35
+
36
+ # The bucket for storing s3 files
37
+ mattr_accessor :s3_bucket_name
38
+ self.s3_bucket_name = "your-bucket"
39
+
40
+ # The access_key_id for storing s3 files
41
+ mattr_accessor :s3_access_key_id
42
+ self.s3_access_key_id = "your-access-key-id"
43
+
44
+ # The secret_access_key for storing s3 files
45
+ mattr_accessor :s3_secret_access_key
46
+ self.s3_secret_access_key = "your-secret-access-key"
47
+
48
+ # The javascript library to use for updates
49
+ # either 'prototype' or 'jquery'
50
+ mattr_accessor :javascript_library
51
+ self.javascript_library = 'prototype'
52
+
53
+ # The local directory where files should be stored
54
+ mattr_accessor :local_base_dir
55
+ self.local_base_dir = '/tmp'
56
+
57
+ # The config file that tells phocoder where to find
58
+ # config options.
59
+ mattr_accessor :config_file
60
+ self.config_file = "config/phocodable.yml"
61
+
62
+
63
+ # The list of image content types that are considered web safe
64
+ # These can be displayed directly, skipping processing if in offline mode
65
+ mattr_accessor :web_safe_image_types
66
+ self.web_safe_image_types = [
67
+ 'image/jpeg',
68
+ 'image/jpg',
69
+ 'image/gif',
70
+ 'image/png',
71
+ 'image/x-png',
72
+ 'image/jpg',
73
+ 'application/png',
74
+ 'application/x-png'
75
+ ]
76
+
77
+ # The list of image content types that are not considered web safe
78
+ # These can not be displayed directly
79
+ mattr_accessor :other_image_types
80
+ self.other_image_types = [
81
+ 'image/pjpeg',
82
+ 'image/x-ms-bmp',
83
+ 'image/bmp',
84
+ 'image/x-bmp',
85
+ 'image/x-bitmap',
86
+ 'image/x-xbitmap',
87
+ 'image/x-win-bitmap',
88
+ 'image/x-windows-bmp',
89
+ 'image/ms-bmp',
90
+ 'application/bmp',
91
+ 'application/x-bmp',
92
+ 'application/x-win-bitmap',
93
+ 'application/preview',
94
+ 'image/jp_',
95
+ 'application/jpg',
96
+ 'application/x-jpg',
97
+ 'image/pipeg',
98
+ 'image/vnd.swiftview-jpeg',
99
+ 'image/x-xbitmap',
100
+ 'image/gi_',
101
+ 'image/x-citrix-pjpeg',
102
+ 'image/x-nikon-nef',
103
+ 'image/tiff',
104
+ 'image/x-olympus-orf',
105
+ 'image/x-dcraw'
106
+ ]
107
+
108
+ # The list of content types that will trigger image handling.
109
+ def image_types
110
+ web_safe_image_types + other_image_types
111
+ end
112
+
113
+
114
+ # The list of content types that will trigger video handling.
115
+ mattr_accessor :video_types
116
+ self.video_types = [
117
+ 'application/x-flash-video',
118
+ 'video/avi',
119
+ 'video/mp4',
120
+ 'video/ogg',
121
+ 'video/quicktime',
122
+ 'video/3gp',
123
+ 'video/3gpp',
124
+ 'video/vnd.objectvideo',
125
+ 'video/x-ms-wmv',
126
+ 'video/x-ms-asf',
127
+ 'video/x-ms-wvx',
128
+ 'video/x-ms-wm',
129
+ 'video/x-ms-wmx'
130
+ ]
131
+
132
+ # Mapping for generating a file extension
133
+ # based on the codec passed in for zencoder
134
+ mattr_accessor :video_extensions
135
+ self.video_extensions = {
136
+ "h264" => "mp4",
137
+ "vp6" => "vp6",
138
+ "vp8" => "webm",
139
+ "theora" => "ogv",
140
+ "mpeg4" => "mpg",
141
+ "wmv" => "wmv"
142
+ }
143
+
144
+ # TODO : This needs to be fixed.
145
+ # It currently matches anything with an 'x' in it
146
+ mattr_accessor :label_size_regex
147
+ self.label_size_regex = /(\d*)x(\d*)(pad|crop|stretch|preserve)?/ #
148
+
149
+ mattr_accessor :size_string_regex
150
+ self.size_string_regex = /(\d*)x(\d*)([!>]?)/
151
+
152
+ def image?(content_type)
153
+ image_types.include?(content_type)
154
+ end
155
+
156
+ def web_safe?(content_type)
157
+ web_safe_image_types.include?(content_type)
158
+ end
159
+
160
+ def video?(content_type)
161
+ video_types.include?(content_type)
162
+ end
163
+
164
+
165
+ #def self.storeage_mode
166
+ # @@storeage_mode
167
+ #end
168
+
169
+
170
+ def acts_as_phocodable(options = { })
171
+
172
+ include InstanceMethods
173
+ include Spawn
174
+ attr_reader :saved_file
175
+ attr_accessor :phocoding
176
+ after_save :save_local_file
177
+ before_destroy :cleanup #:remove_local_file,:destroy_thumbnails,:remove_s3_file
178
+
179
+ include ActiveSupport::Callbacks
180
+
181
+ define_callbacks :local_file_saved, :file_saved, :file_ready, :phocode_hdr, :phocode_composite, :phocode_tone_mapping
182
+
183
+ #cattr_accessor :phocoder_options
184
+ #self.phocoder_options = options
185
+
186
+ cattr_accessor :phocoder_thumbnails
187
+ self.phocoder_thumbnails = options[:thumbnails] ||= []
188
+
189
+ cattr_accessor :zencoder_videos
190
+ self.zencoder_videos = options[:videos] ||= []
191
+
192
+ cattr_accessor :thumbnail_class
193
+ self.thumbnail_class = options[:thumbnail_class] ? options[:thumbnail_class].constantize : self
194
+
195
+ cattr_accessor :parent_class
196
+ self.parent_class = options[:parent_class] ? options[:parent_class].constantize : self
197
+
198
+ has_many :thumbnails, :class_name => "::#{self.thumbnail_class.name}",:as => :parent
199
+ if self.thumbnail_class != self.parent_class
200
+ #we have to do this to get the poster for videos covered
201
+ belongs_to :parent, :polymorphic => true
202
+ else
203
+ belongs_to :parent, :class_name => "::#{self.parent_class.name}" ,:foreign_key => "parent_id"
204
+ end
205
+
206
+
207
+ has_many :encodable_jobs, :as => :encodable
208
+
209
+ scope :top_level, where({:parent_id=>nil}) if respond_to?(:parent_id)
210
+ scope :top_level, where({}) if !respond_to?(:parent_id)
211
+ # we can't just call this next scope 'parents' because that is already
212
+ # taken and returns an array of parent classes of the ruby object
213
+ scope :parent_items, where({:parent_id=>nil}) if respond_to?(:parent_id)
214
+ scope :parent_items, where({}) if !respond_to?(:parent_id)
215
+
216
+ scope :thumbnails, where("#{base_class.table_name}.parent_id is not null")
217
+
218
+ #just a writer, the reader is below
219
+ cattr_accessor :phocodable_configuration
220
+ read_phocodable_configuration
221
+ end
222
+
223
+ def config
224
+ return phocodable_configuration if !phocodable_configuration.blank?
225
+ self.read_phocodable_configuration
226
+ end
227
+
228
+ def validates_phocodable
229
+ validates_presence_of :content_type, :filename, :if=>lambda{ parent_id.blank? }
230
+ end
231
+
232
+ def update_from_phocoder(params)
233
+ Rails.logger.debug "tying to call update from phocoder for params = #{params.to_json}"
234
+ if !params[:output].blank?
235
+ Rails.logger.debug "find_by_phocoder_output_id #{params[:output][:id]}"
236
+ iu = find_by_phocoder_output_id params[:output][:id]
237
+ Rails.logger.debug "the item = #{iu}"
238
+ img_params = params[:output]
239
+ iu.filename = File.basename(params[:output][:url]) #if iu.filename.blank?
240
+ if ActsAsPhocodable.storeage_mode == "local"
241
+ iu.save_url(params[:output][:url])
242
+ end
243
+ else
244
+ iu = find_by_phocoder_input_id params[:input][:id]
245
+ img_params = params[:input]
246
+ end
247
+ [:file_size,:width,:height,:taken_at,:lat,:lng].each do |att|
248
+ setter = att.to_s + "="
249
+ if iu.respond_to? setter and !img_params[att].blank?
250
+ iu.send setter, img_params[att]
251
+ end
252
+ end
253
+
254
+ #iu.file_size = img_params[:file_size]
255
+ #iu.width = img_params[:width]
256
+ #iu.height = img_params[:height]
257
+ iu.phocoder_status = "ready"
258
+ iu.save
259
+ iu
260
+ end
261
+
262
+
263
+ # Updating from zencoder is a two pass operation.
264
+ # This method gets called for each output when it's ready.
265
+ # Once all outputs are ready, we call parent.check_zencoder_details
266
+ def update_from_zencoder(params)
267
+ Rails.logger.debug "tying to call update from zencoder for params = #{params}"
268
+ iu = find_by_zencoder_output_id params[:output][:id]
269
+ if params[:output][:url].match /%2F(.*)\?/
270
+ iu.filename = $1
271
+ else
272
+ iu.filename = File.basename(params[:output][:url].match(/(.*)\??/)[1])
273
+ end
274
+ #iu.filename = File.basename(params[:output][:url].match(/(.*)\??/)[1]) if iu.filename.blank?
275
+ if ActsAsPhocodable.storeage_mode == "local"
276
+ iu.save_url(params[:output][:url])
277
+ end
278
+ iu.zencoder_status = "ready"
279
+ iu.save
280
+ iu.parent.check_zencoder_details
281
+ end
282
+
283
+ def thumbnail_attributes_for(thumbnail_name = "small")
284
+ atts = self.phocoder_thumbnails.select{|atts| atts[:label] == thumbnail_name }.first
285
+ if atts.blank?
286
+ atts = create_atts_from_size_string(thumbnail_name)
287
+ end
288
+ if atts.blank?
289
+ raise ThumbnailAttributesNotFoundError.new("No thumbnail attributes were found for label '#{thumbnail_name}'")
290
+ end
291
+ atts
292
+ end
293
+
294
+ def create_label_from_size_string(size_string)
295
+ if size_string.match ActsAsPhocodable.size_string_regex
296
+ size_string = size_string.gsub("!","crop")
297
+ size_string = size_string.gsub(">","preserve")
298
+ end
299
+ size_string
300
+ end
301
+
302
+ def create_atts_from_size_string(label_string)
303
+ match = label_string.match ActsAsPhocodable.label_size_regex
304
+ return nil if match.blank?
305
+ atts = {}
306
+ if !match[1].blank?
307
+ atts[:width] = match[1]
308
+ end
309
+ if !match[2].blank?
310
+ atts[:height] = match[2]
311
+ end
312
+ if !match[3].blank?
313
+ atts[:aspect_mode] = match[3]
314
+ end
315
+ atts[:label] = label_string
316
+ #atts[:label] = "#{atts[:width]}x#{atts[:height]}"
317
+ #atts[:label] += "_#{atts[:aspect_mode]}" if atts[:aspect_mode]
318
+ atts
319
+ end
320
+
321
+
322
+
323
+ def read_phocodable_configuration
324
+ config_path = File.join(::Rails.root.to_s, ActsAsPhocodable.config_file)
325
+ puts "looking for a config in #{config_path}"
326
+ self.phocodable_configuration = YAML.load(ERB.new(File.read(config_path)).result)[::Rails.env.to_s].symbolize_keys
327
+ self.apply_phocodable_configuration
328
+ end
329
+
330
+ def apply_phocodable_configuration
331
+ if self.phocodable_configuration[:base_url]
332
+ ActsAsPhocodable.base_url = phocodable_configuration[:base_url]
333
+ end
334
+ if self.phocodable_configuration[:storeage_mode]
335
+ ActsAsPhocodable.storeage_mode = phocodable_configuration[:storeage_mode]
336
+ end
337
+ if self.phocodable_configuration[:processing_mode]
338
+ ActsAsPhocodable.processing_mode = phocodable_configuration[:processing_mode]
339
+ end
340
+ if self.phocodable_configuration[:s3_bucket_name]
341
+ ActsAsPhocodable.s3_bucket_name = phocodable_configuration[:s3_bucket_name]
342
+ end
343
+ if self.phocodable_configuration[:s3_access_key_id]
344
+ ActsAsPhocodable.s3_access_key_id = phocodable_configuration[:s3_access_key_id]
345
+ end
346
+ if self.phocodable_configuration[:s3_secret_access_key]
347
+ ActsAsPhocodable.s3_secret_access_key = phocodable_configuration[:s3_secret_access_key]
348
+ end
349
+ if self.phocodable_configuration[:javascript_library]
350
+ ActsAsPhocodable.javascript_library = phocodable_configuration[:javascript_library]
351
+ end
352
+ if self.phocodable_configuration[:local_base_dir]
353
+ ActsAsPhocodable.local_base_dir = phocodable_configuration[:local_base_dir]
354
+ end
355
+
356
+
357
+ if self.phocodable_configuration[:phocoder_url]
358
+ ::Phocoder.base_url = phocodable_configuration[:phocoder_url]
359
+ end
360
+ if self.phocodable_configuration[:phocoder_api_key]
361
+ ::Phocoder.api_key = phocodable_configuration[:phocoder_api_key]
362
+ end
363
+ if self.phocodable_configuration[:zencoder_api_key]
364
+ ::Zencoder.api_key = phocodable_configuration[:zencoder_api_key]
365
+ end
366
+ if ActsAsPhocodable.storeage_mode == "s3"
367
+ self.establish_aws_connection
368
+ end
369
+ end
370
+
371
+ def establish_aws_connection
372
+ AWS::S3::Base.establish_connection!(
373
+ :access_key_id => ActsAsPhocodable.s3_access_key_id,
374
+ :secret_access_key => ActsAsPhocodable.s3_secret_access_key
375
+ )
376
+ end
377
+
378
+
379
+
380
+
381
+
382
+
383
+ module InstanceMethods
384
+
385
+
386
+
387
+ def image?
388
+ self.class.image?(content_type)
389
+ end
390
+
391
+ def web_safe?
392
+ self.class.web_safe?(content_type)
393
+ end
394
+
395
+ def video?
396
+ self.class.video?(content_type)
397
+ end
398
+
399
+ def encode
400
+ if image?
401
+ phocode
402
+ elsif video?
403
+ zencode
404
+ end
405
+ end
406
+
407
+ def recode
408
+ reload #make sure that we have current thumbs
409
+ destroy_thumbnails
410
+ reload
411
+ encode
412
+ end
413
+
414
+ def ready?
415
+ if ActsAsPhocodable.storeage_mode == "offline"
416
+ true
417
+ #elsif image?
418
+ # return phocoder_status=='ready'
419
+ #elsif video?
420
+ # return zencoder_status=='ready'
421
+ #else
422
+ # return false
423
+ else
424
+ return encodable_status == "ready"
425
+ end
426
+ end
427
+
428
+ def error?
429
+ if ActsAsPhocodable.storeage_mode == "offline"
430
+ false
431
+ #elsif image?
432
+ # return phocoder_status=='failed'
433
+ #elsif video?
434
+ # return zencoder_status=='failed'
435
+ #else
436
+ # true
437
+ else
438
+ return encodable_status == "ready"
439
+ end
440
+ end
441
+
442
+ def create_thumbnails_from_response(response_thumbs,job_id)
443
+ new_thumbs = []
444
+ response_thumbs.each do |thumb_params|
445
+ puts "creating a thumb for #{thumb_params["label"]}"
446
+ # we do this the long way around just in case some of these
447
+ # atts are attr_protected
448
+ thumb = nil
449
+ if respond_to?(:parent_id) and !self.parent_id.blank?
450
+ Rails.logger.debug "trying to create a thumb from the parent "
451
+ thumb = self.parent.thumbnails.new()
452
+ self.parent.thumbnails << thumb
453
+ else
454
+ Rails.logger.debug "trying to create a thumb from myself "
455
+ thumb = self.thumbnails.new()
456
+ self.thumbnails << thumb
457
+ end
458
+
459
+
460
+ thumb.thumbnail = thumb_params["label"]
461
+ thumb.filename = thumb_params["filename"]
462
+ thumb.width = thumb_params["width"]
463
+ thumb.height = thumb_params["height"]
464
+ tjob = thumb.encodable_jobs.new
465
+
466
+ tjob.phocoder_output_id = thumb_params["id"]
467
+ tjob.phocoder_job_id = job_id
468
+ #thumb.parent_id = self.id
469
+ tjob.phocoder_status = "phocoding"
470
+ thumb.encodable_jobs << tjob
471
+ thumb.encodable_status = "phocoding"
472
+ thumb.save
473
+ new_thumbs << thumb
474
+ Rails.logger.debug " thumb.errors = #{thumb.errors.to_json}"
475
+ puts " thumb.errors = #{thumb.errors.to_json}"
476
+ end
477
+ new_thumbs
478
+ end
479
+
480
+ def clear_phocoding
481
+ phocoding = false
482
+ end
483
+
484
+ def dedupe_input_thumbs(input_thumbs)
485
+ needed_thumbs = []
486
+ input_thumbs.each do |it|
487
+ t = thumbnails.find_by_thumbnail(it[:label])
488
+ if t.blank?
489
+ needed_thumbs << it
490
+ end
491
+ end
492
+ needed_thumbs
493
+ end
494
+
495
+ def phocode(input_thumbs = self.parent_class.phocoder_thumbnails)
496
+ puts " input_thumbs.count = #{input_thumbs.size}"
497
+ input_thumbs = dedupe_input_thumbs(input_thumbs)
498
+ puts " after dedupe input_thumbs.count = #{input_thumbs.size}"
499
+ #if self.thumbnails.count >= self.class.phocoder_thumbnails.size
500
+ # raise "This item already has thumbnails!"
501
+ # return
502
+ #end
503
+
504
+ return if input_thumbs.size == 0
505
+ # We do this because sometimes save will get called more than once
506
+ # during a single request
507
+ return if phocoding
508
+ phocoding = true
509
+
510
+ Rails.logger.debug "trying to phocode for #{Phocoder.base_url} "
511
+ Rails.logger.debug "callback url = #{callback_url}"
512
+ response = Phocoder::Job.create(phocoder_params(input_thumbs))
513
+ Rails.logger.debug "the phocode response = #{response.to_json}" if Rails.env != "test"
514
+ puts "the phocode response = #{response.to_json}" if Rails.env != "test"
515
+ job = self.encodable_jobs.new
516
+ job.phocoder_input_id = response.body["job"]["inputs"].first["id"]
517
+ job.phocoder_job_id = response.body["job"]["id"]
518
+ job.phocoder_status = "phocoding"
519
+ self.encodable_jobs << job
520
+ self.encodable_status = "phocoding" unless self.encodable_status == "ready" # the unless clause allows new thumbs to be created on the fly without jacking with the status
521
+ self.save #false need to do save(false) here if we're calling phocode on after_save
522
+ response_thumbs = response.body["job"]["thumbnails"]
523
+ Rails.logger.debug "trying to decode #{response_thumbs.size} response_thumbs = #{response_thumbs.to_json}"
524
+ puts "trying to decode #{response_thumbs.size} response_thumbs = #{response_thumbs.to_json}"
525
+ create_thumbnails_from_response(response_thumbs,response.body["job"]["id"])
526
+ end
527
+
528
+ def phocode_hdr
529
+ #if self.thumbnails.count >= self.class.phocoder_thumbnails.size
530
+ # raise "This item already has thumbnails!"
531
+ # return
532
+ #end
533
+
534
+ # We do this because sometimes save will get called more than once
535
+ # during a single request
536
+ return if phocoding
537
+ phocoding = true
538
+ run_callbacks :phocode_hdr do
539
+ Rails.logger.debug "trying to phocode for #{Phocoder.base_url} "
540
+ Rails.logger.debug "callback url = #{callback_url}"
541
+ response = Phocoder::Job.create(phocoder_hdr_params)
542
+ Rails.logger.debug "the response from phocode_hdr = #{response.body.to_json}"
543
+ job = self.encodable_jobs.new
544
+ job.phocoder_output_id = response.body["job"]["hdr"]["id"]
545
+ job.phocoder_job_id = response.body["job"]["id"]
546
+ job.phocoder_status = "phocoding"
547
+ self.encodable_jobs << job
548
+ self.encodable_status = "phocoding"
549
+ self.save #false need to do save(false) here if we're calling phocode on after_save
550
+ end
551
+
552
+ end
553
+
554
+
555
+ def phocode_tone_mapping
556
+ #if self.thumbnails.count >= self.class.phocoder_thumbnails.size
557
+ # raise "This item already has thumbnails!"
558
+ # return
559
+ #end
560
+
561
+ # We do this because sometimes save will get called more than once
562
+ # during a single request
563
+ return if phocoding
564
+ phocoding = true
565
+ run_callbacks :phocode_tone_mapping do
566
+ destroy_thumbnails
567
+ Rails.logger.debug "trying to phocode for #{Phocoder.base_url} "
568
+ Rails.logger.debug "callback url = #{callback_url}"
569
+ response = Phocoder::Job.create(phocoder_tone_mapping_params)
570
+ Rails.logger.debug "tone_mapping response = #{response.body.to_json}"
571
+ puts "tone_mapping response = #{response.body.to_json}"
572
+ job = self.encodable_jobs.new
573
+ job.phocoder_output_id = response.body["job"]["tone_mapping"]["id"]
574
+ job.phocoder_job_id = response.body["job"]["id"]
575
+ job.phocoder_status = "phocoding"
576
+ self.encodable_jobs << job
577
+ self.encodable_status = "phocoding"
578
+ self.save #false need to do save(false) here if we're calling phocode on after_save
579
+ response_thumbs = response.body["job"]["thumbnails"]
580
+ Rails.logger.debug "trying to decode #{response_thumbs.size} response_thumbs = #{response_thumbs.to_json}"
581
+ create_thumbnails_from_response(response_thumbs,response.body["job"]["id"])
582
+ end
583
+ end
584
+
585
+
586
+ def phocode_composite
587
+ #if self.thumbnails.count >= self.class.phocoder_thumbnails.size
588
+ # raise "This item already has thumbnails!"
589
+ # return
590
+ #end
591
+
592
+ # We do this because sometimes save will get called more than once
593
+ # during a single request
594
+ return if phocoding
595
+ phocoding = true
596
+ run_callbacks :phocode_composite do
597
+ destroy_thumbnails
598
+ Rails.logger.debug "trying to phocode for #{Phocoder.base_url} "
599
+ Rails.logger.debug "callback url = #{callback_url}"
600
+ response = Phocoder::Job.create(phocoder_composite_params)
601
+ Rails.logger.debug "composite response = #{response.body.to_json}"
602
+ puts "composite response = #{response.body.to_json}"
603
+ job = self.encodable_jobs.new
604
+ job.phocoder_output_id = response.body["job"]["composite"]["id"]
605
+ job.phocoder_job_id = response.body["job"]["id"]
606
+ job.phocoder_status = "phocoding"
607
+ self.encodable_jobs << job
608
+ self.encodable_status = "phocoding"
609
+ self.save #false need to do save(false) here if we're calling phocode on after_save
610
+ response_thumbs = response.body["job"]["thumbnails"]
611
+ Rails.logger.debug "trying to decode #{response_thumbs.size} response_thumbs = #{response_thumbs.to_json}"
612
+ puts "trying to decode #{response_thumbs.size} response_thumbs = #{response_thumbs.to_json}"
613
+ create_thumbnails_from_response(response_thumbs,response.body["job"]["id"])
614
+ end
615
+ end
616
+
617
+
618
+ def zencode
619
+ # We do this because sometimes save will get called more than once
620
+ # during a single request
621
+ return if @zencoding
622
+ @zencoding = true
623
+
624
+ Rails.logger.debug "trying to zencode!!!!!"
625
+ Rails.logger.debug "callback url = #{callback_url}"
626
+ response = Zencoder::Job.create(zencoder_params)
627
+ Rails.logger.debug "response from Zencoder = #{response.body.to_json}"
628
+ job = self.encodable_jobs.new
629
+ job.zencoder_job_id = response.body["id"]
630
+ self.encodable_jobs << job
631
+
632
+ response.body["outputs"].each do |output_params|
633
+ thumb = self.thumbnails.new()
634
+ thumb.thumbnail = output_params["label"]
635
+
636
+ tjob = thumb.encodable_jobs.new
637
+ tjob.zencoder_output_id = output_params["id"]
638
+ tjob.zencoder_url = output_params["url"]
639
+ tjob.zencoder_job_id = response.body["id"]
640
+ tjob.zencoder_status = "zencoding"
641
+ thumb.encodable_jobs << tjob
642
+
643
+ self.thumbnails << thumb
644
+ thumb.encodable_status = "zencoding"
645
+ thumb.save
646
+ puts " thumb.errors = #{thumb.errors.to_json}"
647
+ end
648
+
649
+ self.save
650
+ end
651
+
652
+ def phocoder_extension
653
+ if self.content_type.blank?
654
+ ".jpg"
655
+ else
656
+ self.content_type.match(/png/) ? ".png" : ".jpg"
657
+ end
658
+ end
659
+
660
+ def phocoder_params(input_thumbs = self.parent_class.phocoder_thumbnails)
661
+ {:input => {:url => self.public_url, :notifications=>[{:url=>callback_url }] },
662
+ :thumbnails => phocoder_thumbnail_params(input_thumbs)
663
+ }
664
+ end
665
+
666
+ def phocoder_thumbnail_params(input_thumbs = self.parent_class.phocoder_thumbnails)
667
+ input_thumbs.map{|thumb|
668
+ thumb_filename = thumb[:label] + "_" + File.basename(self.filename,File.extname(self.filename)) + phocoder_extension
669
+ base_url = ActsAsPhocodable.storeage_mode == "s3" ? "s3://#{self.s3_bucket_name}/#{self.thumbnail_resource_dir}/" : ""
670
+ th = thumb.clone
671
+ th[:base_url] = base_url if !base_url.blank?
672
+ th.merge({
673
+ :filename=>thumb_filename,
674
+ :notifications=>[{:url=>thumbnail_callback_url }]
675
+ })
676
+ }
677
+ end
678
+
679
+
680
+ def phocoder_hdr_params
681
+ { }
682
+ end
683
+
684
+ def phocoder_tone_mapping_params
685
+ { }
686
+ end
687
+
688
+ def phocoder_composite_params
689
+ { }
690
+ end
691
+
692
+ def zencoder_params
693
+ base_url = ActsAsPhocodable.storeage_mode == "s3" ? "s3://#{self.s3_bucket_name}/#{self.thumbnail_resource_dir}/" : ""
694
+ params = {:input => self.public_url ,
695
+ :outputs => self.class.zencoder_videos.map{|video|
696
+ vid_filename = self.new_zencoder_filename( video[:video_codec] )
697
+ vid = video.clone
698
+ if vid[:thumbnails] and vid[:thumbnails].is_a? Array
699
+ vid[:thumbnails].each do |thumb|
700
+ thumb[:base_url] = base_url if !base_url.blank?
701
+ end
702
+ elsif vid[:thumbnails]
703
+ vid[:thumbnails][:base_url] = base_url if !base_url.blank?
704
+ end
705
+
706
+ vid[:base_url] = base_url if !base_url.blank?
707
+ vid.merge({
708
+ :filename=>vid_filename,
709
+ :public=>1,
710
+ :notifications=>[{:url=>zencoder_thumbnail_callback_url }]
711
+ })
712
+ }
713
+ }
714
+ params[:outputs][0][:thumbnails] = { :number=>1, :start_at_first_frame=>1,:public=>1 }
715
+ params[:outputs][0][:thumbnails][:base_url] = base_url if !base_url.blank?
716
+ Rails.logger.debug "for zencoder the params = #{params.to_json}"
717
+ #puts "for zencoder the params = #{params.to_json}"
718
+ params
719
+ end
720
+
721
+ def new_zencoder_filename(format)
722
+ filename + "." + self.class.video_extensions[format]
723
+ end
724
+
725
+
726
+ def check_zencoder_details
727
+ # we don't get a lot of info from zencoder, so we have to ask for details
728
+ shouldCheck = true
729
+ thumbnails.each do |t|
730
+ if t.encodable_status != 'ready'
731
+ shouldCheck = false
732
+ end
733
+ end
734
+
735
+ return if !shouldCheck
736
+
737
+ details = Zencoder::Job.details(self.encodable_jobs.last.zencoder_job_id)
738
+
739
+ #puts "in check_zencoder_details the details.body = #{details.body.to_json}"
740
+
741
+ if details.body["job"]["state"] != 'finished'
742
+ self.encodable_jobs.last.zencoder_status = details.body["job"]["state"]
743
+ save
744
+ return
745
+ end
746
+
747
+ self.encodable_jobs.last.zencoder_status = self.encodable_status = "ready"
748
+ self.width = details.body["job"]["input_media_file"]["width"]
749
+ self.height = details.body["job"]["input_media_file"]["height"]
750
+ self.duration_in_ms = details.body["job"]["input_media_file"]["duration_in_ms"]
751
+ self.file_size = details.body["job"]["input_media_file"]["file_size_bytes"]
752
+ self.save
753
+
754
+ #puts "the output files = #{details.body["job"]["output_media_files"]}"
755
+
756
+ update_zencoder_outputs(details)
757
+
758
+ # Now create the image thumb
759
+ create_zencoder_image_thumb(details)
760
+
761
+ end
762
+
763
+
764
+ def update_zencoder_outputs(details)
765
+ details.body["job"]["output_media_files"].each do |output|
766
+ #puts "updating for output = #{output.to_json}"
767
+ job = EncodableJob.find_by_zencoder_output_id output["id"]
768
+ thumb = job.encodable
769
+ thumb.width = output["width"]
770
+ thumb.height = output["height"]
771
+ thumb.file_size = output["file_size_bytes"]
772
+ thumb.duration_in_ms = output["duration_in_ms"]
773
+ thumb.save
774
+ end
775
+ end
776
+
777
+ def create_zencoder_image_thumb(details)
778
+
779
+ # for now we should only have one thumbnail
780
+ output = details.body["job"]["thumbnails"].first
781
+ return if output.blank?
782
+ thumb = thumbnails.new
783
+ thumb.thumbnail = "poster"
784
+ thumb.width = output["width"]
785
+ thumb.height = output["height"]
786
+ thumb.file_size = output["file_size_bytes"]
787
+ thumb.filename = "frame_0000.png" #File.basename(output["url"])
788
+ thumb.content_type = "image/png"
789
+ if ActsAsPhocodable.storeage_mode == "local"
790
+ thumb.save_url(output["url"])
791
+ end
792
+ thumb.save
793
+ #now get thumbnails for the poster
794
+ thumb.phocode
795
+ end
796
+
797
+ def phocodable_config
798
+ puts "looking for config!"
799
+ self.class.config
800
+ end
801
+
802
+ def phocodable?
803
+ true
804
+ end
805
+
806
+ def save_url(url)
807
+ Rails.logger.debug "We are about to download : #{url} to #{local_dir} - #{local_path}"
808
+ FileUtils.mkdir_p(local_dir) if !File.exists?(local_dir)
809
+ FileUtils.touch local_path
810
+ writeOut = open(local_path, "wb")
811
+ writeOut.write(open(url).read)
812
+ writeOut.close
813
+ end
814
+
815
+ def destroy_thumbnails
816
+ if self.class.phocoder_thumbnails.size == 0 and self.class.zencoder_videos.size == 0
817
+ puts "we're skipping destory_thumbnails since we don't do any processing "
818
+ return
819
+ end
820
+ #puts "calling destory thumbnails for #{self.thumbnails.count} - #{self.thumbnails.size}"
821
+ self.thumbnails.each do |thumb|
822
+ thumb.destroy
823
+ end
824
+ #puts "calling destory thumbnails for #{self.thumbnails.count}"
825
+ end
826
+
827
+ def create_atts_from_size_string(label_string)
828
+ self.class.create_atts_from_size_string(label_string)
829
+ end
830
+
831
+ def thumbnail_attributes_for(thumbnail_name)
832
+ self.class.thumbnail_attributes_for(thumbnail_name)
833
+ end
834
+
835
+ def thumbnail_for(thumbnail_hash_or_name)
836
+ thumbnail_name = thumbnail_hash_or_name.is_a?(Hash) ? thumbnail_hash_or_name[:label] : thumbnail_hash_or_name
837
+ if thumbnail_name and thumbnail_name.match ActsAsPhocodable.size_string_regex
838
+ thumbnail_name = self.class.create_label_from_size_string(thumbnail_name)
839
+ end
840
+ if thumbnail_name.blank? and thumbnail_hash_or_name.is_a?(Hash)
841
+ thumbnail_name = "#{thumbnail_hash_or_name[:width]}x#{thumbnail_hash_or_name[:height]}"
842
+ puts "thumbnail_name = #{thumbnail_name}"
843
+ thumbnail_hash_or_name[:label] = thumbnail_name
844
+ end
845
+ thumb = thumbnails.find_by_thumbnail(thumbnail_name)
846
+ if thumb.blank? and ActsAsPhocodable.storeage_mode == "offline"
847
+ thumb = self
848
+ elsif thumb.blank? and thumbnail_hash_or_name.is_a? Hash
849
+ thumb = self.phocode([thumbnail_hash_or_name]).first
850
+ elsif thumb.blank? and thumbnail_hash_or_name.is_a?(String) and thumbnail_hash_or_name.match ActsAsPhocodable.label_size_regex
851
+ atts = create_atts_from_size_string(thumbnail_name)
852
+ thumb = self.phocode([atts]).first
853
+ end
854
+ if thumb.blank?
855
+ raise ThumbnailNotFoundError.new("No thumbnail was found for label '#{thumbnail_name}'")
856
+ end
857
+ thumb
858
+ #a dirty hack for now to keep things working.
859
+ #Remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!
860
+ #Go back to just returning the thumb
861
+ #thumb.blank? ? self : thumb
862
+ end
863
+
864
+ def get_thumbnail(thumbnail_name)
865
+ thumbnail_for(thumbnail_name)
866
+ end
867
+
868
+ def file=(new_file)
869
+ return if new_file.nil?
870
+ Rails.logger.debug "we got a new file of class = #{new_file.class}"
871
+ cleanup
872
+ if new_file.is_a? File
873
+ self.filename = File.basename new_file.path
874
+ self.content_type = MIME::Types.type_for(self.filename).first.content_type
875
+ self.file_size = new_file.size
876
+ else
877
+ self.filename = new_file.original_filename
878
+ self.content_type = new_file.content_type
879
+ self.file_size = new_file.size
880
+ end
881
+
882
+ if new_file.respond_to? :tempfile
883
+ @saved_file = new_file.tempfile
884
+ else
885
+ @saved_file = new_file
886
+ end
887
+ end
888
+
889
+ #compatability method for attachment_fu
890
+ def uploaded_data=(data)
891
+ self.file = data
892
+ end
893
+
894
+ def save_local_file
895
+ return if @saved_file.blank?
896
+ puts "saving the local file!!!!!!"
897
+ Rails.logger.debug "==================================================================================================="
898
+ Rails.logger.debug "about to save the local file"
899
+ run_callbacks :file_saved do
900
+ run_callbacks :local_file_saved do
901
+ FileUtils.mkdir_p local_dir
902
+ FileUtils.cp @saved_file.path, local_path
903
+ FileUtils.chmod 0755, local_path
904
+ self.encodable_status = "local"
905
+ if self.respond_to? :upload_host
906
+ self.upload_host = %x{hostname}.strip
907
+ end
908
+ @saved_file = nil
909
+ @saved_a_new_file = true
910
+ self.save
911
+ end
912
+ if ActsAsPhocodable.storeage_mode == "s3" and ActsAsPhocodable.processing_mode == "automatic"
913
+ self.save_s3_file
914
+ end
915
+ if ActsAsPhocodable.storeage_mode == "s3" and ActsAsPhocodable.processing_mode == "spawn"
916
+ spawn do # :method => :thread # <-- I think that should be set at the config/environment level
917
+ Rails.logger.debug "------------beginning of spawn block"
918
+ puts "------------beginning of spawn block"
919
+ self.save_s3_file
920
+ Rails.logger.debug "------------end of spawn block"
921
+ puts "------------end of spawn block"
922
+ end
923
+ end
924
+ if ActsAsPhocodable.storeage_mode == "local" and ActsAsPhocodable.processing_mode == "automatic"
925
+ self.encode
926
+ end
927
+
928
+ end
929
+ end
930
+
931
+ def fire_ready_callback
932
+ run_callbacks :file_ready do
933
+ end
934
+ end
935
+
936
+
937
+ def cleanup
938
+ #puts "calling cleanup!"
939
+ destroy_thumbnails
940
+ remove_local_file
941
+ if ActsAsPhocodable.storeage_mode == "s3"
942
+ remove_s3_file
943
+ end
944
+ end
945
+
946
+ def remove_local_file
947
+ if local_path and File.exists? local_path
948
+ FileUtils.rm local_path
949
+ if Dir.glob(File.join(local_dir,"*")).size == 0
950
+ FileUtils.rmdir local_dir
951
+ end
952
+ end
953
+ end
954
+
955
+ def path_id
956
+
957
+ #puts "parent_id = #{parent_id}"
958
+ #puts "parent = #{parent}"
959
+ if respond_to?(:parent_id)
960
+ parent_id.blank? ? id : parent.path_id
961
+ else
962
+ id
963
+ end
964
+ end
965
+
966
+ def resource_dir
967
+ File.join(self.class.table_name, path_id.to_s )
968
+ end
969
+
970
+ def thumbnail_resource_dir
971
+ File.join(self.thumbnail_class.table_name, path_id.to_s )
972
+ end
973
+
974
+ def local_dir
975
+ File.join(ActsAsPhocodable.local_base_dir,resource_dir)
976
+ end
977
+
978
+ def local_path
979
+ filename.blank? ? nil : File.join(local_dir,filename)
980
+ end
981
+
982
+ def local_url
983
+ filename.blank? ? nil : File.join("/",resource_dir,filename)
984
+ end
985
+
986
+ # This should generate a fully qualified http://something-something
987
+ # type of a reference. Depending on storeage_mode/base_url settings.
988
+ def public_url
989
+ puts "our base_url = #{base_url} and our local_url = #{local_url}"
990
+ if ActsAsPhocodable.storeage_mode == "local" or ActsAsPhocodable.storeage_mode == "offline"
991
+ base_url + local_url
992
+ else
993
+ s3_url
994
+ end
995
+ end
996
+
997
+ def public_filename
998
+ public_url
999
+ end
1000
+
1001
+
1002
+ def callback_url
1003
+ self.base_url + self.notification_callback_path
1004
+ end
1005
+
1006
+ def zencoder_callback_url
1007
+ self.base_url + self.zencoder_notification_callback_path
1008
+ end
1009
+
1010
+ def thumbnail_callback_url
1011
+ self.base_url + self.thumbnail_notification_callback_path
1012
+ end
1013
+
1014
+ def zencoder_thumbnail_callback_url
1015
+ self.base_url + self.zencoder_thumbnail_notification_callback_path
1016
+ end
1017
+
1018
+ def notification_callback_path
1019
+ "/phocoder/phocoder_notifications/#{self.class.name}/#{self.id}.json"
1020
+ end
1021
+
1022
+ def zencoder_notification_callback_path
1023
+ "/phocoder/zencoder_notifications/#{self.class.name}/#{self.id}.json"
1024
+ end
1025
+
1026
+ def thumbnail_notification_callback_path
1027
+ "/phocoder/phocoder_notifications/#{self.thumbnail_class.name}/#{self.id}.json"
1028
+ end
1029
+
1030
+ def zencoder_thumbnail_notification_callback_path
1031
+ "/phocoder/zencoder_notifications/#{self.thumbnail_class.name}/#{self.id}.json"
1032
+ end
1033
+
1034
+ def base_url
1035
+ self.class.base_url
1036
+ end
1037
+
1038
+ def s3_key
1039
+ filename.blank? ? nil : File.join(resource_dir,filename)
1040
+ end
1041
+
1042
+ def s3_url
1043
+ "http://#{s3_bucket_name}.s3.amazonaws.com/#{s3_key}"
1044
+ end
1045
+
1046
+ def s3_bucket_name
1047
+ self.class.s3_bucket_name
1048
+ end
1049
+
1050
+ def save_s3_file
1051
+ #I don't think we need this return check anymore.
1052
+ #return if !@saved_a_new_file
1053
+ #@saved_a_new_file = false
1054
+ AWS::S3::S3Object.store(
1055
+ s3_key,
1056
+ open(local_path),
1057
+ s3_bucket_name,
1058
+ :access => :public_read
1059
+ )
1060
+ self.encodable_status = "s3"
1061
+ self.save
1062
+ obj_data = AWS::S3::S3Object.find(s3_key,s3_bucket_name)
1063
+ Rails.logger.debug "----------------------------------------------------------------------------------------------------"
1064
+ if obj_data.size == file_size # it made it into s3 safely
1065
+ Rails.logger.debug " we are about to remove local file!"
1066
+ remove_local_file
1067
+ else
1068
+ msg = "The file was not saved to S3 sucessfully. Orig size: #{file_size} - S3 size: #{obj_data.size}"
1069
+ Rails.logger.debug msg
1070
+ raise ActsAsPhocodable::Error.new msg
1071
+ end
1072
+ self.encode
1073
+ end
1074
+
1075
+
1076
+ def remove_s3_file
1077
+ #puts "trying to delete #{s3_key} #{s3_bucket_name}"
1078
+ #if ActsAsPhocodable.storeage_mode == "s3"
1079
+ AWS::S3::S3Object.delete s3_key, s3_bucket_name
1080
+ #end
1081
+ rescue Exception => e
1082
+ #this probably means that the file never made it to S3
1083
+ end
1084
+
1085
+ # Sanitizes a filename.
1086
+ def filename=(new_name)
1087
+ write_attribute :filename, sanitize_filename(new_name)
1088
+ end
1089
+
1090
+ def sanitize_filename(filename)
1091
+ return unless filename
1092
+ filename.strip.tap do |name|
1093
+ # NOTE: File.basename doesn't work right with Windows paths on Unix
1094
+ # get only the filename, not the whole path
1095
+ name.gsub! /^.*(\\|\/)/, ''
1096
+
1097
+ # Finally, replace all non alphanumeric, underscore or periods with underscore
1098
+ name.gsub! /[^A-Za-z0-9\.\-]/, '_'
1099
+ end
1100
+ end
1101
+
1102
+ # Calculate the width for the target thumbnail atts
1103
+ def calc_width(thumbnail_atts)
1104
+ tw = thumbnail_atts[:width].blank? ? 100000 : thumbnail_atts[:width].to_f
1105
+ th = thumbnail_atts[:height].blank? ? 100000 : thumbnail_atts[:height].to_f
1106
+ w = width.to_f
1107
+ h = height.to_f
1108
+ if w <= tw and h <= th
1109
+ w.round
1110
+ elsif w > h
1111
+ if (h * ( tw / w )).round < tw
1112
+ tw .round
1113
+ else
1114
+ (h * ( tw / w )).round
1115
+ end
1116
+ else
1117
+ if (w * ( th / h )).round < tw
1118
+ (w * ( th / h )).round
1119
+ else
1120
+ tw.round
1121
+ end
1122
+ end
1123
+ end #end calc_width
1124
+
1125
+
1126
+ def calc_height(thumbnail_atts)
1127
+ tw = thumbnail_atts[:width].blank? ? 100000 : thumbnail_atts[:width].to_f
1128
+ th = thumbnail_atts[:height].blank? ? 100000 : thumbnail_atts[:height].to_f
1129
+ w = width.to_f
1130
+ h = height.to_f
1131
+ if w <= tw and h <= th
1132
+ h.round
1133
+ elsif w > h
1134
+ if (h * ( tw / w )).round < th
1135
+ (h * ( tw / w )).round
1136
+ else
1137
+ th.round
1138
+ end
1139
+ else
1140
+ if (w * ( th / h )).round < tw
1141
+ th.round
1142
+ else
1143
+ (h * ( tw / w )).round
1144
+ end
1145
+ end
1146
+ end #end calc_height
1147
+
1148
+
1149
+
1150
+ end#module InstanceMethods
1151
+
1152
+ end
1153
+ ActiveRecord::Base.extend ActsAsPhocodable