shrine-transloadit 0.5.1 → 1.0.0.beta

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72d852ce420b15983f981ac19cd6bd8f3b5d5f5d5750e9843a6ee88f8c6d4a4b
4
- data.tar.gz: e3dbef98ddbf4b8b6897590371020a214576abfb4a091ed29ff2c92d6d2908e3
3
+ metadata.gz: 379b39a12a74f5b4e3e0c9336c1cc560eaa5ce78596fc55aa8990ea017d632bf
4
+ data.tar.gz: '04930268d5f2c682dac35cb635fd2e0be617fc6ce1c3bd55b0ee572f771b300f'
5
5
  SHA512:
6
- metadata.gz: 640d10e9ee8d7ddd884166dd1d8d0f26c0bf16ee3b22e25bf2c605e1677230c4c75a123aefdf84799350fc786136cf886f820bec9b318e1a73c5707122ac792e
7
- data.tar.gz: dbf511fd3c4382c4ba11cacc38988e7e1542df5254f9d5c0842d64fc44799ef78d3ff132db766ec1b88a1e4abe1dc8dab6d74425fa81e9b33f29b8e3ef8149c7
6
+ metadata.gz: 0bc371cbec27b5cf8a642eedea74c372e5b1bbef48b82956635a28dd76b02eb1436870a5ba3b18671f26bc897199c77c995df16070704688fecb3d42d11b8f74
7
+ data.tar.gz: 3da98b2895dd0af05e9f6aac2cc835105b754ef91e0ae0cd528b6cd7ace93a9ac82e8b7dc1ae5dc22b928d24caff89ebb3203b9a97735ac69349e97249ec89b4
data/README.md CHANGED
@@ -1,474 +1,608 @@
1
1
  # Shrine::Plugins::Transloadit
2
2
 
3
- Provides [Transloadit] integration for [Shrine].
3
+ Provides [Transloadit] integration for [Shrine], using its [Ruby SDK].
4
4
 
5
- Transloadit is a service that helps you handles file uploads, resize, crop and
5
+ Transloadit is a service that helps you handle file uploads, resize, crop and
6
6
  watermark your images, make GIFs, transcode your videos, extract thumbnails,
