fogged 0.0.2

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