fogged 0.0.2

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 (89) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +34 -0
  4. data/app/controllers/fogged/resources_controller.rb +259 -0
  5. data/app/jobs/fogged/resources/zencoder_poll_job.rb +39 -0
  6. data/app/models/fogged/resource.rb +153 -0
  7. data/app/models/fogged/resources/aws_encoder.rb +64 -0
  8. data/app/models/fogged/resources/encoder.rb +13 -0
  9. data/app/serializers/fogged/resource_serializer.rb +35 -0
  10. data/config/locales/en.yml +6 -0
  11. data/config/locales/fr.yml +6 -0
  12. data/config/routes.rb +4 -0
  13. data/db/migrate/20141103143408_create_fogged_resources.rb +20 -0
  14. data/lib/fogged.rb +46 -0
  15. data/lib/fogged/acts_as_having_many_resources.rb +30 -0
  16. data/lib/fogged/acts_as_having_one_resource.rb +34 -0
  17. data/lib/fogged/engine.rb +50 -0
  18. data/lib/fogged/version.rb +3 -0
  19. data/lib/tasks/fogged_tasks.rake +4 -0
  20. data/test/controllers/fogged/resources_controller/confirm_test.rb +56 -0
  21. data/test/controllers/fogged/resources_controller/create_test.rb +56 -0
  22. data/test/controllers/fogged/resources_controller/destroy_test.rb +29 -0
  23. data/test/controllers/fogged/resources_controller/show_test.rb +35 -0
  24. data/test/controllers/fogged/resources_controller/update_test.rb +31 -0
  25. data/test/dummy/Rakefile +6 -0
  26. data/test/dummy/app/assets/javascripts/application.js +13 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  28. data/test/dummy/app/controllers/application_controller.rb +5 -0
  29. data/test/dummy/app/controllers/images_controller.rb +5 -0
  30. data/test/dummy/app/helpers/application_helper.rb +2 -0
  31. data/test/dummy/app/models/image.rb +3 -0
  32. data/test/dummy/app/models/movie.rb +4 -0
  33. data/test/dummy/app/models/movie_fogged_resource.rb +4 -0
  34. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/test/dummy/bin/bundle +3 -0
  36. data/test/dummy/bin/delayed_job +5 -0
  37. data/test/dummy/bin/rails +4 -0
  38. data/test/dummy/bin/rake +4 -0
  39. data/test/dummy/config.ru +4 -0
  40. data/test/dummy/config/application.rb +23 -0
  41. data/test/dummy/config/boot.rb +5 -0
  42. data/test/dummy/config/database.yml +25 -0
  43. data/test/dummy/config/environment.rb +5 -0
  44. data/test/dummy/config/environments/development.rb +37 -0
  45. data/test/dummy/config/environments/production.rb +78 -0
  46. data/test/dummy/config/environments/test.rb +39 -0
  47. data/test/dummy/config/initializers/assets.rb +8 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  50. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  51. data/test/dummy/config/initializers/fog.rb +1 -0
  52. data/test/dummy/config/initializers/fogged.rb +7 -0
  53. data/test/dummy/config/initializers/inflections.rb +16 -0
  54. data/test/dummy/config/initializers/mime_types.rb +4 -0
  55. data/test/dummy/config/initializers/session_store.rb +3 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +23 -0
  58. data/test/dummy/config/routes.rb +4 -0
  59. data/test/dummy/config/secrets.yml +22 -0
  60. data/test/dummy/db/development.sqlite3 +0 -0
  61. data/test/dummy/db/migrate/20141104135257_create_images.rb +10 -0
  62. data/test/dummy/db/migrate/20141104152227_create_movies.rb +9 -0
  63. data/test/dummy/db/migrate/20141104152345_create_movie_fogged_resources.rb +10 -0
  64. data/test/dummy/db/migrate/20141105073909_create_delayed_jobs.rb +22 -0
  65. data/test/dummy/db/schema.rb +72 -0
  66. data/test/dummy/db/test.sqlite3 +0 -0
  67. data/test/dummy/log/development.log +234 -0
  68. data/test/dummy/log/test.log +20250 -0
  69. data/test/dummy/public/404.html +67 -0
  70. data/test/dummy/public/422.html +67 -0
  71. data/test/dummy/public/500.html +66 -0
  72. data/test/dummy/public/favicon.ico +0 -0
  73. data/test/dummy/test/controllers/resources_controller/index_test.rb +56 -0
  74. data/test/dummy/test/models/image_test.rb +24 -0
  75. data/test/dummy/test/models/movie_test.rb +26 -0
  76. data/test/fixtures/fogged/resources.yml +31 -0
  77. data/test/fixtures/images.yml +5 -0
  78. data/test/fixtures/movie_fogged_resources.yml +19 -0
  79. data/test/fixtures/movies.yml +8 -0
  80. data/test/fogged_test.rb +7 -0
  81. data/test/integration/navigation_test.rb +10 -0
  82. data/test/jobs/fogged/resources/zencoder_poll_job_test.rb +92 -0
  83. data/test/models/fogged/resource_test.rb +97 -0
  84. data/test/models/fogged/resources/aws_encoder_test.rb +54 -0
  85. data/test/models/fogged/resources/encoder_test.rb +18 -0
  86. data/test/support/concerns/json_test_helper.rb +43 -0
  87. data/test/support/concerns/resource_test_helper.rb +28 -0
  88. data/test/test_helper.rb +59 -0
  89. metadata +354 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 117c0db8e6eced45592f16bb7cbb19aadb026dba
