{"id":17709240,"url":"https://github.com/shrinerb/shrine-transloadit","last_synced_at":"2026-02-13T09:16:16.775Z","repository":{"id":44910916,"uuid":"62530900","full_name":"shrinerb/shrine-transloadit","owner":"shrinerb","description":"Transloadit integration for Shrine","archived":false,"fork":false,"pushed_at":"2022-01-19T07:24:18.000Z","size":248,"stargazers_count":13,"open_issues_count":1,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-03T18:50:59.198Z","etag":null,"topics":["asynchronous","processing","ruby","shrine","transloadit","webhooks"],"latest_commit_sha":null,"homepage":"https://transloadit.com/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shrinerb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-04T04:14:07.000Z","updated_at":"2022-10-24T10:06:51.000Z","dependencies_parsed_at":"2022-09-05T13:11:05.927Z","dependency_job_id":null,"html_url":"https://github.com/shrinerb/shrine-transloadit","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/shrinerb/shrine-transloadit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-transloadit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-transloadit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-transloadit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-transloadit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shrinerb","download_url":"https://codeload.github.com/shrinerb/shrine-transloadit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-transloadit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276888954,"owners_count":25722745,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-25T02:00:09.612Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","processing","ruby","shrine","transloadit","webhooks"],"created_at":"2024-10-25T03:05:55.163Z","updated_at":"2025-09-25T09:06:23.188Z","avatar_url":"https://github.com/shrinerb.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shrine::Plugins::Transloadit\n\nProvides [Transloadit] integration for [Shrine], using its [Ruby SDK].\n\nTransloadit is a service that helps you handle file uploads, resize, crop and\nwatermark your images, make GIFs, transcode your videos, extract thumbnails,\ngenerate audio waveforms and more.\n\n## Contents\n\n* [Installation](#installation)\n* [Setup](#setup)\n* [Usage](#usage)\n* [Notifications](#notifications)\n* [Direct uploads](#direct-uploads)\n* [Promotion](#promotion)\n* [Skipping exports](#skipping-exports)\n* [API](#api)\n  - [Processor](#processor)\n  - [Saver](#saver)\n  - [Step](#step)\n    - [Import step](#import-step)\n    - [Export step](#export-step)\n  - [File](#file)\n* [Instrumentation](#instrumentation)\n\n## Installation\n\nPut the gem in your Gemfile:\n\n```rb\n# Gemfile\ngem \"shrine-transloadit\", \"~\u003e 1.0\"\n```\n\n## Setup\n\nYou'll first need to create [credentials] for the storage service you want to\nimport from and export to. Let's assume you're using S3 and have named the\ncredentials `s3_store`. Now you can load the `transloadit` plugin, providing\nTransloadit key \u0026 secret, and mapping credentials to Shrine storages:\n\n```rb\n# example storage configuration\nShrine.storages = {\n  cache: Shrine::Storage::S3.new(prefix: \"cache\", **options),\n  store: Shrine::Storage::S3.new(**options),\n}\n\n# transloadit plugin configuration\nShrine.plugin :transloadit,\n  auth: {\n    key:    \"YOUR_TRANSLOADIT_KEY\",\n    secret: \"YOUR_TRANSLOADIT_SECRET\",\n  },\n  credentials: {\n    cache: :s3_store, # use \"s3_store\" credentials for :cache storage\n    store: :s3_store, # use \"s3_store\" credentials for :store storage\n  }\n\n# for storing processed files\nShrine.plugin :derivatives\n```\n\n## Usage\n\nThe `transloadit` plugin provides helper methods for creating [import][import\nrobots] and [export][export robots] steps, as well as for parsing out exported\nfiles from results.\n\nHere is a basic example where we kick off transcoding and thumbnail extraction\nfrom an attached video, wait for assembly to complete, then save processed\nfiles as derivatives:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor do\n    import = file.transloadit_import_step\n    encode = transloadit_step \"encode\", \"/video/encode\", use: import\n    thumbs = transloadit_step \"thumbs\", \"/video/thumbs\", use: import\n    export = store.transloadit_export_step use: [encode, thumbs]\n\n    assembly = transloadit.assembly(steps: [import, encode, thumbs, export])\n    assembly.create!\n  end\n\n  Attacher.transloadit_saver do |results|\n    transcoded = store.transloadit_file(results[\"encode\"])\n    thumbnails = store.transloadit_files(results[\"thumbs\"])\n\n    merge_derivatives(transcoded: transcoded, thumbnails: thumbnails)\n  end\nend\n```\n```rb\nresponse = attacher.transloadit_process\nresponse.reload_until_finished!\n\nif response.error?\n  # handle error\nend\n\nattacher.transloadit_save(response[\"results\"])\nattacher.derivatives #=\u003e\n# {\n#   transcoded: #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#   thumbnails: [\n#     #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#     #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#     ...\n#   ]\n# }\n```\n\n### Backgrounding\n\nWhen using [backgrounding], it's probably best to create the assembly after\npromotion:\n\n```rb\nShrine.plugin :backgrounding\nShrine::Attacher.promote_block do\n  PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)\nend\n```\n```rb\nclass PromoteJob\n  include Sidekiq::Worker\n\n  def perform(attacher_class, record_class, record_id, name, file_data)\n    attacher_class = Object.const_get(attacher_class)\n    record         = Object.const_get(record_class).find(record_id) # if using Active Record\n\n    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)\n    attacher.atomic_promote\n    attacher.transloadit_process\n    # ...\n  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound\n  end\nend\n```\n\n## Notifications\n\nWhen using [assembly notifications], the attacher data can be sent to the\nwebhook via `:fields`:\n\n```rb\nAttacher.transloadit_processor do\n  # ...\n  assembly = transloadit.assembly(\n    steps:      [ ... ],\n    notify_url: \"https://example.com/webhooks/transloadit\",\n    fields:     {\n      attacher: {\n        record_class: record.class,\n        record_id:    record.id,\n        name:         name,\n        data:         file_data,\n      }\n    }\n  )\n  assembly.create!\nend\n```\n\nThen in the webhook handler we can load the attacher and [atomically\npersist][atomic_helpers] assembly results. If during processing the attachment\nhas changed or record was deleted, we make sure we delete processed files.\n\n```rb\npost \"/transloadit/video\" do\n  Shrine.transloadit_verify!(params) # verify transloadit signature\n\n  response = JSON.parse(params[\"transloadit\"])\n\n  record_class, record_id, name, file_data = response[\"fields\"][\"attacher\"].values\n  record_class = Object.const_get(record_class)\n\n  attacher    = record_class.send(:\"#{name}_attacher\")\n  derivatives = attacher.transloadit_save(response[\"results\"])\n\n  begin\n    record   = record_class.find(record_id)\n    attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)\n\n    attacher.merge_derivatives(derivatives)\n    attacher.atomic_persist\n  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound\n    attacher.destroy_attached # delete orphaned processed files\n  end\n\n  # return successful response for Transloadit\n  status 200\nend\n```\n\nNote that if you have CSRF protection, make sure that you skip verifying the\nCSRF token for this route.\n\n## Direct uploads\n\nTransloadit supports client side uploads via [Robodog], an [Uppy]-based\nJavaScript library.\n\nIf you have an HTML form, you can use Robodog's [Form API][Robodog Form] to add\nTransloadit's encoding capabilities to it:\n\n```js\nwindow.Robodog.form('form#myform', {\n  params: {\n    auth: { key: 'YOUR_TRANSLOADIT_KEY' },\n    template_id: 'YOUR_TEMPLATE_ID',\n  },\n  waitForEncoding: true,\n  // ...\n})\n```\n\nWith the above setup, Robodog will send the assembly results to your controller\nin the `transloadit` param, which we can parse out and save to our record. See\nthe [demo app] for an example of doing this.\n\n## Promotion\n\nIf you want Transloadit to also upload your cached original file to permanent\nstorage, you can skip promotion on the Shrine side:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor do\n    import = file.transloadit_import_step\n    encode = transloadit_step \"encode\", \"/video/encode\", use: import\n    thumbs = transloadit_step \"thumbs\", \"/video/thumbs\", use: import\n    export = store.transloadit_export_step use: [import, encode, thumbs] # include original\n\n    assembly = transloadit.assembly(steps: [import, encode, thumbs, export])\n    assembly.create!\n  end\n\n  Attacher.transloadit_saver do |results|\n    stored     = store.transloadit_file(results[\"import\"])\n    transcoded = store.transloadit_file(results[\"encode\"])\n    thumbnails = store.transloadit_files(results[\"thumbs\"])\n\n    set(stored) # set promoted file\n    merge_derivatives(transcoded: transcoded, thumbnails: thumbnails)\n  end\nend\n```\n```rb\nclass PromoteJob\n  include Sidekiq::Worker\n\n  def perform(attacher_class, record_class, record_id, name, file_data)\n    attacher_class = Object.const_get(attacher_class)\n    record         = Object.const_get(record_class).find(record_id) # if using Active Record\n\n    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)\n\n    response = attacher.transloadit_process\n    response.reload_until_finished!\n\n    if response.error?\n      # handle error\n    end\n\n    attacher.transloadit_save(response[\"results\"])\n    attacher.atomic_persist attacher.uploaded_file(file_data)\n  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound\n    attacher\u0026.destroy_attached # delete orphaned processed files\n  end\nend\n```\n\n## Skipping exports\n\nIf you want to use Transloadit only for processing, and prefer to store results\nto yourself, you can do so with help of the [shrine-url] gem.\n\n```rb\n# Gemfile\ngem \"shrine-url\"\n```\n```rb\n# ...\nrequire \"shrine/storage/url\"\n\nShrine.storages = {\n  # ...\n  url: Shrine::Storage::Url.new,\n}\n```\n\nIf you don't specify an export step, Transloadit will return processed files\nuploaded to Transloadit's temporary storage. You can load these results using\nthe `:url` storage, and then upload them to your permanent storage:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor do\n    import = file.transloadit_import_step\n    encode = transloadit_step \"encode\", \"/video/encode\", use: import\n    thumbs = transloadit_step \"thumbs\", \"/video/thumbs\", use: import\n    # no export step\n\n    assembly = transloadit.assembly(steps: [import, encode, thumbs])\n    assembly.create!\n  end\n\n  Attacher.transloadit_saver do |results|\n    url        = shrine_class.new(:url)\n    transcoded = url.transloadit_file(results[\"encode\"])\n    thumbnails = url.transloadit_files(results[\"thumbs\"])\n\n    # results are uploaded to Transloadit's temporary storage\n    transcoded #=\u003e #\u003cShrine::UploadedFile @storage_key=:url @id=\"https://tmp.transloadit.com/...\" ...\u003e\n    thumbnails #=\u003e [#\u003cShrine::UploadedFile @storage_key=:url @id=\"https://tmp.transloadit.com/...\" ...\u003e, ...]\n\n    # upload results to permanent storage\n    add_derivatives(transcoded: transcoded, thumbnails: thumbnails)\n  end\nend\n```\n```rb\nresponse = attacher.transloadit_process\nresponse.reload_until_finished!\n\nif response.error?\n  # handle error\nend\n\nattacher.transloadit_save(response[\"results\"])\nattacher.derivatives #=\u003e\n# {\n#   transcoded: #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#   thumbnails: [\n#     #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#     #\u003cShrine::UploadedFile storage_key=:store ...\u003e,\n#     ...\n#   ]\n# }\n```\n\n## API\n\n### Processor\n\nThe processor is just a block registered under an identifier, which is expected\nto create a Transloadit assembly:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor :video do\n    # ...\n  end\nend\n```\n\nIt is executed when `Attacher#transloadit_process` is called:\n\n```rb\nattacher.transloadit_process(:video) # calls :video processor\n```\n\nAny arguments passed to the processor will be given to the block:\n\n```rb\nattacher.transloadit_process(:video, foo: \"bar\")\n```\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor :video do |options|\n    options #=\u003e { :foo =\u003e \"bar\" }\n  end\nend\n```\n\nThe processor block is executed in context of a `Shrine::Attacher` instance:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_processor :video do\n    self #=\u003e #\u003cShrine::Attacher\u003e\n\n    record #=\u003e #\u003cVideo\u003e\n    name   #=\u003e :file\n    file   #=\u003e #\u003cShrine::UploadedFile\u003e\n  end\nend\n```\n\n### Saver\n\nThe saver is just a block registered under an identifier, which is expected to\nsave given Transloadit results into the attacher:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_saver :video do |results|\n    # ...\n  end\nend\n```\n\nIt is executed when `Attacher#transloadit_save` is called:\n\n```rb\nattacher.transloadit_save(:video, results) # calls :video saver\n```\n\nAny arguments passed to the saver will be given to the block:\n\n```rb\nattacher.transloadit_save(:video, results, foo: \"bar\")\n```\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_saver :video do |results, options|\n    options #=\u003e { :foo =\u003e \"bar\" }\n  end\nend\n```\n\nThe saver block is executed in context of a `Shrine::Attacher` instance:\n\n```rb\nclass VideoUploader \u003c Shrine\n  Attacher.transloadit_saver :video do |results|\n    self #=\u003e #\u003cShrine::Attacher\u003e\n\n    record #=\u003e #\u003cVideo\u003e\n    name   #=\u003e :file\n    file   #=\u003e #\u003cShrine::UploadedFile\u003e\n  end\nend\n```\n\n### Step\n\nYou can generate `Transloadit::Step` objects with `Shrine.transloadit_step`:\n\n```rb\nShrine.transloadit_step \"my_name\", \"/my/robot\", **options\n#=\u003e #\u003cTransloadit::Step name=\"my_name\", robot=\"/my/robot\", options={...}\u003e\n```\n\nThis method adds the ability to pass another `Transloadit::Step` object as the\n`:use` parameter:\n\n```rb\nstep_one = Shrine.transloadit_step \"one\", \"/robot/one\"\nstep_two = Shrine.transloadit_step \"two\", \"/robot/two\", use: step_one\nstep_two.options[:use] #=\u003e [\"one\"]\n```\n\n### Import step\n\nThe `Shrine::UploadedFile#transloadit_import_step` method generates an import\nstep for the uploaded file:\n\n```rb\nfile = Shrine.upload(io, :store)\nfile.storage #=\u003e #\u003cShrine::Storage::S3\u003e\nfile.id      #=\u003e \"foo\"\n\nstep = file.transloadit_import_step\n\nstep       #=\u003e #\u003cTransloadit::Step ...\u003e\nstep.name  #=\u003e \"import\"\nstep.robot #=\u003e \"/s3/import\"\n\nstep.options[:path]        #=\u003e \"foo\"\nstep.options[:credentials] #=\u003e :s3_store (inferred from the plugin setting)\n```\n\nYou can change the default step name:\n\n```rb\nstep = file.transloadit_import_step(\"my_import\")\nstep.name #=\u003e \"my_import\"\n```\n\nYou can also pass step options:\n\n```rb\nstep = file.transloadit_import_step(ignore_errors: [\"meta\"])\nstep.options[:ignore_errors] #=\u003e [\"meta\"]\n```\n\nThe following import robots are currently supported:\n\n| Robot          | Description                                                |\n| :-----------   | :----------                                                |\n| `/s3/import`   | activated for `Shrine::Storage::S3`                        |\n| `/http/import` | activated for any other storage which returns HTTP(S) URLs |\n| `/ftp/import`  | activated for any other storage which returns FTP URLs     |\n\n### Export step\n\nThe `Shrine#transloadit_export_step` method generates an export step for the underlying\nstorage:\n\n```rb\nuploader = Shrine.new(:store)\nuploader.storage #=\u003e #\u003cShrine::Storage::S3\u003e\n\nstep = uploader.transloadit_export_step\n\nstep       #=\u003e #\u003cTransloadit::Step ...\u003e\nstep.name  #=\u003e \"export\"\nstep.robot #=\u003e \"/s3/store\"\n\nstep.options[:credentials] #=\u003e :s3_store (inferred from the plugin setting)\n```\n\nYou can change the default step name:\n\n```rb\nstep = uploader.transloadit_export_step(\"my_export\")\nstep.name #=\u003e \"my_export\"\n```\n\nYou can also pass step options:\n\n```rb\nstep = file.transloadit_export_step(acl: \"public-read\")\nstep.options[:acl] #=\u003e \"public-read\"\n```\n\nThe following export robots are currently supported:\n\n| Robot            | Description                                                       |\n| :----            | :----------                                                       |\n| `/s3/store`      | activated for `Shrine::Storage::S3`                               |\n| `/google/store`  | activated for [`Shrine::Storage::GoogleCloudStorage`][shrine-gcs] |\n| `/youtube/store` | activated for [`Shrine::Storage::YouTube`][shrine-youtube]        |\n\n### File\n\nThe `Shrine#transloadit_file` method will convert a Transloadit result hash\ninto a `Shrine::UploadedFile` object:\n\n```rb\nuploader = Shrine.new(:store)\nuploader.storage #=\u003e #\u003cShrine::Storage::S3\u003e\n\nfile = uploader.transloadit_file(\n  \"url\" =\u003e \"https://my-bucket.s3.amazonaws.com/foo\",\n  # ...\n)\n\nfile #=\u003e #\u003cShrine::UploadedFile @id=\"foo\" storage_key=:store ...\u003e\n\nfile.storage #=\u003e #\u003cShrine::Storage::S3\u003e\nfile.id      #=\u003e \"foo\"\n```\n\nYou can use the plural `Shrine#transloadit_files` to convert an array of\nresults:\n\n```rb\nfiles = uploader.transloadit_files [\n  { \"url\" =\u003e \"https://my-bucket.s3.amazonaws.com/foo\", ... },\n  { \"url\" =\u003e \"https://my-bucket.s3.amazonaws.com/bar\", ... },\n  { \"url\" =\u003e \"https://my-bucket.s3.amazonaws.com/baz\", ... },\n]\n\nfiles #=\u003e\n# [\n#   #\u003cShrine::UploadedFile @id=\"foo\" @storage_key=:store ...\u003e,\n#   #\u003cShrine::UploadedFile @id=\"bar\" @storage_key=:store ...\u003e,\n#   #\u003cShrine::UploadedFile @id=\"baz\" @storage_key=:store ...\u003e,\n# ]\n```\n\nIt will include basic metadata:\n\n```rb\nfile = uploader.transloadit_file(\n  # ...\n  \"name\" =\u003e \"matrix.mp4\",\n  \"size\" =\u003e 44198,\n  \"mime\" =\u003e \"video/mp4\",\n)\n\nfile.original_filename #=\u003e \"matrix.mp4\"\nfile.size              #=\u003e 44198\nfile.mime_type         #=\u003e \"video/mp4\"\n```\n\nIt will also merge any custom metadata:\n\n```rb\nfile = uploader.transloadit_file(\n  # ...\n  \"meta\" =\u003e { \"duration\" =\u003e 9000, ... },\n)\n\nfile[\"duration\"] #=\u003e 9000\n```\n\nCurrently only `Shrine::Stroage::S3` is supported. However, you can still\nhandle other remote files using [`Shrine::Storage::Url`][shrine-url]:\n\n```rb\nShrine.storages =\u003e {\n  # ...\n  url: Shrine::Storage::Url.new,\n}\n```\n```rb\nuploader = Shrine.new(:url)\nuploader #=\u003e #\u003cShrine::Storage::Url\u003e\n\nfile = uploader.transloadit_file(\n  \"url\" =\u003e \"https://example.com/foo\",\n  # ...\n)\n\nfile.id #=\u003e \"https://example.com/foo\"\n```\n\n## Instrumentation\n\nIf the `instrumentation` plugin has been loaded, the `transloadit` plugin adds\ninstrumentation around triggering processing.\n\n```rb\n# instrumentation plugin needs to be loaded *before* transloadit\nplugin :instrumentation\nplugin :transloadit\n```\n\nCalling the processor will trigger a `transloadit.shrine` event with the\nfollowing payload:\n\n| Key          | Description                            |\n| :----        | :----------                            |\n| `:processor` | Name of the processor                  |\n| `:uploader`  | The uploader class that sent the event |\n\nA default log subscriber is added as well which logs these events:\n\n```\nTransloadit (1238ms) – {:processor=\u003e:video, :uploader=\u003eVideoUploader}\n```\n\nYou can also use your own log subscriber:\n\n```rb\nplugin :transloadit, log_subscriber: -\u003e (event) {\n  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)\n}\n```\n```\n{\"name\":\"transloadit\",\"duration\":1238,\"processor\":\"video\",\"uploader\":\"VideoUploader\"}\n```\n\nOr disable logging altogether:\n\n```rb\nplugin :transloadit, log_subscriber: nil\n```\n\n## Contributing\n\nTests are run with:\n\n```sh\n$ bundle exec rake test\n```\n\n## License\n\n[MIT](/LICENSE.txt)\n\n[Shrine]: https://github.com/shrinerb/shrine\n[Transloadit]: https://transloadit.com/\n[Ruby SDK]: https://github.com/transloadit/ruby-sdk\n[credentials]: https://transloadit.com/docs/#16-template-credentials\n[import robots]: https://transloadit.com/docs/transcoding/#overview-service-file-importing\n[export robots]: https://transloadit.com/docs/transcoding/#overview-service-file-exporting\n[derivatives]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/derivatives.md#readme\n[assembly notifications]: https://transloadit.com/docs/#24-assembly-notifications\n[backgrounding]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/backgrounding.md#readme\n[shrine-url]: https://github.com/shrinerb/shrine-url\n[Robodog]: https://uppy.io/docs/robodog/\n[Robodog Form]: https://uppy.io/docs/robodog/form/\n[Uppy]: https://uppy.io/\n[atomic_helpers]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/atomic_helpers.md#readme\n[shrine-gcs]: https://github.com/renchap/shrine-google_cloud_storage\n[shrine-youtube]: https://github.com/thedyrt/shrine-storage-you_tube\n[shrine-url]: https://github.com/shrinerb/shrine-url\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshrinerb%2Fshrine-transloadit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshrinerb%2Fshrine-transloadit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshrinerb%2Fshrine-transloadit/lists"}