1 | const Translator = require('@uppy/utils/lib/Translator')
|
2 | const hasProperty = require('@uppy/utils/lib/hasProperty')
|
3 | const { Plugin } = require('@uppy/core')
|
4 | const Tus = require('@uppy/tus')
|
5 | const Assembly = require('./Assembly')
|
6 | const Client = require('./Client')
|
7 | const AssemblyOptions = require('./AssemblyOptions')
|
8 | const AssemblyWatcher = require('./AssemblyWatcher')
|
9 |
|
10 | function defaultGetAssemblyOptions (file, options) {
|
11 | return {
|
12 | params: options.params,
|
13 | signature: options.signature,
|
14 | fields: options.fields
|
15 | }
|
16 | }
|
17 |
|
18 | const COMPANION = 'https://api2.transloadit.com/companion'
|
19 |
|
20 | const ALLOWED_COMPANION_PATTERN = /\.transloadit\.com$/
|
21 |
|
22 | const TL_COMPANION = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/companion/
|
23 | const TL_UPPY_SERVER = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/uppy-server/
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | module.exports = class Transloadit extends Plugin {
|
29 | static VERSION = require('../package.json').version
|
30 |
|
31 | constructor (uppy, opts) {
|
32 | super(uppy, opts)
|
33 | this.type = 'uploader'
|
34 | this.id = this.opts.id || 'Transloadit'
|
35 | this.title = 'Transloadit'
|
36 |
|
37 | this.defaultLocale = {
|
38 | strings: {
|
39 | creatingAssembly: 'Preparing upload...',
|
40 | creatingAssemblyFailed: 'Transloadit: Could not create Assembly',
|
41 | encoding: 'Encoding...'
|
42 | }
|
43 | }
|
44 |
|
45 | const defaultOptions = {
|
46 | service: 'https://api2.transloadit.com',
|
47 | errorReporting: true,
|
48 | waitForEncoding: false,
|
49 | waitForMetadata: false,
|
50 | alwaysRunAssembly: false,
|
51 | importFromUploadURLs: false,
|
52 | signature: null,
|
53 | params: null,
|
54 | fields: {},
|
55 | getAssemblyOptions: defaultGetAssemblyOptions,
|
56 | limit: 0
|
57 | }
|
58 |
|
59 | this.opts = { ...defaultOptions, ...opts }
|
60 |
|
61 | this.i18nInit()
|
62 |
|
63 | this._prepareUpload = this._prepareUpload.bind(this)
|
64 | this._afterUpload = this._afterUpload.bind(this)
|
65 | this._onError = this._onError.bind(this)
|
66 | this._onTusError = this._onTusError.bind(this)
|
67 | this._onCancelAll = this._onCancelAll.bind(this)
|
68 | this._onFileUploadURLAvailable = this._onFileUploadURLAvailable.bind(this)
|
69 | this._onRestored = this._onRestored.bind(this)
|
70 | this._getPersistentData = this._getPersistentData.bind(this)
|
71 |
|
72 | const hasCustomAssemblyOptions = this.opts.getAssemblyOptions !== defaultOptions.getAssemblyOptions
|
73 | if (this.opts.params) {
|
74 | AssemblyOptions.validateParams(this.opts.params)
|
75 | } else if (!hasCustomAssemblyOptions) {
|
76 |
|
77 |
|
78 | AssemblyOptions.validateParams(null)
|
79 | }
|
80 |
|
81 | this.client = new Client({
|
82 | service: this.opts.service,
|
83 | client: this._getClientVersion(),
|
84 | errorReporting: this.opts.errorReporting
|
85 | })
|
86 |
|
87 | this.activeAssemblies = {}
|
88 |
|
89 | this.assemblyWatchers = {}
|
90 |
|
91 | this.completedFiles = Object.create(null)
|
92 | }
|
93 |
|
94 | setOptions (newOpts) {
|
95 | super.setOptions(newOpts)
|
96 | this.i18nInit()
|
97 | }
|
98 |
|
99 | i18nInit () {
|
100 | this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale])
|
101 | this.i18n = this.translator.translate.bind(this.translator)
|
102 | this.i18nArray = this.translator.translateArray.bind(this.translator)
|
103 | this.setPluginState()
|
104 | }
|
105 |
|
106 | _getClientVersion () {
|
107 | const list = [
|
108 | `uppy-core:${this.uppy.constructor.VERSION}`,
|
109 | `uppy-transloadit:${this.constructor.VERSION}`,
|
110 | `uppy-tus:${Tus.VERSION}`
|
111 | ]
|
112 |
|
113 | const addPluginVersion = (pluginName, versionName) => {
|
114 | const plugin = this.uppy.getPlugin(pluginName)
|
115 | if (plugin) {
|
116 | list.push(`${versionName}:${plugin.constructor.VERSION}`)
|
117 | }
|
118 | }
|
119 |
|
120 | if (this.opts.importFromUploadURLs) {
|
121 | addPluginVersion('XHRUpload', 'uppy-xhr-upload')
|
122 | addPluginVersion('AwsS3', 'uppy-aws-s3')
|
123 | addPluginVersion('AwsS3Multipart', 'uppy-aws-s3-multipart')
|
124 | }
|
125 |
|
126 | addPluginVersion('Dropbox', 'uppy-dropbox')
|
127 | addPluginVersion('Facebook', 'uppy-facebook')
|
128 | addPluginVersion('GoogleDrive', 'uppy-google-drive')
|
129 | addPluginVersion('Instagram', 'uppy-instagram')
|
130 | addPluginVersion('OneDrive', 'uppy-onedrive')
|
131 | addPluginVersion('Zoom', 'uppy-zoom')
|
132 | addPluginVersion('Url', 'uppy-url')
|
133 |
|
134 | return list.join(',')
|
135 | }
|
136 |
|
137 | |
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | _attachAssemblyMetadata (file, status) {
|
147 |
|
148 | const meta = {
|
149 | ...file.meta,
|
150 | assembly_url: status.assembly_url,
|
151 | filename: file.name,
|
152 | fieldname: 'file'
|
153 | }
|
154 |
|
155 | const tus = {
|
156 | ...file.tus,
|
157 | endpoint: status.tus_url,
|
158 |
|
159 | addRequestId: true
|
160 | }
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 | let remote = file.remote
|
167 | if (file.remote && TL_UPPY_SERVER.test(file.remote.companionUrl)) {
|
168 | const err = new Error(
|
169 | 'The https://api2.transloadit.com/uppy-server endpoint was renamed to ' +
|
170 | 'https://api2.transloadit.com/companion, please update your `companionUrl` ' +
|
171 | 'options accordingly.')
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | this.uppy.log(err)
|
177 | throw err
|
178 | }
|
179 |
|
180 | if (file.remote && TL_COMPANION.test(file.remote.companionUrl)) {
|
181 | const newHost = status.companion_url
|
182 | .replace(/\/$/, '')
|
183 | const path = file.remote.url
|
184 | .replace(file.remote.companionUrl, '')
|
185 | .replace(/^\//, '')
|
186 |
|
187 | remote = {
|
188 | ...file.remote,
|
189 | companionUrl: newHost,
|
190 | url: `${newHost}/${path}`
|
191 | }
|
192 | }
|
193 |
|
194 |
|
195 | const newFile = {
|
196 | ...file,
|
197 | transloadit: {
|
198 | assembly: status.assembly_id
|
199 | }
|
200 | }
|
201 |
|
202 | if (!this.opts.importFromUploadURLs) {
|
203 | Object.assign(newFile, { meta, tus, remote })
|
204 | }
|
205 | return newFile
|
206 | }
|
207 |
|
208 | _createAssembly (fileIDs, uploadID, options) {
|
209 | this.uppy.log('[Transloadit] Create Assembly')
|
210 |
|
211 | return this.client.createAssembly({
|
212 | params: options.params,
|
213 | fields: options.fields,
|
214 | expectedFiles: fileIDs.length,
|
215 | signature: options.signature
|
216 | }).then((newAssembly) => {
|
217 | const assembly = new Assembly(newAssembly)
|
218 | const status = assembly.status
|
219 | const assemblyID = status.assembly_id
|
220 |
|
221 | const { assemblies, uploadsAssemblies } = this.getPluginState()
|
222 | this.setPluginState({
|
223 |
|
224 | assemblies: {
|
225 | ...assemblies,
|
226 | [assemblyID]: status
|
227 | },
|
228 |
|
229 | uploadsAssemblies: {
|
230 | ...uploadsAssemblies,
|
231 | [uploadID]: [
|
232 | ...uploadsAssemblies[uploadID],
|
233 | assemblyID
|
234 | ]
|
235 | }
|
236 | })
|
237 |
|
238 | const { files } = this.uppy.getState()
|
239 | const updatedFiles = {}
|
240 | fileIDs.forEach((id) => {
|
241 | updatedFiles[id] = this._attachAssemblyMetadata(this.uppy.getFile(id), status)
|
242 | })
|
243 | this.uppy.setState({
|
244 | files: {
|
245 | ...files,
|
246 | ...updatedFiles
|
247 | }
|
248 | })
|
249 |
|
250 | this.uppy.emit('transloadit:assembly-created', status, fileIDs)
|
251 |
|
252 | this.uppy.log(`[Transloadit] Created Assembly ${assemblyID}`)
|
253 | return assembly
|
254 | }).catch((err) => {
|
255 | err.message = `${this.i18n('creatingAssemblyFailed')}: ${err.message}`
|
256 |
|
257 | throw err
|
258 | })
|
259 | }
|
260 |
|
261 | _createAssemblyWatcher (assemblyID, fileIDs, uploadID) {
|
262 |
|
263 | const watcher = new AssemblyWatcher(this.uppy, assemblyID)
|
264 |
|
265 | watcher.on('assembly-complete', (id) => {
|
266 | const files = this.getAssemblyFiles(id)
|
267 | files.forEach((file) => {
|
268 | this.completedFiles[file.id] = true
|
269 | this.uppy.emit('postprocess-complete', file)
|
270 | })
|
271 | })
|
272 |
|
273 | watcher.on('assembly-error', (id, error) => {
|
274 |
|
275 | const files = this.getAssemblyFiles(id)
|
276 | files.forEach((file) => {
|
277 |
|
278 | this.uppy.emit('upload-error', file, error)
|
279 |
|
280 | this.uppy.emit('postprocess-complete', file)
|
281 | })
|
282 | })
|
283 |
|
284 | this.assemblyWatchers[uploadID] = watcher
|
285 | }
|
286 |
|
287 | _shouldWaitAfterUpload () {
|
288 | return this.opts.waitForEncoding || this.opts.waitForMetadata
|
289 | }
|
290 |
|
291 | |
292 |
|
293 |
|
294 |
|
295 | _reserveFiles (assembly, fileIDs) {
|
296 | return Promise.all(fileIDs.map((fileID) => {
|
297 | const file = this.uppy.getFile(fileID)
|
298 | return this.client.reserveFile(assembly, file)
|
299 | }))
|
300 | }
|
301 |
|
302 | |
303 |
|
304 |
|
305 |
|
306 | _onFileUploadURLAvailable (file) {
|
307 | if (!file || !file.transloadit || !file.transloadit.assembly) {
|
308 | return
|
309 | }
|
310 |
|
311 | const { assemblies } = this.getPluginState()
|
312 | const assembly = assemblies[file.transloadit.assembly]
|
313 |
|
314 | this.client.addFile(assembly, file).catch((err) => {
|
315 | this.uppy.log(err)
|
316 | this.uppy.emit('transloadit:import-error', assembly, file.id, err)
|
317 | })
|
318 | }
|
319 |
|
320 | _findFile (uploadedFile) {
|
321 | const files = this.uppy.getFiles()
|
322 | for (let i = 0; i < files.length; i++) {
|
323 | const file = files[i]
|
324 |
|
325 | if (file.uploadURL === uploadedFile.tus_upload_url) {
|
326 | return file
|
327 | }
|
328 |
|
329 | if (file.tus && file.tus.uploadUrl === uploadedFile.tus_upload_url) {
|
330 | return file
|
331 | }
|
332 | if (!uploadedFile.is_tus_file) {
|
333 |
|
334 | if (file.name === uploadedFile.name && file.size === uploadedFile.size) {
|
335 | return file
|
336 | }
|
337 | }
|
338 | }
|
339 | }
|
340 |
|
341 | _onFileUploadComplete (assemblyId, uploadedFile) {
|
342 | const state = this.getPluginState()
|
343 | const file = this._findFile(uploadedFile)
|
344 | if (!file) {
|
345 | this.uppy.log('[Transloadit] Couldn’t file the file, it was likely removed in the process')
|
346 | return
|
347 | }
|
348 | this.setPluginState({
|
349 | files: {
|
350 | ...state.files,
|
351 | [uploadedFile.id]: {
|
352 | assembly: assemblyId,
|
353 | id: file.id,
|
354 | uploadedFile
|
355 | }
|
356 | }
|
357 | })
|
358 | this.uppy.emit('transloadit:upload', uploadedFile, this.getAssembly(assemblyId))
|
359 | }
|
360 |
|
361 | |
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 | _onResult (assemblyId, stepName, result) {
|
369 | const state = this.getPluginState()
|
370 | const file = state.files[result.original_id]
|
371 |
|
372 | result.localId = file ? file.id : null
|
373 |
|
374 | const entry = {
|
375 | result,
|
376 | stepName,
|
377 | id: result.id,
|
378 | assembly: assemblyId
|
379 | }
|
380 |
|
381 | this.setPluginState({
|
382 | results: [...state.results, entry]
|
383 | })
|
384 | this.uppy.emit('transloadit:result', stepName, result, this.getAssembly(assemblyId))
|
385 | }
|
386 |
|
387 | |
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | _onAssemblyFinished (status) {
|
394 | const url = status.assembly_ssl_url
|
395 | this.client.getAssemblyStatus(url).then((finalStatus) => {
|
396 | const assemblyId = finalStatus.assembly_id
|
397 | const state = this.getPluginState()
|
398 | this.setPluginState({
|
399 | assemblies: {
|
400 | ...state.assemblies,
|
401 | [assemblyId]: finalStatus
|
402 | }
|
403 | })
|
404 | this.uppy.emit('transloadit:complete', finalStatus)
|
405 | })
|
406 | }
|
407 |
|
408 | _cancelAssembly (assembly) {
|
409 | return this.client.cancelAssembly(assembly).then(() => {
|
410 |
|
411 | this.uppy.emit('transloadit:assembly-cancelled', assembly)
|
412 | })
|
413 | }
|
414 |
|
415 | |
416 |
|
417 |
|
418 | _onCancelAll () {
|
419 | const { uploadsAssemblies } = this.getPluginState()
|
420 |
|
421 | const assemblyIDs = Object.keys(uploadsAssemblies).reduce((acc, uploadID) => {
|
422 | acc.push(...uploadsAssemblies[uploadID])
|
423 | return acc
|
424 | }, [])
|
425 |
|
426 | const cancelPromises = assemblyIDs.map((assemblyID) => {
|
427 | const assembly = this.getAssembly(assemblyID)
|
428 | return this._cancelAssembly(assembly)
|
429 | })
|
430 |
|
431 | Promise.all(cancelPromises).catch((err) => {
|
432 | this.uppy.log(err)
|
433 | })
|
434 | }
|
435 |
|
436 | |
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 | _getPersistentData (setData) {
|
443 | const state = this.getPluginState()
|
444 | const assemblies = state.assemblies
|
445 | const uploadsAssemblies = state.uploadsAssemblies
|
446 |
|
447 | setData({
|
448 | [this.id]: {
|
449 | assemblies,
|
450 | uploadsAssemblies
|
451 | }
|
452 | })
|
453 | }
|
454 |
|
455 | _onRestored (pluginData) {
|
456 | const savedState = pluginData && pluginData[this.id] ? pluginData[this.id] : {}
|
457 | const previousAssemblies = savedState.assemblies || {}
|
458 | const uploadsAssemblies = savedState.uploadsAssemblies || {}
|
459 |
|
460 | if (Object.keys(uploadsAssemblies).length === 0) {
|
461 |
|
462 | return
|
463 | }
|
464 |
|
465 |
|
466 | const restoreState = (assemblies) => {
|
467 | const files = {}
|
468 | const results = []
|
469 | Object.keys(assemblies).forEach((id) => {
|
470 | const status = assemblies[id]
|
471 |
|
472 | status.uploads.forEach((uploadedFile) => {
|
473 | const file = this._findFile(uploadedFile)
|
474 | files[uploadedFile.id] = {
|
475 | id: file.id,
|
476 | assembly: id,
|
477 | uploadedFile
|
478 | }
|
479 | })
|
480 |
|
481 | const state = this.getPluginState()
|
482 | Object.keys(status.results).forEach((stepName) => {
|
483 | status.results[stepName].forEach((result) => {
|
484 | const file = state.files[result.original_id]
|
485 | result.localId = file ? file.id : null
|
486 | results.push({
|
487 | id: result.id,
|
488 | result,
|
489 | stepName,
|
490 | assembly: id
|
491 | })
|
492 | })
|
493 | })
|
494 | })
|
495 |
|
496 | this.setPluginState({
|
497 | assemblies,
|
498 | files,
|
499 | results,
|
500 | uploadsAssemblies
|
501 | })
|
502 | }
|
503 |
|
504 |
|
505 | const restoreAssemblies = () => {
|
506 | const { assemblies, uploadsAssemblies } = this.getPluginState()
|
507 |
|
508 |
|
509 | Object.keys(uploadsAssemblies).forEach((uploadID) => {
|
510 | const assemblyIDs = uploadsAssemblies[uploadID]
|
511 | const fileIDsInUpload = assemblyIDs.reduce((acc, assemblyID) => {
|
512 | const fileIDsInAssembly = this.getAssemblyFiles(assemblyID).map((file) => file.id)
|
513 | acc.push(...fileIDsInAssembly)
|
514 | return acc
|
515 | }, [])
|
516 | this._createAssemblyWatcher(assemblyIDs, fileIDsInUpload, uploadID)
|
517 | })
|
518 |
|
519 | const allAssemblyIDs = Object.keys(assemblies)
|
520 | allAssemblyIDs.forEach((id) => {
|
521 | const assembly = new Assembly(assemblies[id])
|
522 | this._connectAssembly(assembly)
|
523 | })
|
524 | }
|
525 |
|
526 |
|
527 | const updateAssemblies = () => {
|
528 | const { assemblies } = this.getPluginState()
|
529 | return Promise.all(
|
530 | Object.keys(assemblies).map((id) => {
|
531 | return this.activeAssemblies[id].update()
|
532 | })
|
533 | )
|
534 | }
|
535 |
|
536 |
|
537 | this.restored = Promise.resolve().then(() => {
|
538 | restoreState(previousAssemblies)
|
539 | restoreAssemblies()
|
540 | return updateAssemblies()
|
541 | })
|
542 |
|
543 | this.restored.then(() => {
|
544 | this.restored = null
|
545 | })
|
546 | }
|
547 |
|
548 | _connectAssembly (assembly) {
|
549 | const { status } = assembly
|
550 | const id = status.assembly_id
|
551 | this.activeAssemblies[id] = assembly
|
552 |
|
553 |
|
554 | assembly.on('status', (newStatus) => {
|
555 | const { assemblies } = this.getPluginState()
|
556 | this.setPluginState({
|
557 | assemblies: {
|
558 | ...assemblies,
|
559 | [id]: newStatus
|
560 | }
|
561 | })
|
562 | })
|
563 |
|
564 | assembly.on('upload', (file) => {
|
565 | this._onFileUploadComplete(id, file)
|
566 | })
|
567 | assembly.on('error', (error) => {
|
568 | error.assembly = assembly.status
|
569 | this.uppy.emit('transloadit:assembly-error', assembly.status, error)
|
570 | })
|
571 |
|
572 | assembly.on('executing', () => {
|
573 | this.uppy.emit('transloadit:assembly-executing', assembly.status)
|
574 | })
|
575 |
|
576 | if (this.opts.waitForEncoding) {
|
577 | assembly.on('result', (stepName, result) => {
|
578 | this._onResult(id, stepName, result)
|
579 | })
|
580 | }
|
581 |
|
582 | if (this.opts.waitForEncoding) {
|
583 | assembly.on('finished', () => {
|
584 | this._onAssemblyFinished(assembly.status)
|
585 | })
|
586 | } else if (this.opts.waitForMetadata) {
|
587 | assembly.on('metadata', () => {
|
588 | this._onAssemblyFinished(assembly.status)
|
589 | })
|
590 | }
|
591 |
|
592 |
|
593 | if (assembly.ok === 'ASSEMBLY_COMPLETE') {
|
594 | return assembly
|
595 | }
|
596 |
|
597 |
|
598 |
|
599 | const connected = new Promise((resolve, reject) => {
|
600 | assembly.once('connect', resolve)
|
601 | assembly.once('status', resolve)
|
602 | assembly.once('error', reject)
|
603 | }).then(() => {
|
604 | this.uppy.log('[Transloadit] Socket is ready')
|
605 | })
|
606 |
|
607 | assembly.connect()
|
608 | return assembly
|
609 | }
|
610 |
|
611 | _prepareUpload (fileIDs, uploadID) {
|
612 |
|
613 | fileIDs = fileIDs.filter((file) => !file.error)
|
614 |
|
615 | fileIDs.forEach((fileID) => {
|
616 | const file = this.uppy.getFile(fileID)
|
617 | this.uppy.emit('preprocess-progress', file, {
|
618 | mode: 'indeterminate',
|
619 | message: this.i18n('creatingAssembly')
|
620 | })
|
621 | })
|
622 |
|
623 | const createAssembly = ({ fileIDs, options }) => {
|
624 | let createdAssembly
|
625 | return this._createAssembly(fileIDs, uploadID, options).then((assembly) => {
|
626 | createdAssembly = assembly
|
627 | if (this.opts.importFromUploadURLs) {
|
628 | return this._reserveFiles(assembly, fileIDs)
|
629 | }
|
630 | }).then(() => {
|
631 | fileIDs.forEach((fileID) => {
|
632 | const file = this.uppy.getFile(fileID)
|
633 | this.uppy.emit('preprocess-complete', file)
|
634 | })
|
635 | return createdAssembly
|
636 | }).catch((err) => {
|
637 | fileIDs.forEach((fileID) => {
|
638 | const file = this.uppy.getFile(fileID)
|
639 |
|
640 |
|
641 | this.uppy.emit('preprocess-complete', file)
|
642 | this.uppy.emit('upload-error', file, err)
|
643 | })
|
644 | throw err
|
645 | })
|
646 | }
|
647 |
|
648 | const { uploadsAssemblies } = this.getPluginState()
|
649 | this.setPluginState({
|
650 | uploadsAssemblies: {
|
651 | ...uploadsAssemblies,
|
652 | [uploadID]: []
|
653 | }
|
654 | })
|
655 |
|
656 | const files = fileIDs.map((id) => this.uppy.getFile(id))
|
657 | const assemblyOptions = new AssemblyOptions(files, this.opts)
|
658 |
|
659 | return assemblyOptions.build().then(
|
660 | (assemblies) => Promise.all(
|
661 | assemblies.map(createAssembly)
|
662 | ).then((createdAssemblies) => {
|
663 | const assemblyIDs = createdAssemblies.map(assembly => assembly.status.assembly_id)
|
664 | this._createAssemblyWatcher(assemblyIDs, fileIDs, uploadID)
|
665 | createdAssemblies.map(assembly => this._connectAssembly(assembly))
|
666 | }),
|
667 |
|
668 |
|
669 | (err) => {
|
670 | fileIDs.forEach((fileID) => {
|
671 | const file = this.uppy.getFile(fileID)
|
672 | this.uppy.emit('preprocess-complete', file)
|
673 | this.uppy.emit('upload-error', file, err)
|
674 | })
|
675 | throw err
|
676 | }
|
677 | )
|
678 | }
|
679 |
|
680 | _afterUpload (fileIDs, uploadID) {
|
681 | const files = fileIDs.map(fileID => this.uppy.getFile(fileID))
|
682 |
|
683 | fileIDs = files.filter((file) => !file.error).map(file => file.id)
|
684 |
|
685 | const state = this.getPluginState()
|
686 |
|
687 |
|
688 | if (this.restored) {
|
689 | return this.restored.then(() => {
|
690 | return this._afterUpload(fileIDs, uploadID)
|
691 | })
|
692 | }
|
693 |
|
694 | const assemblyIDs = state.uploadsAssemblies[uploadID]
|
695 |
|
696 |
|
697 |
|
698 | if (!this._shouldWaitAfterUpload()) {
|
699 | assemblyIDs.forEach((assemblyID) => {
|
700 | const assembly = this.activeAssemblies[assemblyID]
|
701 | assembly.close()
|
702 | delete this.activeAssemblies[assemblyID]
|
703 | })
|
704 | const assemblies = assemblyIDs.map((id) => this.getAssembly(id))
|
705 | this.uppy.addResultData(uploadID, { transloadit: assemblies })
|
706 | return Promise.resolve()
|
707 | }
|
708 |
|
709 |
|
710 |
|
711 | if (assemblyIDs.length === 0) {
|
712 | this.uppy.addResultData(uploadID, { transloadit: [] })
|
713 | return Promise.resolve()
|
714 | }
|
715 |
|
716 | const incompleteFiles = files.filter(file => !hasProperty(this.completedFiles, file.id))
|
717 | incompleteFiles.forEach((file) => {
|
718 | this.uppy.emit('postprocess-progress', file, {
|
719 | mode: 'indeterminate',
|
720 | message: this.i18n('encoding')
|
721 | })
|
722 | })
|
723 |
|
724 | const watcher = this.assemblyWatchers[uploadID]
|
725 | return watcher.promise.then(() => {
|
726 | const assemblies = assemblyIDs.map((id) => this.getAssembly(id))
|
727 |
|
728 |
|
729 |
|
730 | const state = this.getPluginState()
|
731 | const uploadsAssemblies = { ...state.uploadsAssemblies }
|
732 | delete uploadsAssemblies[uploadID]
|
733 | this.setPluginState({ uploadsAssemblies })
|
734 |
|
735 | this.uppy.addResultData(uploadID, {
|
736 | transloadit: assemblies
|
737 | })
|
738 | })
|
739 | }
|
740 |
|
741 | _onError (err = null, uploadID) {
|
742 | const state = this.getPluginState()
|
743 | const assemblyIDs = state.uploadsAssemblies[uploadID]
|
744 |
|
745 | assemblyIDs.forEach((assemblyID) => {
|
746 | if (this.activeAssemblies[assemblyID]) {
|
747 | this.activeAssemblies[assemblyID].close()
|
748 | }
|
749 | })
|
750 | }
|
751 |
|
752 | _onTusError (err) {
|
753 | if (err && /^tus: /.test(err.message)) {
|
754 | const xhr = err.originalRequest ? err.originalRequest.getUnderlyingObject() : null
|
755 | const url = xhr && xhr.responseURL ? xhr.responseURL : null
|
756 | this.client.submitError(err, { url, type: 'TUS_ERROR' }).then((_) => {
|
757 |
|
758 | })
|
759 | }
|
760 | }
|
761 |
|
762 | install () {
|
763 | this.uppy.addPreProcessor(this._prepareUpload)
|
764 | this.uppy.addPostProcessor(this._afterUpload)
|
765 |
|
766 |
|
767 | this.uppy.on('error', this._onError)
|
768 |
|
769 |
|
770 | this.uppy.on('cancel-all', this._onCancelAll)
|
771 |
|
772 |
|
773 | this.uppy.on('upload-error', this._onTusError)
|
774 |
|
775 | if (this.opts.importFromUploadURLs) {
|
776 |
|
777 | this.uppy.on('upload-success', this._onFileUploadURLAvailable)
|
778 | } else {
|
779 | this.uppy.use(Tus, {
|
780 |
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 |
|
787 |
|
788 |
|
789 | storeFingerprintForResuming: false,
|
790 | resume: false,
|
791 |
|
792 |
|
793 | useFastRemoteRetry: false,
|
794 |
|
795 | metaFields: ['assembly_url', 'filename', 'fieldname'],
|
796 |
|
797 | limit: this.opts.limit
|
798 | })
|
799 | }
|
800 |
|
801 | this.uppy.on('restore:get-data', this._getPersistentData)
|
802 | this.uppy.on('restored', this._onRestored)
|
803 |
|
804 | this.setPluginState({
|
805 |
|
806 | assemblies: {},
|
807 |
|
808 | uploadsAssemblies: {},
|
809 |
|
810 | files: {},
|
811 |
|
812 | results: []
|
813 | })
|
814 |
|
815 |
|
816 | const { capabilities } = this.uppy.getState()
|
817 | this.uppy.setState({
|
818 | capabilities: {
|
819 | ...capabilities,
|
820 | individualCancellation: false
|
821 | }
|
822 | })
|
823 | }
|
824 |
|
825 | uninstall () {
|
826 | this.uppy.removePreProcessor(this._prepareUpload)
|
827 | this.uppy.removePostProcessor(this._afterUpload)
|
828 | this.uppy.off('error', this._onError)
|
829 |
|
830 | if (this.opts.importFromUploadURLs) {
|
831 | this.uppy.off('upload-success', this._onFileUploadURLAvailable)
|
832 | }
|
833 |
|
834 | const { capabilities } = this.uppy.getState()
|
835 | this.uppy.setState({
|
836 | capabilities: {
|
837 | ...capabilities,
|
838 | individualCancellation: true
|
839 | }
|
840 | })
|
841 | }
|
842 |
|
843 | getAssembly (id) {
|
844 | const { assemblies } = this.getPluginState()
|
845 | return assemblies[id]
|
846 | }
|
847 |
|
848 | getAssemblyFiles (assemblyID) {
|
849 | return this.uppy.getFiles().filter((file) => {
|
850 | return file && file.transloadit && file.transloadit.assembly === assemblyID
|
851 | })
|
852 | }
|
853 | }
|
854 |
|
855 | module.exports.COMPANION = COMPANION
|
856 | module.exports.UPPY_SERVER = COMPANION
|
857 | module.exports.COMPANION_PATTERN = ALLOWED_COMPANION_PATTERN
|