7
- generate audio waveforms, and so much more. In short, Transloadit is the Swiss
8
- Army Knife for your files.
7
+ generate audio waveforms and more.
8
+
9
+ ## Contents
10
+
11
+ * [Installation](#installation)
12
+ * [Setup](#setup)
13
+ - [Credentials](#credentials)
14
+ * [Usage](#usge)
15
+ - [Backgrounding](#backgrounding)
16
+ * [Notifications](#notifications)
17
+ * [Direct uploads](#direct-uploads)
18
+ * [Promotion](#promotion)
19
+ * [Skipping exports](#skipping-exports)
20
+ * [API](#api)
21
+ - [Processing & Saving](#processing-saving)
22
+ - [Generating steps](#generating-steps)
23
+ - [Parsing files](#parsing-files)
24
+ - [Verifying signature](#verifying-signature)
25
+ - [Transloadit instance](#transloadit-instance)
26
+
27
+ ## Installation
28
+
29
+ Put the gem in your Gemfile:
30
+
31
+ ```rb
32
+ # Gemfile
33
+ gem "shrine-transloadit", "~> 1.0"
34
+ ```
9
35
 
10
36
  ## Setup
11
37
 
12
- While Transloadit is able to export processed files to [many storage services],
13
- this plugin currently supports only Amazon S3 (just because there are no Shrine
14
- integrations written for other services on that list yet). You can just add
15
- shrine-transloadit to your current setup:
38
+ Load the `transloadit` plugin and configure your Transloadit key and secret:
16
39
 
17
40
  ```rb
18
- gem "shrine"
19
- gem "aws-sdk-s3", "~> 1.2" # for Amazon S3
20
- gem "shrine-transloadit"
41
+ Shrine.plugin :transloadit, auth: {
42
+ key: "YOUR_TRANSLOADIT_KEY",
43
+ secret: "YOUR_TRANSLOADIT_SECRET",
44
+ }
21
45
  ```
22
46
 
23
- ```rb
24
- require "shrine"
25
- require "shrine/storage/s3"
47
+ ### Credentials
26
48
 
27
- s3_options = {
28
- bucket: "my-bucket",
29
- region: "my-region",
30
- access_key_id: "abc",
31
- secret_access_key: "xyz",
32
- }
49
+ You'll need to create [credentials] for the storage services you want to import
50
+ from and export to. Then you need to map these credentials to Shrine storages:
33
51
 
52
+ ```rb
34
53
  Shrine.storages = {
35
- cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
36
- store: Shrine::Storage::S3.new(prefix: "store", **s3_options),
54
+ cache: Shrine::Storage::S3.new(prefix: "cache", **options),
55
+ store: Shrine::Storage::S3.new(**options),
37
56
  }
38
57
 
39
- class TransloaditUploader < Shrine
40
- plugin :transloadit,
41
- auth_key: "your transloadit key",
42
- auth_secret: "your transloadit secret"
43
- end
58
+ Shrine.plugin :transloadit, auth: { ... },
59
+ credentials: {
60
+ cache: :s3_store, # use "s3_store" credentials for :cache storage
61
+ store: :s3_store, # use "s3_store" credentials for :store storage
62
+ }
44
63
  ```
45
64
 
46
- This setup assumes you're doing direct S3 uploads, but you can also do [direct
47
- uploads to Transloadit], or just use any other `:cache` storage which provides
48
- URLs for uploaded files.
49
-
50
- ## How it works
51
-
52
- Transloadit works in a way that you create an "assembly", which contains all
53
- information about how the file(s) should be processed, from import to export.
54
- Processing itself happens asynchronously, and you can give Transloadit a URL
55
- which it will POST results to when processing finishes.
65
+ ### Derivatives
56
66
 
57
- This plugin allows you to easily implement this webhook flow. You can intercept
58
- promoting, and submit a Transloadit assembly using the cached file, along with
59
- a URL to the route in your app where you'd like Transloadit to POST the results
60
- of processing. Then you can call the plugin again in the route to save the
61
- results to your attachment column.
67
+ The examples will assume you have the [`derivatives`][derivatives] plugin
68
+ loaded:
62
69
 
63
- The **[demo app]** shows a complete implementation of this flow, and can serve
64
- as a good baseline for your own implementation.
70
+ ```rb
71
+ Shrine.plugin :derivatives
72
+ ```
65
73
 
66
74
  ## Usage
67
75
 
68
- We loaded the `transloadit` plugin in a `TransloaditUploader` base uploader
69
- class, so that any uploaders that you want to do Transloadit processing with
70
- can just inherit from that class.
76
+ The `transloadit` plugin provides helper methods for creating [import][import
77
+ robots] and [export][export robots] steps, as well as for parsing out exported
78
+ files from results.
71
79
 
72
- Transloadit assemblies are built inside `#transloadit_process` method in your
73
- uploader, and you can use some convenient helper methods which the plugin
74
- provides.
80
+ Here is a basic example where we kick off transcoding and thumbnail extraction
81
+ from an attached video, wait for assembly to complete, then save processed
82
+ files as derivatives:
75
83
 
76
84
  ```rb
77
- class ImageUploader < TransloaditUploader # inherit from our Transloadit base uploader class
78
- def transloadit_process(io, context)
79
- resized = transloadit_file(io)
80
- .add_step("resize", "/image/resize", width: 800)
85
+ class VideoUploader < Shrine
86
+ Attacher.transloadit_processor :video do
87
+ import = file.transloadit_import_step
88
+ encode = transloadit_step "encode", "/video/encode", use: import
89
+ thumbs = transloadit_step "thumbs", "/video/thumbs", use: import
90
+ export = store.transloadit_export_step use: [encode, thumbs]
91
+
92
+ assembly = transloadit.assembly(steps: [import, encode, thumbs, export])
93
+ assembly.create!
94
+ end
81
95
 
82
- transloadit_assembly(resized, context: context)
96
+ Attacher.transloadit_saver :video do |response|
97
+ transcoded = store.transloadit_file(response["results"]["encode"])
98
+ thumbnails = store.transloadit_files(response["results"]["thumbs"])
99
+
100
+ merge_derivatives(transcoded: transcoded, thumbnails: thumbnails)
83
101
  end
84
102
  end
85
103
  ```
104
+ ```rb
105
+ response = attacher.transloadit_process(:video)
106
+ response.reload_until_finished!
86
107
 
87
- These helper methods just provide a higher-level interface over the
88
- [transloadit gem], which you might want look at to get a better understanding
89
- of how building assemblies works.
108
+ if response.error?
109
+ # handle error
110
+ end
90
111
 
91
- In short, in Transloadit every action, be it import, processing, or export, is
92
- a "step". Each step is defined by its [robot and arguments], and needs to have
93
- a *unique name*. Transloadit allows you to define the entire processing flow
94
- (which can result in multiple files) as a collection of steps, which is called
95
- an "assembly". Once the assembly is built it can be submitted to Transloadit.
112
+ attacher.transloadit_save(:video, response)
113
+ attacher.derivatives #=>
114
+ # {
115
+ # transcoded: #<Shrine::UploadedFile storage_key=:store ...>,
116
+ # thumbnails: [
117
+ # #<Shrine::UploadedFile storage_key=:store ...>,
118
+ # #<Shrine::UploadedFile storage_key=:store ...>,
119
+ # ...
120
+ # ]
121
+ # }
122
+ ```
96
123
 
97
- ### Versions
124
+ ### Backgrounding
98
125
 
99
- With Transloadit you can create multiple files in a single assembly, and this
100
- plugin allows you to leverage that in form of a hash of versions.
126
+ When using [backgrounding], it's probably best to create the assembly after
127
+ promotion:
101
128
 
102
129
  ```rb
103
- class ImageUploader < TransloaditUploader
104
- plugin :versions
130
+ class PromoteJob
131
+ def perform(record, name, file_data)
132
+ attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)
133
+ attacher.atomic_promote
134
+ attacher.transloadit_process(:video)
135
+ # ...
136
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
137
+ end
138
+ end
139
+ ```
105
140
 
106
- def transloadit_process(io, context)
107
- original = transloadit_file(io)
108
- medium = original.add_step("resize_500", "/image/resize", width: 500)
109
- small = original.add_step("resize_300", "/image/resize", width: 300)
141
+ ## Notifications
110
142
 
111
- files = { original: original, medium: medium, small: small }
143
+ When using [assembly notifications], the attacher data can be sent to the
144
+ webhook via `:fields`:
112
145
 
113
- transloadit_assembly(files, context: context)
114
- end
146
+ ```rb
147
+ Attacher.transloadit_processor :video do
148
+ # ...
149
+ assembly = transloadit.assembly(
150
+ steps: [ ... ],
151
+ notify_url: "https://example.com/webhooks/transloadit",
152
+ fields: {
153
+ attacher: {
154
+ record_class: record.class,
155
+ record_id: record.id,
156
+ name: name,
157
+ data: file_data,
158
+ }
159
+ }
160
+ )
161
+ assembly.create!
115
162
  end
116
163
  ```
117
164
 
118
- ### Multiple files
165
+ Then in the webhook handler we can load the attacher and [atomically
166
+ persist][atomic_helpers] assembly results. If during processing the attachment
167
+ has changed or record was deleted, we make sure we delete processed files.
119
168
 
120
- Some Transloadit robots might produce multiple files, e.g. `/video/adaptive` or
121
- `/document/thumbs`. By default, shrine-transloadit will raise an error when a
122
- step returned more than one file, but it's possible to specify the result
123
- format in which shrine-transloadit should save the processed files.
169
+ ```rb
170
+ post "/transloadit/video" do
171
+ Shrine.transloadit_verify!(params) # verify transloadit signature
124
172
 
125
- #### `list`
173
+ response = JSON.parse(params["transloadit"])
126
174
 
127
- The `list` format means that the processed files will be saved as a flat list.
175
+ record_class, record_id, name, file_data = response["fields"]["attacher"].values
176
+ record_class = Object.const_get(record_class)
128
177
 
129
- ```rb
130
- class PdfUploader < TransloaditUploader
131
- def transloadit_process(io, context)
132
- thumbs = transloadit_file(io)
133
- .add_step("thumbs", "/document/thumbs", ...)
134
- .multiple(:list) # marks that the result of this pipeline should be saved as a list
178
+ begin
179
+ record = record_class.find(record_id)
180
+ attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)
135
181
 
136
- transloadit_assembly(thumbs)
182
+ attacher.transloadit_save(:video, response)
183
+
184
+ attacher.atomic_persist
185
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
186
+ unless attacher
187
+ attacher = record_class.send(:"#{name}_attacher")
188
+ attacher.transloadit_save(:video, response)
189
+ end
190
+
191
+ attacher.destroy(background: true) # delete orphaned files
137
192
  end
193
+
194
+ # return successful response for Transloadit
195
+ status 200
138
196
  end
139
197
  ```
140
198
 
141
- This will make the processing results save as an array of files:
199
+ Note that if you have CSRF protection, make sure that you skip verifying the
200
+ CSRF token for this route.
201
+
202
+ ## Direct uploads
142
203
 
143
- ```rb
144
- pdf.file #=>
145
- # [
146
- # #<Shrine::UploadedFile ...>
147
- # #<Shrine::UploadedFile ...>
148
- # ...
149
- # ]
204
+ Transloadit supports client side uploads via [Robodog], an [Uppy]-based
205
+ JavaScript library.
150
206
 
151
- pdf.file[0] # page 1
207
+ If you have an HTML form, you can use Robodog's [Form API][Robodog Form] to add
208
+ Transloadit's encoding capabilities to it:
209
+
210
+ ```js
211
+ window.Robodog.form('form#myform', {
212
+ params: {
213
+ auth: { key: 'YOUR_TRANSLOADIT_KEY' },
214
+ template_id: 'YOUR_TEMPLATE_ID',
215
+ },
216
+ waitForEncoding: true,
217
+ // ...
218
+ })
152
219
  ```
153
220
 
154
- ### Webhooks
221
+ With the above setup, Robodog will send the assembly results to your controller
222
+ in the `transloadit` param, which we can parse out and save to our record. See
223
+ the [demo app] for an example of doing this.
224
+
225
+ ## Promotion
155
226
 
156
- Transloadit performs its processing asynchronously, and you can provide a URL
157
- where you want Transloadit to POST results of processing once it's finished.
227
+ If you want Transloadit to also upload your cached original file to permanent
228
+ storage, you can skip promotion on the Shrine side:
158
229
 
159
230
  ```rb
160
- class ImageUploader < TransloaditUploader
161
- def transloadit_process(io, context)
162
- # ...
163
- transloadit_assembly(files, notify_url: "http://myapp.com/webhooks/transloadit")
231
+ class VideoUploader < Shrine
232
+ Attacher.transloadit_processor :video do
233
+ import = file.transloadit_import_step
234
+ encode = transloadit_step "encode", "/video/encode", use: import
235
+ thumbs = transloadit_step "thumbs", "/video/thumbs", use: import
236
+ export = store.transloadit_export_step use: [import, encode, thumbs] # include original
237
+
238
+ assembly = transloadit.assembly(use: [import, encode, thumbs, export])
239
+ assembly.create!
240
+ end
241
+
242
+ Attacher.transloadit_saver :video do |response|
243
+ stored = store.transloadit_file(response["results"]["import"])
244
+ transcoded = store.transloadit_file(response["results"]["encode"])
245
+ thumbnails = store.transloadit_files(response["results"]["thumbs"])
246
+
247
+ set(stored) # set promoted file
248
+ merge_derivatives(transcoded: transcoded, thumbnails: thumbnails)
164
249
  end
165
250
  end
166
251
  ```
252
+ ```rb
253
+ class PromoteJob
254
+ def perform(record, name, file_data)
255
+ attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)
167
256
 
168
- Then in your `POST /webhooks/transloadit` route you can call the plugin to
169
- automatically save the results to the attachment column in Shrine's format.
257
+ response = attacher.transloadit_process(:video)
258
+ response.reload_until_finished!
170
259
 
171
- ```rb
172
- post "/webhooks/transloadit" do
173
- TransloaditUploader::Attacher.transloadit_save(params)
174
- # return 200 status
260
+ if response.error?
261
+ # handle error
262
+ end
263
+
264
+ original_file = attacher.file
265
+ attacher.transloadit_save(:video, response)
266
+
267
+ attacher.atomic_persist(original_file)
268
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
269
+ # delete orphaned processed files (backgrounding plugin is recommended)
270
+ attacher.destroy(background: true) if attacher
271
+ end
175
272
  end
176
273
  ```
177
274
 
178
- Note that if you have CSRF protection, make sure that you skip verifying the
179
- CSRF token for this route.
180
-
181
- ### Direct uploads
275
+ ## Skipping exports
182
276
 
183
- Transloadit supports direct uploads, allowing you to do some processing on the
184
- file before it's submitted to the app. It's recommended that you use [Uppy] for
185
- client side uploads, take a look its [Transloadit plugin][uppy transloadit] for
186
- more details.
277
+ If you want to use Transloadit only for processing, and prefer to store results
278
+ to yourself, you can do so with help of the [shrine-url] gem.
187
279
 
188
- ```js
189
- // https://uppy.io/docs/transloadit/
190
- var uppy = Uppy.Core({})
191
- .use(Uppy.FileInput, {
192
- target: fileInput.parentNode,
193
- allowMultipleFiles: fileInput.multiple
194
- })
195
- .use(Uppy.Tus, {})
196
- .use(Uppy.Transloadit, {
197
- waitForEncoding: true,
198
- params: {
199
- auth: { key: 'YOUR_TRANSLOADIT_KEY' },
200
- steps: {
201
- // ...
202
- }
203
- }
204
- })
280
+ ```rb
281
+ # Gemfile
282
+ gem "shrine-url"
283
+ ```
284
+ ```rb
285
+ # ...
286
+ require "shrine/storage/url"
205
287
 
206
- uppy.run()
288
+ Shrine.storages = {
289
+ # ...
290
+ url: Shrine::Storage::Url.new,
291
+ }
207
292
  ```
208
293
 
209
- When direct upload finishes Transloadit will return processing results, which
210
- you can use to construct the Shrine uploaded file data and send it as the
211
- Shrine attachment. Let's assume that we didn't provide and export step, in
212
- which case Transloadit will return temporary URL to the processed files, which
213
- we can use as the uploaded file identifier:
294
+ If you don't specify an export step, Transloadit will return processed files
295
+ uploaded to Transloadit's temporary storage. You can load these results using
296
+ the `:url` storage, and then upload them to your permanent storage:
214
297
 
215
- ```js
216
- uppy.on('transloadit:result', function (stepName, result) {
217
- var uploadedFileData = JSON.stringify({
218
- id: result['ssl_url'],
219
- storage: 'cache',
220
- metadata: {
221
- size: result['size'],
222
- filename: result['name'],
223
- mime_type: result['mime'],
224
- width: result['meta'] && result['meta']['width'],
225
- height: result['meta'] && result['meta']['height'],
226
- transloadit: result['meta'],
227
- }
228
- })
298
+ ```rb
299
+ class VideoUploader < Shrine
300
+ Attacher.transloadit_processor :video do
301
+ import = file.transloadit_import_step
302
+ encode = transloadit_step "encode", "/video/encode", use: import
303
+ thumbs = transloadit_step "thumbs", "/video/thumbs", use: import
304
+ # no export step
305
+
306
+ assembly = transloadit.assembly(steps: [import, encode, thumbs])
307
+ assembly.create!
308
+ end
229
309
 
230
- // send `uploadedFileData` as the Shrine attachment
231
- })
232
- ```
310
+ Attacher.transloadit_saver :video do |response|
311
+ url = shrine_class.new(:url)
312
+ transcoded = url.transloadit_file(response["results"]["encode"])
313
+ thumbnails = url.transloadit_files(response["results"]["thumbs"])
233
314
 
234
- In order for using an URL as the uploaded file identifier to work, you'll need
235
- to set [shrine-url] as your temporary storage:
315
+ # results are uploaded to Transloadit's temporary storage
316
+ transcoded #=> #<Shrine::UploadedFile @storage_key=:url @id="https://tmp.transloadit.com/..." ...>
317
+ thumbnails #=> [#<Shrine::UploadedFile @storage_key=:url @id="https://tmp.transloadit.com/..." ...>, ...]
236
318
 
319
+ # upload results to permanent storage
320
+ add_derivatives(transcoded: transcoded, thumbnails: thumbnails)
321
+ end
322
+ end
323
+ ```
237
324
  ```rb
238
- gem "shrine-url"
325
+ response = attacher.transloadit_process(:video)
326
+ response.reload_until_finished!
327
+
328
+ if response.error?
329
+ # handle error
330
+ end
331
+
332
+ attacher.transloadit_save(:video, response)
333
+ attacher.derivatives #=>
334
+ # {
335
+ # transcoded: #<Shrine::UploadedFile storage_key=:store ...>,
336
+ # thumbnails: [
337
+ # #<Shrine::UploadedFile storage_key=:store ...>,
338
+ # #<Shrine::UploadedFile storage_key=:store ...>,
339
+ # ...
340
+ # ]
341
+ # }
239
342
  ```
240
343
 
344
+ ## API
345
+
346
+ ### Processor
347
+
348
+ The processor is just a block registered under an identifier, which is expected
349
+ to create a Transloadit assembly:
350
+
241
351
  ```rb
242
- require "shrine/storage/url"
243
- Shrine.storages[:cache] = Shrine::Storage::Url.new
352
+ class VideoUploader < Shrine
353
+ Attacher.transloadit_processor :video do
354
+ # ...
355
+ end
356
+ end
244
357
  ```
245
358
 
246
- See the **[demo app]** for a complete example of direct uploads.
247
-
248
- ### Templates
359
+ It is executed when `Attacher#transloadit_process` is called:
249
360
 
250
- Transloadit recommends using [templates], since they allow you to replay failed
251
- assemblies, and also allow you not to expose credentials in your HTML.
361
+ ```rb
362
+ attacher.transloadit_process(:video) # calls :video processor
363
+ ```
252
364
 
253
- Here is an example where the whole processing is defined inside a template,
254
- and we just set the location of the imported file.
365
+ Any arguments passed to the processor will be given to the block:
255
366
 
256
367
  ```rb
257
- # Your Transloadit template saved as "my_template"
258
- {
259
- steps: {
260
- resize: {
261
- robot: "/image/resize",
262
- use: "import", # the "import" step will be passed in
263
- width: 800
264
- },
265
- export: {
266
- robot: "/s3/store",
267
- use: "resize",
268
- bucket: "YOUR_AWS_BUCKET",
269
- key: "YOUR_AWS_KEY",
270
- secret: "YOUR_AWS_SECRET",
271
- bucket_region: "YOUR_AWS_REGION",
272
- path: "videos/${unique_prefix}/${file.url_name}"
273
- }
274
- }
275
- }
368
+ attacher.transloadit_process(:video, foo: "bar")
276
369
  ```
277
370
  ```rb
278
- class ImageUplaoder < TransloaditUploader
279
- def transloadit_process(io, context)
280
- import = transloadit_import_step("import", io)
281
- transloadit_assembly("my_template", steps: [import])
371
+ class VideoUploader < Shrine
372
+ Attacher.transloadit_processor :video do |options|
373
+ options #=> { :foo => "bar" }
282
374
  end
283
375
  end
284
376
  ```
285
377
 
286
- ### Backgrounding
287
-
288
- Even though submitting a Transloadit assembly doesn't require any uploading, it
289
- still does two HTTP requests, so you might want to put them into a background
290
- job. You can configure that in the `TransloaditUploader` base uploader class:
378
+ The processor block is executed in context of a `Shrine::Attacher` instance:
291
379
 
292
380
  ```rb
293
- class TransloaditUploader < Shrine
294
- plugin :transloadit,
295
- auth_key: "your transloadit key",
296
- auth_secret: "your transloadit secret"
381
+ class VideoUploader < Shrine
382
+ Attacher.transloadit_processor :video do
383
+ self #=> #<Shrine::Attacher>
297
384
 
298
- Attacher.promote { |data| TransloaditJob.perform_async(data) }
385
+ record #=> #<Video>
386
+ name #=> :file
387
+ file #=> #<Shrine::UploadedFile>
388
+ end
299
389
  end
300
390
  ```
301
- ```rb
302
- class TransloaditJob
303
- include Sidekiq::Worker
304
391
 
305
- def perform(data)
306
- TransloaditUploader::Attacher.transloadit_process(data)
392
+ ### Saver
393
+
394
+ The saver is just a block registered under an identifier, which is expected to
395
+ save given Transloadit results into the attacher:
396
+
397
+ ```rb
398
+ class VideoUploader < Shrine
399
+ Attacher.transloadit_saver :video do |results|
400
+ # ...
307
401
  end
308
402
  end
309
403
  ```
310
404
 
311
- ### Tracking progress
405
+ It is executed when `Attacher#transloadit_save` is called:
406
+
407
+ ```rb
408
+ attacher.transloadit_save(:video, results) # calls :video saver
409
+ ```
312
410
 
313
- When an assembly is submitted, Transloadit returns a lot of useful information
314
- about the status of that assembly, which the plugin saves to the cached
315
- attachment's metadata.
411
+ Any arguments passed to the saver will be given to the block:
316
412
 
317
413
  ```rb
318
- response = photo.image.transloadit_response
319
- response.body #=>
320
- # {
321
- # "ok" => "ASSEMBLY_EXECUTING",
322
- # "message" => "The assembly is currently being executed.",
323
- # "assembly_id" => "83d07d10414011e68cc8c5df79919836",
324
- # "assembly_url" => "http://api2.janani.transloadit.com/assemblies/83d07d10414011e68cc8c5df79919836",
325
- # "execution_start" => "2016/07/03 17:06:42 GMT",
326
- # "execution_duration" => 2.113,
327
- # "params" => "{\"steps\":{...}}",
328
- # ...
329
- # }
414
+ attacher.transloadit_save(:video, results, foo: "bar")
415
+ ```
416
+ ```rb
417
+ class VideoUploader < Shrine
418
+ Attacher.transloadit_saver :video do |results, options|
419
+ options #=> { :foo => "bar" }
420
+ end
421
+ end
330
422
  ```
331
423
 
332
- At an point during the execution of the assembly you can refresh this
333
- information:
424
+ The saver block is executed in context of a `Shrine::Attacher` instance:
334
425
 
335
426
  ```rb
336
- response.finished? #=> false
337
- response.reload!
338
- response.finished? #=> true
427
+ class VideoUploader < Shrine
428
+ Attacher.transloadit_saver :video do |results|
429
+ self #=> #<Shrine::Attacher>
430
+
431
+ record #=> #<Video>
432
+ name #=> :file
433
+ file #=> #<Shrine::UploadedFile>
434
+ end
435
+ end
339
436
  ```
340
437
 
341
- ### Metadata
438
+ ### Step
342
439
 
343
- For each processed file Transloadit also extracts a great deal of useful
344
- metadata. When the Transloadit processing is finished and the results are saved
345
- as a Shrine attachment, this metadata will be automatically used to populate
346
- the attachment's metadata.
440
+ You can generate `Transloadit::Step` objects with `Shrine.transloadit_step`:
347
441
 
348
- Additionally the Transloadit's metadata hash will be saved in an additional
349
- metadata key, so that you can access any other values:
442
+ ```rb
443
+ Shrine.transloadit_step "my_name", "/my/robot", **options
444
+ #=> #<Transloadit::Step name="my_name", robot="/my/robot", options={...}>
445
+ ```
446
+
447
+ This method adds the ability to pass another `Transloadit::Step` object as the
448
+ `:use` parameter:
350
449
 
351
450
  ```rb
352
- photo = Photo.create(image: image_file)
353
- photo.image.metadata["transloadit"] #=>
354
- # {
355
- # "date_recorded" => "2013/09/04 08:03:39",
356
- # "date_file_created" => "2013/09/04 12:03:39 GMT",
357
- # "date_file_modified" => "2016/07/11 02:27:11 GMT",
358
- # "aspect_ratio" => "1.504",
359
- # "city" => "Decatur",
360
- # "state" => "Georgia",
361
- # "country" => "United States",
362
- # "latitude" => 33.77519301,
363
- # "longitude" => -84.295608,
364
- # "orientation" => "Horizontal (normal)",
365
- # "colorspace" => "RGB",
366
- # "average_color" => "#8b8688",
367
- # ...
368
- # }
451
+ step_one = Shrine.transloadit_step "one", "/robot/one"
452
+ step_two = Shrine.transloadit_step "two", "/robot/two", use: step_one
453
+ step_two.options[:use] #=> ["one"]
369
454
  ```
370
455
 
371
- ### Import & Export
456
+ ### Import step
372
457
 
373
- Every `TransloaditFile` needs to have an import and an export step. This plugin
374
- automatically generates those steps for you:
458
+ The `Shrine::UploadedFile#transloadit_import_step` method generates an import
459
+ step for the uploaded file:
375
460
 
376
461
  ```rb
377
- transloadit_file(io)
462
+ file = Shrine.upload(io, :store)
463
+ file.storage #=> #<Shrine::Storage::S3>
464
+ file.id #=> "foo"
378
465
 
379
- # is equivalent to
466
+ step = file.transloadit_import_step
380
467
 
381
- file = transloadit_file
382
- file.add_step(transloadit_import_step("import", io))
468
+ step #=> #<Transloadit::Step ...>
469
+ step.name #=> "import"
470
+ step.robot #=> "/s3/import"
471
+
472
+ step.options[:path] #=> "foo"
473
+ step.options[:credentials] #=> :s3_store (inferred from the plugin setting)
383
474
  ```
384
475
 
476
+ You can change the default step name:
477
+
385
478
  ```rb
386
- transloadit_assembly({ original: original, thumb: thumb })
479
+ step = file.transloadit_import_step("my_import")
480
+ step.name #=> "my_import"
481
+ ```
387
482
 
388
- # is equivalent to
483
+ You can also pass step options:
389
484
 
390
- transloadit_assembly({
391
- original: original.add_step(transloadit_export_step("export_original")),
392
- thumb: thumb.add_step(transloadit_export_step("export_thumb")),
393
- })
485
+ ```rb
486
+ step = file.transloadit_import_step(ignore_errors: ["meta"])
487
+ step.options[:ignore_errors] #=> ["meta"]
394
488
  ```
395
489
 
396
- If you want/need to generate these steps yourself, you can just use the
397
- expanded forms.
490
+ The following import robots are currently supported:
398
491
 
399
- ### Errors
492
+ | Robot | Description |
493
+ | :----------- | :---------- |
494
+ | `/s3/import` | activated for `Shrine::Storage::S3` |
495
+ | `/http/import` | activated for any other storage which returns HTTP(S) URLs |
496
+ | `/ftp/import` | activated for any other storage which returns FTP URLs |
400
497
 
401
- Any errors that happen on assembly submission or during processing will be
402
- propagated as a `Shrine::Plugins::Transloadit::ResponseError`, which
403
- additionally carries the Transloadit response in the `#response` attribute.
498
+ ### Export step
499
+
500
+ The `Shrine#transloadit_export_step` method generates an export step for the underlying
501
+ storage:
404
502
 
405
503
  ```rb
406
- post "/webhooks/transloadit" do
407
- begin
408
- TransloaditUploader::Attacher.transloadit_save(params)
409
- rescue Shrine::Plugins::Transloadit::ResponseError => exception
410
- exception.response # include this response in your exception notification
411
- end
412
- # ...
413
- end
504
+ uploader = Shrine.new(:store)
505
+ uploader.storage #=> #<Shrine::Storage::S3>
506
+
507
+ step = uploader.transloadit_export_step
508
+
509
+ step #=> #<Transloadit::Step ...>
510
+ step.name #=> "export"
511
+ step.robot #=> "/s3/store"
512
+
513
+ step.options[:credentials] #=> :s3_store (inferred from the plugin setting)
414
514
  ```
415
515
 
416
- ### Testing
516
+ You can change the default step name:
417
517
 
418
- In development or test environment you cannot use webhooks, because Transloadit
419
- as an external service cannot access your localhost. In this case you can just
420
- do polling:
518
+ ```rb
519
+ step = uploader.transloadit_export_step("my_export")
520
+ step.name #=> "my_export"
521
+ ```
522
+
523
+ You can also pass step options:
421
524
 
422
525
  ```rb
423
- class MyUploader < TransloaditUploader
424
- def transloadit_process(io, context)
425
- # ...
526
+ step = file.transloadit_export_step(acl: "public-read")
527
+ step.options[:acl] #=> "public-read"
528
+ ```
426
529
 
427
- if ENV["RACK_ENV"] == "production"
428
- notify_url = "https://myapp.com/webhooks/transloadit"
429
- else
430
- # In development we cannot receive webhooks, because Transloadit as an
431
- # external service cannot reach our localhost.
432
- end
530
+ The following export robots are currently supported:
433
531
 
434
- transloadit_assembly(files, context: context, notify_url: notify_url)
435
- end
436
- end
532
+ | Robot | Description |
533
+ | :---- | :---------- |
534
+ | `/s3/store` | activated for `Shrine::Storage::S3` |
535
+ | `/google/store` | activated for [`Shrine::Storage::GoogleCloudStorage`][shrine-gcs] |
536
+ | `/youtube/store` | activated for [`Shrine::Storage::YouTube`][shrine-youtube] |
537
+
538
+ ### File
539
+
540
+ The `Shrine#transloadit_file` method will convert a Transloadit result hash
541
+ into a `Shrine::UploadedFile` object:
542
+
543
+ ```rb
544
+ uploader = Shrine.new(:store)
545
+ uploader.storage #=> #<Shrine::Storage::S3>
546
+
547
+ file = uploader.transloadit_file(
548
+ "url" => "https://my-bucket.s3.amazonaws.com/foo",
549
+ # ...
550
+ )
551
+
552
+ file.storage #=> #<Shrine::Storage::S3>
553
+ file.id #=> "foo"
437
554
  ```
438
555
 
556
+ It will include basic metadata:
557
+
439
558
  ```rb
440
- class TransloaditJob
441
- include Sidekiq::Worker
559
+ file = uploader.transloadit_file(
560
+ # ...
561
+ "name" => "matrix.mp4",
562
+ "size" => 44198,
563
+ "mime" => "video/mp4",
564
+ )
565
+
566
+ file.original_filename #=> "matrix.mp4"
567
+ file.size #=> 44198
568
+ file.mime_type #=> "video/mp4"
569
+ ```
442
570
 
443
- def perform(data)
444
- attacher = TransloaditUploader::Attacher.transloadit_process(data)
571
+ It will also merge any custom metadata:
445
572
 
446
- # Webhooks won't work in development, so we can just use polling.
447
- unless ENV["RACK_ENV"] == "production"
448
- response = attacher.get.transloadit_response
449
- response.reload_until_finished!
450
- attacher.transloadit_save(response.body)
451
- end
452
- end
453
- end
573
+ ```rb
574
+ file = uploader.transloadit_file(
575
+ # ...
576
+ "meta" => { "duration" => 9000, ... },
577
+ )
578
+
579
+ file["duration"] #=> 9000
454
580
  ```
455
581
 
456
- ## Contributing
582
+ Currently only `Shrine::Stroage::S3` is supported. However, you can still
583
+ handle other remote files using [`Shrine::Storage::Url`][shrine-url]:
457
584
 
458
- Before you can run tests, you need to first create an `.env` file in the
459
- project root containing your Transloadit and Amazon S3 credentials:
585
+ ```rb
586
+ Shrine.storages => {
587
+ # ...
588
+ url: Shrine::Storage::Url.new,
589
+ }
590
+ ```
591
+ ```rb
592
+ uploader = Shrine.new(:url)
593
+ uploader #=> #<Shrine::Storage::Url>
460
594
 
461
- ```sh
462
- # .env
463
- TRANSLOADIT_KEY="..."
464
- TRANSLOADIT_SECRET="..."
465
- S3_BUCKET="..."
466
- S3_REGION="..."
467
- S3_ACCESS_KEY_ID="..."
468
- S3_SECRET_ACCESS_KEY="..."
595
+ file = uploader.transloadit_file(
596
+ "url" => "https://example.com/foo",
597
+ # ...
598
+ )
599
+
600
+ file.id #=> "https://example.com/foo"
469
601
  ```
470
602
 
471
- Afterwards you can run the tests:
603
+ ## Contributing
604
+
605
+ Tests are run with:
472
606
 
473
607
  ```sh
474
608
  $ bundle exec rake test
@@ -480,12 +614,18 @@ $ bundle exec rake test
480
614
 
481
615
  [Shrine]: https://github.com/shrinerb/shrine
482
616
  [Transloadit]: https://transloadit.com/
483
- [many storage services]: https://transloadit.com/docs/conversion-robots/#file-export-robots
484
- [transloadit gem]: https://github.com/transloadit/ruby-sdk
485
- [robot and arguments]: https://transloadit.com/docs/conversion-robots/
486
- [templates]: https://transloadit.com/docs/#templates
487
- [Uppy]: https://uppy.io
488
- [uppy transloadit]: https://uppy.io/docs/transloadit/
489
- [demo app]: /demo
490
- [direct uploads to Transloadit]: #direct-uploads
617
+ [Ruby SDK]: https://github.com/transloadit/ruby-sdk
618
+ [credentials]: https://transloadit.com/docs/#16-template-credentials
619
+ [import robots]: https://transloadit.com/docs/transcoding/#overview-service-file-importing
620
+ [export robots]: https://transloadit.com/docs/transcoding/#overview-service-file-exporting
621
+ [derivatives]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/derivatives.md#readme
622
+ [assembly notifications]: https://transloadit.com/docs/#24-assembly-notifications
623
+ [backgrounding]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/backgrounding.md#readme
624
+ [shrine-url]: https://github.com/shrinerb/shrine-url
625
+ [Robodog]: https://uppy.io/docs/robodog/
626
+ [Robodog Form]: https://uppy.io/docs/robodog/form/
627
+ [Uppy]: https://uppy.io/
628
+ [atomic_helpers]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/atomic_helpers.md#readme
629
+ [shrine-gcs]: https://github.com/renchap/shrine-google_cloud_storage
630
+ [shrine-youtube]: https://github.com/thedyrt/shrine-storage-you_tube
491
631
  [shrine-url]: https://github.com/shrinerb/shrine-url