4
+ data.tar.gz: 5e52387b1f5338038bf2c378d7b4a7ff9217f0a0
5
+ SHA512:
6
+ metadata.gz: 29213f2ad1476ea58e24a488e8c2a422950b27008e596d497714f9d8f65e9ce1f73ead680b7d33ea0c2ce7ae1380ce05172b5777c71c49c4d40607c752643cd1
7
+ data.tar.gz: 3bff3cc1207dc3f9f28057044ed731cdec712ffed320a14de4896eb5cc0730571e91d70755d90f07b8fe2a3aecf638d4aa5468cf81f51dcf9b78460539debd47
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Gatemedia.ch
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.
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Fogged'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,259 @@
1
+ # The Resource API. This API has only 3 methods: create, confirm and destroy.
2
+ #
3
+ # To create a new resource, call the create method with a filename and a
4
+ # content_type, you will get a resource as a result. This resource has an
5
+ # upload_url you can use to put your content. Caution, this url is valid only
6
+ # for 2 minutes.
7
+ #
8
+ # When uploading a resource, two http headers are *mandatory*:
9
+ # * Content-Type, which *must* be the same value as the one used to create the
10
+ # resource
11
+ # * Set the acl to public read. Eg. on AWS send the x-amz-acl header set to
12
+ # "public-read". This ensures that the resource is publicly readable.
13
+ #
14
+ # The upload can fail if these two flags/settings are missing.
15
+ #
16
+ # Once you finished uploading the resource, you can confirm it with the method
17
+ # confirm. You MUST confirm a resource in order to use it elsewhere (eg. in the
18
+ # Event api). Once confirmed, a resource can't be updated. For example, if you
19
+ # want to update the resource content, create a new one. A resource has these
20
+ # fields:
21
+ #
22
+ # id - the resource's id
23
+ # name - the resource's name
24
+ # upload_url - the secured url where you can upload your content. This link has
25
+ # already all the correct parameters for S3. Do not change them.
26
+ # url - the public url where you can read the content
27
+ # h264_url - the url of the h264 file. Only for video resources
28
+ # mpeg_url - the url of the mpeg file. Only for video resources
29
+ # webm_url - the url of the webm file. Only for video resources
30
+ # thumbnail_urls - the urls of the thumbnails. Only for video resources
31
+ # encoding_progress - the progress of the encoding in %. Only for video
32
+ # resources
33
+ #
34
+ # Examples
35
+ #
36
+ # {
37
+ # "resource": {
38
+ # "id": 1,
39
+ # "upload_url": "https:...",
40
+ # "url": "https:..."
41
+ # }
42
+ # }
43
+ #
44
+ # {
45
+ # "resource": {
46
+ # "id": 42145,
47
+ # "name": "Resource 6",
48
+ # "url": "https://...",
49
+ # "h264_url": "https://...",
50
+ # "mpeg_url": "https://...",
51
+ # "webm_url": "https://...",
52
+ # "thumbnail_urls": [
53
+ # "https://...",
54
+ # "https://...",
55
+ # "https://...",
56
+ # "https://...",
57
+ # "https://..."
58
+ # ],
59
+ # "encoding_progress": 0
60
+ # }
61
+ # }
62
+ module Fogged
63
+ class ResourcesController < Fogged.parent_controller.constantize
64
+ before_action :select_resourceables, :only => :index
65
+ before_action :select_resource, :only => %i(confirm destroy show update)
66
+
67
+ # List all resources. Parameter type is mandatory. It indicates in which
68
+ # "context" you want all resources. You can refine the search, using
69
+ # using parameters type_id or type_ids, which will select the context
70
+ # objects before retrieving the resources. Pagination options are
71
+ # available. Search options are available.
72
+ #
73
+ # type - the context/type in which the resources search is done. Mandatory
74
+ # type_id - the specific type id
75
+ # type_ids - the specific type ids
76
+ # query - text search (insensitive) on the resource's name
77
+ #
78
+ # Example
79
+ #
80
+ # GET /api/v1/resources?type=group&ids[]=2&ids[]=3
81
+ # GET /api/v1/resources?type=group
82
+ #
83
+ # Returns an array of Resources found
84
+ # Raises 400 if the given type is unknown
85
+ # Raises 500 if an error occurs
86
+ def index
87
+ resources = @resourceables.map do |resourceable|
88
+ resourceable.resources.search(params)
89
+ end
90
+
91
+ render :json => paginate(resources.flatten.uniq),
92
+ :meta => @meta,
93
+ :each_serializer => ResourceSerializer
94
+ end
95
+
96
+ # Get the resource.
97
+ #
98
+ # id - the resource's id
99
+ #
100
+ # Example
101
+ #
102
+ # GET /api/v1/resources/123
103
+ #
104
+ # Returns the Resource
105
+ # Raises 404 if the Resource is not found
106
+ # Raises 500 if an error occurs
107
+ def show
108
+ render :json => @resource,
109
+ :serializer => ResourceSerializer
110
+ end
111
+
112
+ # Create a new resource. You must provide a filename (example: foo.png)
113
+ # and a content_type (example: "image/png"). This content_type must be the
114
+ # same as the one used with the upload_url.
115
+ #
116
+ # resource - a hash with a name (optional), a filename and a content_type
117
+ #
118
+ #
119
+ # Examples
120
+ #
121
+ # POST /api/v1/resources
122
+ # {
123
+ # "resource": {
124
+ # "name": "The ultimate ruby"
125
+ # "filename": "ruby.png"
126
+ # "content_type": "image/png"
127
+ # }
128
+ # }
129
+ #
130
+ # Return the created resource which will be marked as uploading.
131
+ # Raises 400 if resource params are not valid.
132
+ # Raises 500 if an error occurs.
133
+ def create
134
+ resource = Resource.create!(
135
+ :name => resource_params.require(:name),
136
+ :extension => extension(resource_params.require(:filename)),
137
+ :content_type => resource_params.require(:content_type),
138
+ :uploading => true
139
+ )
140
+
141
+ render :json => resource,
142
+ :serializer => ResourceSerializer,
143
+ :include_upload_url => true
144
+ end
145
+
146
+ # Update a Resource. You can update the name of a Resource or its context
147
+ # object
148
+ #
149
+ # id - the resource's id
150
+ # resource - the resource params
151
+ #
152
+ # Example
153
+ #
154
+ # PUT /api/v1/resources/1
155
+ # {
156
+ # "resource": {
157
+ # "name": "Updated"
158
+ # }
159
+ # }
160
+ #
161
+ # Returns the updated Resource
162
+ # Raises 400 if the parameters are invalid
163
+ # Raises 404 if the resource or the context object can't be found
164
+ # Raises 500 if an error occurs
165
+ def update
166
+ @resource.update!(resource_params.permit(:name))
167
+
168
+ show
169
+ end
170
+
171
+ # Confirm a resource. You MUST confirm a resource before using it.
172
+ #
173
+ # id - the resource id
174
+ #
175
+ # Examples
176
+ #
177
+ # PUT /api/v1/resources/1/confirm
178
+ #
179
+ # Return the confirmed resource. Note that the upload_url will not be
180
+ # present in the response.
181
+ # Raises 500 if a server errors occurs
182
+ def confirm
183
+ @resource.process!
184
+ @resource.update!(:uploading => false)
185
+ show
186
+ end
187
+
188
+ # Destroy a resource. You can choose to destroy a resource. This
189
+ # will also destroy the content on S3.
190
+ #
191
+ # id - the resource id
192
+ #
193
+ # Examples
194
+ #
195
+ # DELETE /api/v1/resources/1
196
+ #
197
+ # Return an empty response with 204(No content)
198
+ # Raises 500 if a server errors occurs
199
+ def destroy
200
+ @resource.destroy!
201
+ head :no_content
202
+ end
203
+
204
+ private
205
+
206
+ def select_resourceables
207
+ ids = params[:type_id] || params[:type_ids]
208
+ ids = [ids] unless ids.respond_to?(:to_ary)
209
+ @resourceables = resourceable_clazz.all
210
+ @resourceables = resourceable_clazz.where(:id => ids) if ids.try(:any?)
211
+ end
212
+
213
+ def resourceable_clazz
214
+ @_resourceable_clazz ||= resource_type_param.try(:classify)
215
+ .try(:safe_constantize)
216
+ unless @_resourceable_clazz
217
+ fail(ArgumentError, "Unknown resourceable type: #{params[:type]}")
218
+ end
219
+ @_resourceable_clazz
220
+ end
221
+
222
+ def resource_type_param
223
+ params.require(:type)
224
+ end
225
+
226
+ def select_resource
227
+ @resource = Resource.find(params[:id])
228
+ end
229
+
230
+ def resource_params
231
+ params.require(:resource).permit(:name, :filename, :content_type)
232
+ end
233
+
234
+ def extension(filename)
235
+ extension = File.extname(filename)
236
+ extension.tap { |e| e.slice!(0) }
237
+ end
238
+
239
+ def paginate(result)
240
+ page = Integer(params[:page] || 1)
241
+ count = Integer(params[:count] || 50).to_i
242
+ offset = (page - 1) * count
243
+ total = result.size
244
+ @meta = {
245
+ :pagination => {
246
+ :total => total,
247
+ :remaining => [total - offset - count, 0].max
248
+ }
249
+ }
250
+
251
+ case result
252
+ when Array
253
+ result.slice(offset, count)
254
+ else
255
+ result.limit(count).offset(offset)
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,39 @@
1
+ module Fogged
2
+ module Resources
3
+ class ZencoderPollJob < Struct.new(:resource_id)
4
+ def perform
5
+ @resource = Fogged::Resource.find(resource_id)
6
+ update_encoding_progress
7
+
8
+ return if @resource.encoding_progress == 100
9
+
10
+ frequency = Fogged.zencoder_polling_frequency
11
+ Delayed::Job.enqueue(self, :run_at => frequency.seconds.from_now)
12
+ end
13
+
14
+ private
15
+
16
+ def update_encoding_progress
17
+ job = Zencoder::Job.progress(@resource.encoding_job_id)
18
+
19
+ case job.body["state"]
20
+ when "finished"
21
+ job = Zencoder::Job.details(@resource.encoding_job_id)
22
+ f = job.body["job"]["output_media_files"][0]
23
+ @resource.update!(
24
+ :encoding_progress => 100,
25
+ :width => f["width"],
26
+ :height => f["height"],
27
+ :duration => f["duration_in_ms"].to_f / 1000.0
28
+ )
29
+ when "processing", "waiting"
30
+ @resource.update!(
31
+ :encoding_progress => job.body["progress"].to_i
32
+ )
33
+ else
34
+ fail(ArgumentError, "Unknown Zencoder job state #{job.body["state"]}")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,153 @@
1
+ module Fogged
2
+ class Resource < ActiveRecord::Base
3
+ validates :extension, :content_type, :presence => true
4
+
5
+ before_save :ensure_token
6
+ after_destroy :destroy_fogged_file
7
+
8
+ def self.search(params)
9
+ results = all
10
+
11
+ if params[:query]
12
+ results = results.where(
13
+ "#{table_name}.name LIKE :query",
14
+ :query => "%#{params[:query].to_s.downcase}%"
15
+ )
16
+ end
17
+
18
+ results
19
+ end
20
+
21
+ def upload_url
22
+ fogged_file.service.put_object_url(
23
+ Fogged.resources.key,
24
+ fogged_file.key,
25
+ 2.minutes.from_now,
26
+ "Content-Type" => content_type,
27
+ "x-amz-acl" => "public-read"
28
+ )
29
+ end
30
+
31
+ def url
32
+ storage = Fogged.resources
33
+ storage.service.try(
34
+ :request_url,
35
+ :bucket_name => storage.key,
36
+ :object_name => fogged_name
37
+ )
38
+ end
39
+
40
+ def h264_url
41
+ return unless video?
42
+ url.gsub(fogged_name, fogged_name_for(:h264))
43
+ end
44
+
45
+ def mpeg_url
46
+ return unless video?
47
+ url.gsub(fogged_name, fogged_name_for(:mpeg))
48
+ end
49
+
50
+ def webm_url
51
+ return unless video?
52
+ url.gsub(fogged_name, fogged_name_for(:webm))
53
+ end
54
+
55
+ def thumbnail_urls
56
+ return unless video?
57
+ 5.times.map do |n|
58
+ url.gsub(fogged_name, fogged_name_for(:thumbnails, n))
59
+ end
60
+ end
61
+
62
+ def video?
63
+ content_type.start_with?("video/")
64
+ end
65
+
66
+ def image?
67
+ content_type.start_with?("image/")
68
+ end
69
+
70
+ def encoding?
71
+ return false unless video? && encoding_progress.present?
72
+ encoding_progress < 100
73
+ end
74
+
75
+ def process!
76
+ find_size! if image?
77
+ encode! if video?
78
+ end
79
+
80
+ def write(content)
81
+ fogged_file.body = content
82
+ fogged_file.save
83
+ end
84
+
85
+ private
86
+
87
+ def find_size!
88
+ if Fogged.test_enabled
89
+ update!(
90
+ :width => 800,
91
+ :height => 600
92
+ )
93
+ else
94
+ size = FastImage.size(url)
95
+ update!(
96
+ :width => size.first,
97
+ :height => size.second
98
+ ) unless size.blank?
99
+ end
100
+ end
101
+
102
+ def encode!
103
+ return unless Fogged.zencoder_enabled
104
+ Resources::Encoder.for(self).encode!
105
+ end
106
+
107
+ def ensure_token
108
+ self.token = generate_token if token.blank?
109
+ end
110
+
111
+ def generate_token
112
+ loop do
113
+ a_token = SecureRandom.hex(16)
114
+ break a_token unless Resource.find_by(:token => a_token)
115
+ end
116
+ end
117
+
118
+ def fogged_file
119
+ return @fogged_file if defined?(@fogged_file)
120
+
121
+ files = Fogged.resources.files
122
+ @fogged_file = files.get(fogged_name) || files.create(
123
+ :key => fogged_name,
124
+ :body => "",
125
+ :public => true,
126
+ :content_type => content_type
127
+ )
128
+ end
129
+
130
+ def fogged_name
131
+ "#{token}.#{extension}"
132
+ end
133
+
134
+ def fogged_name_for(type, number = 0)
135
+ case type
136
+ when :h264
137
+ "#{token}-h264.mp4"
138
+ when :mpeg
139
+ "#{token}-mpeg.mp4"
140
+ when :webm
141
+ "#{token}-webm.webm"
142
+ when :thumbnails
143
+ "#{token}-thumbnail-#{number}.png"
144
+ else
145
+ fail(ArgumentError, "Can't get fogged name of #{type}")
146
+ end
147
+ end
148
+
149
+ def destroy_fogged_file
150
+ fogged_file.destroy
151
+ end
152
+ end
153
+ end