Jelajahi Sumber

transloadit: Add `getAssemblyOptions`.

This patch adds a `getAssemblyOptions` function option to the
Transloadit plugin. This option can return an object or a Promise for an
object to configure the Transloadit assembly. The returned object can
contain `params`, a `signature`, and `fields`.

`getAssemblyOptions` is called on each file, so each file can return a
different set of options. Files that returned the same options are
bundled together and run through a single assembly. Files that return
different options will be run through different assemblies.

This means that it is now possible to make assembly parameters depend on
user input from eg. the MetaData plugin, and have different parameters
for different files. It's now also possible to generate very short-lived
signatures on the server when an upload starts.
Renée Kooi 7 tahun lalu
induk
melakukan
5f7d3d79cf
1 mengubah file dengan 64 tambahan dan 23 penghapusan
  1. 64 23
      src/plugins/Transloadit/index.js

+ 64 - 23
src/plugins/Transloadit/index.js

@@ -26,6 +26,13 @@ module.exports = class Transloadit extends Plugin {
       signature: null,
       params: null,
       fields: {},
+      getAssemblyOptions (file, options) {
+        return {
+          params: options.params,
+          signature: options.signature,
+          fields: options.fields
+        }
+      },
       locale: defaultLocale
     }
 
@@ -62,14 +69,44 @@ module.exports = class Transloadit extends Plugin {
     this.sockets = {}
   }
 
-  createAssembly (filesToUpload) {
+  getAssemblyOptions (fileIDs) {
+    const options = this.opts
+    return Promise.all(
+      fileIDs.map((fileID) => {
+        const file = this.getFile(fileID)
+        return Promise.resolve(options.getAssemblyOptions(file, options)).then((assemblyOptions) => ({
+          fileIDs: [fileID],
+          options: assemblyOptions
+        }))
+      })
+    )
+  }
+
+  dedupeAssemblyOptions (list) {
+    const dedupeMap = Object.create(null)
+    list.forEach(({ fileIDs, options }) => {
+      const id = JSON.stringify(options)
+      if (dedupeMap[id]) {
+        dedupeMap[id].fileIDs.push(...fileIDs)
+      } else {
+        dedupeMap[id] = {
+          options,
+          fileIDs: [...fileIDs]
+        }
+      }
+    })
+
+    return Object.keys(dedupeMap).map((id) => dedupeMap[id])
+  }
+
+  createAssembly (fileIDs, options) {
     this.core.log('Transloadit: create assembly')
 
     return this.client.createAssembly({
-      params: this.opts.params,
-      fields: this.opts.fields,
-      expectedFiles: Object.keys(filesToUpload).length,
-      signature: this.opts.signature
+      params: options.params,
+      fields: options.fields,
+      expectedFiles: fileIDs.length,
+      signature: options.signature
     }).then((assembly) => {
       this.updateState({
         assemblies: Object.assign(this.state.assemblies, {
@@ -103,13 +140,13 @@ module.exports = class Transloadit extends Plugin {
       }
 
       const files = Object.assign({}, this.core.state.files)
-      Object.keys(filesToUpload).forEach((id) => {
+      fileIDs.forEach((id) => {
         files[id] = attachAssemblyMetadata(files[id], assembly)
       })
 
       this.core.setState({ files })
 
-      this.core.emit('transloadit:assembly', assembly, Object.keys(filesToUpload))
+      this.core.emit('transloadit:assembly', assembly, fileIDs)
 
       return this.connectSocket(assembly)
     }).then(() => {
@@ -208,27 +245,31 @@ module.exports = class Transloadit extends Plugin {
     })
   }
 
-  prepareUpload (fileIDs) {
-    const filesToUpload = fileIDs.map(getFile, this).reduce(intoFileMap, {})
-    function getFile (fileID) {
-      return this.core.state.files[fileID]
-    }
-    function intoFileMap (map, file) {
-      map[file.id] = file
-      return map
-    }
+  getFile (fileID) {
+    return this.core.state.files[fileID]
+  }
 
+  prepareUpload (fileIDs) {
     fileIDs.forEach((fileID) => {
       this.core.emit('core:preprocess-progress', fileID, {
         mode: 'indeterminate',
         message: this.opts.locale.strings.creatingAssembly
       })
     })
-    return this.createAssembly(filesToUpload).then(() => {
-      fileIDs.forEach((fileID) => {
-        this.core.emit('core:preprocess-complete', fileID)
+
+    const createAssembly = ({ fileIDs, options }) => {
+      return this.createAssembly(fileIDs, options).then(() => {
+        fileIDs.forEach((fileID) => {
+          this.core.emit('core:preprocess-complete', fileID)
+        })
       })
-    })
+    }
+
+    return this.getAssemblyOptions(fileIDs)
+      .then((allOptions) => this.dedupeAssemblyOptions(allOptions))
+      .then((assemblies) => Promise.all(
+        assemblies.map(createAssembly)
+      ))
   }
 
   afterUpload (fileIDs) {
@@ -238,7 +279,7 @@ module.exports = class Transloadit extends Plugin {
     // If we don't have to wait for encoding metadata or results, we can close
     // the socket immediately and finish the upload.
     if (!this.shouldWait()) {
-      const file = this.core.getState().files[fileID]
+      const file = this.getFile(fileID)
       const socket = this.socket[file.assembly]
       socket.close()
       return
@@ -253,7 +294,7 @@ module.exports = class Transloadit extends Plugin {
       })
 
       const onAssemblyFinished = (assembly) => {
-        const file = this.core.state.files[fileID]
+        const file = this.getFile(fileID)
         // An assembly for a different upload just finished. We can ignore it.
         if (assembly.assembly_id !== file.transloadit.assembly) {
           return
@@ -273,7 +314,7 @@ module.exports = class Transloadit extends Plugin {
       }
 
       const onAssemblyError = (assembly, error) => {
-        const file = this.core.state.files[fileID]
+        const file = this.getFile(fileID)
         // An assembly for a different upload just errored. We can ignore it.
         if (assembly.assembly_id !== file.transloadit.assembly) {
           return