UNPKG

32.3 kBJavaScriptView Raw
1var _class, _temp;
2
3function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4
5function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
6
7function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
8
9var Translator = require('@uppy/utils/lib/Translator');
10
11var hasProperty = require('@uppy/utils/lib/hasProperty');
12
13var _require = require('@uppy/core'),
14 Plugin = _require.Plugin;
15
16var Tus = require('@uppy/tus');
17
18var Assembly = require('./Assembly');
19
20var Client = require('./Client');
21
22var AssemblyOptions = require('./AssemblyOptions');
23
24var AssemblyWatcher = require('./AssemblyWatcher');
25
26function defaultGetAssemblyOptions(file, options) {
27 return {
28 params: options.params,
29 signature: options.signature,
30 fields: options.fields
31 };
32}
33
34var COMPANION = 'https://api2.transloadit.com/companion'; // Regex matching acceptable postMessage() origins for authentication feedback from companion.
35
36var ALLOWED_COMPANION_PATTERN = /\.transloadit\.com$/; // Regex used to check if a Companion address is run by Transloadit.
37
38var TL_COMPANION = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/companion/;
39var TL_UPPY_SERVER = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/uppy-server/;
40/**
41 * Upload files to Transloadit using Tus.
42 */
43
44module.exports = (_temp = _class = /*#__PURE__*/function (_Plugin) {
45 _inheritsLoose(Transloadit, _Plugin);
46
47 function Transloadit(uppy, opts) {
48 var _this;
49
50 _this = _Plugin.call(this, uppy, opts) || this;
51 _this.type = 'uploader';
52 _this.id = _this.opts.id || 'Transloadit';
53 _this.title = 'Transloadit';
54 _this.defaultLocale = {
55 strings: {
56 creatingAssembly: 'Preparing upload...',
57 creatingAssemblyFailed: 'Transloadit: Could not create Assembly',
58 encoding: 'Encoding...'
59 }
60 };
61 var defaultOptions = {
62 service: 'https://api2.transloadit.com',
63 errorReporting: true,
64 waitForEncoding: false,
65 waitForMetadata: false,
66 alwaysRunAssembly: false,
67 importFromUploadURLs: false,
68 signature: null,
69 params: null,
70 fields: {},
71 getAssemblyOptions: defaultGetAssemblyOptions,
72 limit: 0
73 };
74 _this.opts = _extends({}, defaultOptions, opts);
75
76 _this.i18nInit();
77
78 _this._prepareUpload = _this._prepareUpload.bind(_assertThisInitialized(_this));
79 _this._afterUpload = _this._afterUpload.bind(_assertThisInitialized(_this));
80 _this._onError = _this._onError.bind(_assertThisInitialized(_this));
81 _this._onTusError = _this._onTusError.bind(_assertThisInitialized(_this));
82 _this._onCancelAll = _this._onCancelAll.bind(_assertThisInitialized(_this));
83 _this._onFileUploadURLAvailable = _this._onFileUploadURLAvailable.bind(_assertThisInitialized(_this));
84 _this._onRestored = _this._onRestored.bind(_assertThisInitialized(_this));
85 _this._getPersistentData = _this._getPersistentData.bind(_assertThisInitialized(_this));
86 var hasCustomAssemblyOptions = _this.opts.getAssemblyOptions !== defaultOptions.getAssemblyOptions;
87
88 if (_this.opts.params) {
89 AssemblyOptions.validateParams(_this.opts.params);
90 } else if (!hasCustomAssemblyOptions) {
91 // Throw the same error that we'd throw if the `params` returned from a
92 // `getAssemblyOptions()` function is null.
93 AssemblyOptions.validateParams(null);
94 }
95
96 _this.client = new Client({
97 service: _this.opts.service,
98 client: _this._getClientVersion(),
99 errorReporting: _this.opts.errorReporting
100 }); // Contains Assembly instances for in-progress Assemblies.
101
102 _this.activeAssemblies = {}; // Contains a mapping of uploadID to AssemblyWatcher
103
104 _this.assemblyWatchers = {}; // Contains a file IDs that have completed postprocessing before the upload they belong to has entered the postprocess stage.
105
106 _this.completedFiles = Object.create(null);
107 return _this;
108 }
109
110 var _proto = Transloadit.prototype;
111
112 _proto.setOptions = function setOptions(newOpts) {
113 _Plugin.prototype.setOptions.call(this, newOpts);
114
115 this.i18nInit();
116 };
117
118 _proto.i18nInit = function i18nInit() {
119 this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]);
120 this.i18n = this.translator.translate.bind(this.translator);
121 this.i18nArray = this.translator.translateArray.bind(this.translator);
122 this.setPluginState(); // so that UI re-renders and we see the updated locale
123 };
124
125 _proto._getClientVersion = function _getClientVersion() {
126 var _this2 = this;
127
128 var list = ["uppy-core:" + this.uppy.constructor.VERSION, "uppy-transloadit:" + this.constructor.VERSION, "uppy-tus:" + Tus.VERSION];
129
130 var addPluginVersion = function addPluginVersion(pluginName, versionName) {
131 var plugin = _this2.uppy.getPlugin(pluginName);
132
133 if (plugin) {
134 list.push(versionName + ":" + plugin.constructor.VERSION);
135 }
136 };
137
138 if (this.opts.importFromUploadURLs) {
139 addPluginVersion('XHRUpload', 'uppy-xhr-upload');
140 addPluginVersion('AwsS3', 'uppy-aws-s3');
141 addPluginVersion('AwsS3Multipart', 'uppy-aws-s3-multipart');
142 }
143
144 addPluginVersion('Dropbox', 'uppy-dropbox');
145 addPluginVersion('Facebook', 'uppy-facebook');
146 addPluginVersion('GoogleDrive', 'uppy-google-drive');
147 addPluginVersion('Instagram', 'uppy-instagram');
148 addPluginVersion('OneDrive', 'uppy-onedrive');
149 addPluginVersion('Zoom', 'uppy-zoom');
150 addPluginVersion('Url', 'uppy-url');
151 return list.join(',');
152 }
153 /**
154 * Attach metadata to files to configure the Tus plugin to upload to Transloadit.
155 * Also use Transloadit's Companion
156 *
157 * See: https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus
158 *
159 * @param {object} file
160 * @param {object} status
161 */
162 ;
163
164 _proto._attachAssemblyMetadata = function _attachAssemblyMetadata(file, status) {
165 // Add the metadata parameters Transloadit needs.
166 var meta = _extends({}, file.meta, {
167 assembly_url: status.assembly_url,
168 filename: file.name,
169 fieldname: 'file'
170 }); // Add Assembly-specific Tus endpoint.
171
172
173 var tus = _extends({}, file.tus, {
174 endpoint: status.tus_url,
175 // Include X-Request-ID headers for better debugging.
176 addRequestId: true
177 }); // Set Companion location. We only add this, if 'file' has the attribute
178 // remote, because this is the criteria to identify remote files.
179 // We only replace the hostname for Transloadit's companions, so that
180 // people can also self-host them while still using Transloadit for encoding.
181
182
183 var remote = file.remote;
184
185 if (file.remote && TL_UPPY_SERVER.test(file.remote.companionUrl)) {
186 var err = new Error('The https://api2.transloadit.com/uppy-server endpoint was renamed to ' + 'https://api2.transloadit.com/companion, please update your `companionUrl` ' + 'options accordingly.'); // Explicitly log this error here because it is caught by the `createAssembly`
187 // Promise further along.
188 // That's fine, but createAssembly only shows the informer, we need something a
189 // little more noisy.
190
191 this.uppy.log(err);
192 throw err;
193 }
194
195 if (file.remote && TL_COMPANION.test(file.remote.companionUrl)) {
196 var newHost = status.companion_url.replace(/\/$/, '');
197 var path = file.remote.url.replace(file.remote.companionUrl, '').replace(/^\//, '');
198 remote = _extends({}, file.remote, {
199 companionUrl: newHost,
200 url: newHost + "/" + path
201 });
202 } // Store the Assembly ID this file is in on the file under the `transloadit` key.
203
204
205 var newFile = _extends({}, file, {
206 transloadit: {
207 assembly: status.assembly_id
208 }
209 }); // Only configure the Tus plugin if we are uploading straight to Transloadit (the default).
210
211
212 if (!this.opts.importFromUploadURLs) {
213 _extends(newFile, {
214 meta: meta,
215 tus: tus,
216 remote: remote
217 });
218 }
219
220 return newFile;
221 };
222
223 _proto._createAssembly = function _createAssembly(fileIDs, uploadID, options) {
224 var _this3 = this;
225
226 this.uppy.log('[Transloadit] Create Assembly');
227 return this.client.createAssembly({
228 params: options.params,
229 fields: options.fields,
230 expectedFiles: fileIDs.length,
231 signature: options.signature
232 }).then(function (newAssembly) {
233 var _extends2, _extends3;
234
235 var assembly = new Assembly(newAssembly);
236 var status = assembly.status;
237 var assemblyID = status.assembly_id;
238
239 var _this3$getPluginState = _this3.getPluginState(),
240 assemblies = _this3$getPluginState.assemblies,
241 uploadsAssemblies = _this3$getPluginState.uploadsAssemblies;
242
243 _this3.setPluginState({
244 // Store the Assembly status.
245 assemblies: _extends({}, assemblies, (_extends2 = {}, _extends2[assemblyID] = status, _extends2)),
246 // Store the list of Assemblies related to this upload.
247 uploadsAssemblies: _extends({}, uploadsAssemblies, (_extends3 = {}, _extends3[uploadID] = [].concat(uploadsAssemblies[uploadID], [assemblyID]), _extends3))
248 });
249
250 var _this3$uppy$getState = _this3.uppy.getState(),
251 files = _this3$uppy$getState.files;
252
253 var updatedFiles = {};
254 fileIDs.forEach(function (id) {
255 updatedFiles[id] = _this3._attachAssemblyMetadata(_this3.uppy.getFile(id), status);
256 });
257
258 _this3.uppy.setState({
259 files: _extends({}, files, updatedFiles)
260 });
261
262 _this3.uppy.emit('transloadit:assembly-created', status, fileIDs);
263
264 _this3.uppy.log("[Transloadit] Created Assembly " + assemblyID);
265
266 return assembly;
267 }).catch(function (err) {
268 err.message = _this3.i18n('creatingAssemblyFailed') + ": " + err.message; // Reject the promise.
269
270 throw err;
271 });
272 };
273
274 _proto._createAssemblyWatcher = function _createAssemblyWatcher(assemblyID, fileIDs, uploadID) {
275 var _this4 = this;
276
277 // AssemblyWatcher tracks completion states of all Assemblies in this upload.
278 var watcher = new AssemblyWatcher(this.uppy, assemblyID);
279 watcher.on('assembly-complete', function (id) {
280 var files = _this4.getAssemblyFiles(id);
281
282 files.forEach(function (file) {
283 _this4.completedFiles[file.id] = true;
284
285 _this4.uppy.emit('postprocess-complete', file);
286 });
287 });
288 watcher.on('assembly-error', function (id, error) {
289 // Clear postprocessing state for all our files.
290 var files = _this4.getAssemblyFiles(id);
291
292 files.forEach(function (file) {
293 // TODO Maybe make a postprocess-error event here?
294 _this4.uppy.emit('upload-error', file, error);
295
296 _this4.uppy.emit('postprocess-complete', file);
297 });
298 });
299 this.assemblyWatchers[uploadID] = watcher;
300 };
301
302 _proto._shouldWaitAfterUpload = function _shouldWaitAfterUpload() {
303 return this.opts.waitForEncoding || this.opts.waitForMetadata;
304 }
305 /**
306 * Used when `importFromUploadURLs` is enabled: reserves all files in
307 * the Assembly.
308 */
309 ;
310
311 _proto._reserveFiles = function _reserveFiles(assembly, fileIDs) {
312 var _this5 = this;
313
314 return Promise.all(fileIDs.map(function (fileID) {
315 var file = _this5.uppy.getFile(fileID);
316
317 return _this5.client.reserveFile(assembly, file);
318 }));
319 }
320 /**
321 * Used when `importFromUploadURLs` is enabled: adds files to the Assembly
322 * once they have been fully uploaded.
323 */
324 ;
325
326 _proto._onFileUploadURLAvailable = function _onFileUploadURLAvailable(file) {
327 var _this6 = this;
328
329 if (!file || !file.transloadit || !file.transloadit.assembly) {
330 return;
331 }
332
333 var _this$getPluginState = this.getPluginState(),
334 assemblies = _this$getPluginState.assemblies;
335
336 var assembly = assemblies[file.transloadit.assembly];
337 this.client.addFile(assembly, file).catch(function (err) {
338 _this6.uppy.log(err);
339
340 _this6.uppy.emit('transloadit:import-error', assembly, file.id, err);
341 });
342 };
343
344 _proto._findFile = function _findFile(uploadedFile) {
345 var files = this.uppy.getFiles();
346
347 for (var i = 0; i < files.length; i++) {
348 var file = files[i]; // Completed file upload.
349
350 if (file.uploadURL === uploadedFile.tus_upload_url) {
351 return file;
352 } // In-progress file upload.
353
354
355 if (file.tus && file.tus.uploadUrl === uploadedFile.tus_upload_url) {
356 return file;
357 }
358
359 if (!uploadedFile.is_tus_file) {
360 // Fingers-crossed check for non-tus uploads, eg imported from S3.
361 if (file.name === uploadedFile.name && file.size === uploadedFile.size) {
362 return file;
363 }
364 }
365 }
366 };
367
368 _proto._onFileUploadComplete = function _onFileUploadComplete(assemblyId, uploadedFile) {
369 var _extends4;
370
371 var state = this.getPluginState();
372
373 var file = this._findFile(uploadedFile);
374
375 if (!file) {
376 this.uppy.log('[Transloadit] Couldn’t file the file, it was likely removed in the process');
377 return;
378 }
379
380 this.setPluginState({
381 files: _extends({}, state.files, (_extends4 = {}, _extends4[uploadedFile.id] = {
382 assembly: assemblyId,
383 id: file.id,
384 uploadedFile: uploadedFile
385 }, _extends4))
386 });
387 this.uppy.emit('transloadit:upload', uploadedFile, this.getAssembly(assemblyId));
388 }
389 /**
390 * Callback when a new Assembly result comes in.
391 *
392 * @param {string} assemblyId
393 * @param {string} stepName
394 * @param {object} result
395 */
396 ;
397
398 _proto._onResult = function _onResult(assemblyId, stepName, result) {
399 var state = this.getPluginState();
400 var file = state.files[result.original_id]; // The `file` may not exist if an import robot was used instead of a file upload.
401
402 result.localId = file ? file.id : null;
403 var entry = {
404 result: result,
405 stepName: stepName,
406 id: result.id,
407 assembly: assemblyId
408 };
409 this.setPluginState({
410 results: [].concat(state.results, [entry])
411 });
412 this.uppy.emit('transloadit:result', stepName, result, this.getAssembly(assemblyId));
413 }
414 /**
415 * When an Assembly has finished processing, get the final state
416 * and emit it.
417 *
418 * @param {object} status
419 */
420 ;
421
422 _proto._onAssemblyFinished = function _onAssemblyFinished(status) {
423 var _this7 = this;
424
425 var url = status.assembly_ssl_url;
426 this.client.getAssemblyStatus(url).then(function (finalStatus) {
427 var _extends5;
428
429 var assemblyId = finalStatus.assembly_id;
430
431 var state = _this7.getPluginState();
432
433 _this7.setPluginState({
434 assemblies: _extends({}, state.assemblies, (_extends5 = {}, _extends5[assemblyId] = finalStatus, _extends5))
435 });
436
437 _this7.uppy.emit('transloadit:complete', finalStatus);
438 });
439 };
440
441 _proto._cancelAssembly = function _cancelAssembly(assembly) {
442 var _this8 = this;
443
444 return this.client.cancelAssembly(assembly).then(function () {
445 // TODO bubble this through AssemblyWatcher so its event handlers can clean up correctly
446 _this8.uppy.emit('transloadit:assembly-cancelled', assembly);
447 });
448 }
449 /**
450 * When all files are removed, cancel in-progress Assemblies.
451 */
452 ;
453
454 _proto._onCancelAll = function _onCancelAll() {
455 var _this9 = this;
456
457 var _this$getPluginState2 = this.getPluginState(),
458 uploadsAssemblies = _this$getPluginState2.uploadsAssemblies;
459
460 var assemblyIDs = Object.keys(uploadsAssemblies).reduce(function (acc, uploadID) {
461 acc.push.apply(acc, uploadsAssemblies[uploadID]);
462 return acc;
463 }, []);
464 var cancelPromises = assemblyIDs.map(function (assemblyID) {
465 var assembly = _this9.getAssembly(assemblyID);
466
467 return _this9._cancelAssembly(assembly);
468 });
469 Promise.all(cancelPromises).catch(function (err) {
470 _this9.uppy.log(err);
471 });
472 }
473 /**
474 * Custom state serialization for the Golden Retriever plugin.
475 * It will pass this back to the `_onRestored` function.
476 *
477 * @param {Function} setData
478 */
479 ;
480
481 _proto._getPersistentData = function _getPersistentData(setData) {
482 var _setData;
483
484 var state = this.getPluginState();
485 var assemblies = state.assemblies;
486 var uploadsAssemblies = state.uploadsAssemblies;
487 setData((_setData = {}, _setData[this.id] = {
488 assemblies: assemblies,
489 uploadsAssemblies: uploadsAssemblies
490 }, _setData));
491 };
492
493 _proto._onRestored = function _onRestored(pluginData) {
494 var _this10 = this;
495
496 var savedState = pluginData && pluginData[this.id] ? pluginData[this.id] : {};
497 var previousAssemblies = savedState.assemblies || {};
498 var uploadsAssemblies = savedState.uploadsAssemblies || {};
499
500 if (Object.keys(uploadsAssemblies).length === 0) {
501 // Nothing to restore.
502 return;
503 } // Convert loaded Assembly statuses to a Transloadit plugin state object.
504
505
506 var restoreState = function restoreState(assemblies) {
507 var files = {};
508 var results = [];
509 Object.keys(assemblies).forEach(function (id) {
510 var status = assemblies[id];
511 status.uploads.forEach(function (uploadedFile) {
512 var file = _this10._findFile(uploadedFile);
513
514 files[uploadedFile.id] = {
515 id: file.id,
516 assembly: id,
517 uploadedFile: uploadedFile
518 };
519 });
520
521 var state = _this10.getPluginState();
522
523 Object.keys(status.results).forEach(function (stepName) {
524 status.results[stepName].forEach(function (result) {
525 var file = state.files[result.original_id];
526 result.localId = file ? file.id : null;
527 results.push({
528 id: result.id,
529 result: result,
530 stepName: stepName,
531 assembly: id
532 });
533 });
534 });
535 });
536
537 _this10.setPluginState({
538 assemblies: assemblies,
539 files: files,
540 results: results,
541 uploadsAssemblies: uploadsAssemblies
542 });
543 }; // Set up the Assembly instances and AssemblyWatchers for existing Assemblies.
544
545
546 var restoreAssemblies = function restoreAssemblies() {
547 var _this10$getPluginStat = _this10.getPluginState(),
548 assemblies = _this10$getPluginStat.assemblies,
549 uploadsAssemblies = _this10$getPluginStat.uploadsAssemblies; // Set up the assembly watchers again for all the ongoing uploads.
550
551
552 Object.keys(uploadsAssemblies).forEach(function (uploadID) {
553 var assemblyIDs = uploadsAssemblies[uploadID];
554 var fileIDsInUpload = assemblyIDs.reduce(function (acc, assemblyID) {
555 var fileIDsInAssembly = _this10.getAssemblyFiles(assemblyID).map(function (file) {
556 return file.id;
557 });
558
559 acc.push.apply(acc, fileIDsInAssembly);
560 return acc;
561 }, []);
562
563 _this10._createAssemblyWatcher(assemblyIDs, fileIDsInUpload, uploadID);
564 });
565 var allAssemblyIDs = Object.keys(assemblies);
566 allAssemblyIDs.forEach(function (id) {
567 var assembly = new Assembly(assemblies[id]);
568
569 _this10._connectAssembly(assembly);
570 });
571 }; // Force-update all Assemblies to check for missed events.
572
573
574 var updateAssemblies = function updateAssemblies() {
575 var _this10$getPluginStat2 = _this10.getPluginState(),
576 assemblies = _this10$getPluginStat2.assemblies;
577
578 return Promise.all(Object.keys(assemblies).map(function (id) {
579 return _this10.activeAssemblies[id].update();
580 }));
581 }; // Restore all Assembly state.
582
583
584 this.restored = Promise.resolve().then(function () {
585 restoreState(previousAssemblies);
586 restoreAssemblies();
587 return updateAssemblies();
588 });
589 this.restored.then(function () {
590 _this10.restored = null;
591 });
592 };
593
594 _proto._connectAssembly = function _connectAssembly(assembly) {
595 var _this11 = this;
596
597 var status = assembly.status;
598 var id = status.assembly_id;
599 this.activeAssemblies[id] = assembly; // Sync local `assemblies` state
600
601 assembly.on('status', function (newStatus) {
602 var _extends6;
603
604 var _this11$getPluginStat = _this11.getPluginState(),
605 assemblies = _this11$getPluginStat.assemblies;
606
607 _this11.setPluginState({
608 assemblies: _extends({}, assemblies, (_extends6 = {}, _extends6[id] = newStatus, _extends6))
609 });
610 });
611 assembly.on('upload', function (file) {
612 _this11._onFileUploadComplete(id, file);
613 });
614 assembly.on('error', function (error) {
615 error.assembly = assembly.status;
616
617 _this11.uppy.emit('transloadit:assembly-error', assembly.status, error);
618 });
619 assembly.on('executing', function () {
620 _this11.uppy.emit('transloadit:assembly-executing', assembly.status);
621 });
622
623 if (this.opts.waitForEncoding) {
624 assembly.on('result', function (stepName, result) {
625 _this11._onResult(id, stepName, result);
626 });
627 }
628
629 if (this.opts.waitForEncoding) {
630 assembly.on('finished', function () {
631 _this11._onAssemblyFinished(assembly.status);
632 });
633 } else if (this.opts.waitForMetadata) {
634 assembly.on('metadata', function () {
635 _this11._onAssemblyFinished(assembly.status);
636 });
637 } // No need to connect to the socket if the Assembly has completed by now.
638
639
640 if (assembly.ok === 'ASSEMBLY_COMPLETE') {
641 return assembly;
642 } // TODO Do we still need this for anything…?
643 // eslint-disable-next-line no-unused-vars
644
645
646 var connected = new Promise(function (resolve, reject) {
647 assembly.once('connect', resolve);
648 assembly.once('status', resolve);
649 assembly.once('error', reject);
650 }).then(function () {
651 _this11.uppy.log('[Transloadit] Socket is ready');
652 });
653 assembly.connect();
654 return assembly;
655 };
656
657 _proto._prepareUpload = function _prepareUpload(fileIDs, uploadID) {
658 var _this12 = this,
659 _extends7;
660
661 // Only use files without errors
662 fileIDs = fileIDs.filter(function (file) {
663 return !file.error;
664 });
665 fileIDs.forEach(function (fileID) {
666 var file = _this12.uppy.getFile(fileID);
667
668 _this12.uppy.emit('preprocess-progress', file, {
669 mode: 'indeterminate',
670 message: _this12.i18n('creatingAssembly')
671 });
672 });
673
674 var createAssembly = function createAssembly(_ref) {
675 var fileIDs = _ref.fileIDs,
676 options = _ref.options;
677 var createdAssembly;
678 return _this12._createAssembly(fileIDs, uploadID, options).then(function (assembly) {
679 createdAssembly = assembly;
680
681 if (_this12.opts.importFromUploadURLs) {
682 return _this12._reserveFiles(assembly, fileIDs);
683 }
684 }).then(function () {
685 fileIDs.forEach(function (fileID) {
686 var file = _this12.uppy.getFile(fileID);
687
688 _this12.uppy.emit('preprocess-complete', file);
689 });
690 return createdAssembly;
691 }).catch(function (err) {
692 fileIDs.forEach(function (fileID) {
693 var file = _this12.uppy.getFile(fileID); // Clear preprocessing state when the Assembly could not be created,
694 // otherwise the UI gets confused about the lingering progress keys
695
696
697 _this12.uppy.emit('preprocess-complete', file);
698
699 _this12.uppy.emit('upload-error', file, err);
700 });
701 throw err;
702 });
703 };
704
705 var _this$getPluginState3 = this.getPluginState(),
706 uploadsAssemblies = _this$getPluginState3.uploadsAssemblies;
707
708 this.setPluginState({
709 uploadsAssemblies: _extends({}, uploadsAssemblies, (_extends7 = {}, _extends7[uploadID] = [], _extends7))
710 });
711 var files = fileIDs.map(function (id) {
712 return _this12.uppy.getFile(id);
713 });
714 var assemblyOptions = new AssemblyOptions(files, this.opts);
715 return assemblyOptions.build().then(function (assemblies) {
716 return Promise.all(assemblies.map(createAssembly)).then(function (createdAssemblies) {
717 var assemblyIDs = createdAssemblies.map(function (assembly) {
718 return assembly.status.assembly_id;
719 });
720
721 _this12._createAssemblyWatcher(assemblyIDs, fileIDs, uploadID);
722
723 createdAssemblies.map(function (assembly) {
724 return _this12._connectAssembly(assembly);
725 });
726 });
727 }, // If something went wrong before any Assemblies could be created,
728 // clear all processing state.
729 function (err) {
730 fileIDs.forEach(function (fileID) {
731 var file = _this12.uppy.getFile(fileID);
732
733 _this12.uppy.emit('preprocess-complete', file);
734
735 _this12.uppy.emit('upload-error', file, err);
736 });
737 throw err;
738 });
739 };
740
741 _proto._afterUpload = function _afterUpload(fileIDs, uploadID) {
742 var _this13 = this;
743
744 var files = fileIDs.map(function (fileID) {
745 return _this13.uppy.getFile(fileID);
746 }); // Only use files without errors
747
748 fileIDs = files.filter(function (file) {
749 return !file.error;
750 }).map(function (file) {
751 return file.id;
752 });
753 var state = this.getPluginState(); // If we're still restoring state, wait for that to be done.
754
755 if (this.restored) {
756 return this.restored.then(function () {
757 return _this13._afterUpload(fileIDs, uploadID);
758 });
759 }
760
761 var assemblyIDs = state.uploadsAssemblies[uploadID]; // If we don't have to wait for encoding metadata or results, we can close
762 // the socket immediately and finish the upload.
763
764 if (!this._shouldWaitAfterUpload()) {
765 assemblyIDs.forEach(function (assemblyID) {
766 var assembly = _this13.activeAssemblies[assemblyID];
767 assembly.close();
768 delete _this13.activeAssemblies[assemblyID];
769 });
770 var assemblies = assemblyIDs.map(function (id) {
771 return _this13.getAssembly(id);
772 });
773 this.uppy.addResultData(uploadID, {
774 transloadit: assemblies
775 });
776 return Promise.resolve();
777 } // If no Assemblies were created for this upload, we also do not have to wait.
778 // There's also no sockets or anything to close, so just return immediately.
779
780
781 if (assemblyIDs.length === 0) {
782 this.uppy.addResultData(uploadID, {
783 transloadit: []
784 });
785 return Promise.resolve();
786 }
787
788 var incompleteFiles = files.filter(function (file) {
789 return !hasProperty(_this13.completedFiles, file.id);
790 });
791 incompleteFiles.forEach(function (file) {
792 _this13.uppy.emit('postprocess-progress', file, {
793 mode: 'indeterminate',
794 message: _this13.i18n('encoding')
795 });
796 });
797 var watcher = this.assemblyWatchers[uploadID];
798 return watcher.promise.then(function () {
799 var assemblies = assemblyIDs.map(function (id) {
800 return _this13.getAssembly(id);
801 }); // Remove the Assembly ID list for this upload,
802 // it's no longer going to be used anywhere.
803
804 var state = _this13.getPluginState();
805
806 var uploadsAssemblies = _extends({}, state.uploadsAssemblies);
807
808 delete uploadsAssemblies[uploadID];
809
810 _this13.setPluginState({
811 uploadsAssemblies: uploadsAssemblies
812 });
813
814 _this13.uppy.addResultData(uploadID, {
815 transloadit: assemblies
816 });
817 });
818 };
819
820 _proto._onError = function _onError(err, uploadID) {
821 var _this14 = this;
822
823 if (err === void 0) {
824 err = null;
825 }
826
827 var state = this.getPluginState();
828 var assemblyIDs = state.uploadsAssemblies[uploadID];
829 assemblyIDs.forEach(function (assemblyID) {
830 if (_this14.activeAssemblies[assemblyID]) {
831 _this14.activeAssemblies[assemblyID].close();
832 }
833 });
834 };
835
836 _proto._onTusError = function _onTusError(err) {
837 if (err && /^tus: /.test(err.message)) {
838 var xhr = err.originalRequest ? err.originalRequest.getUnderlyingObject() : null;
839 var url = xhr && xhr.responseURL ? xhr.responseURL : null;
840 this.client.submitError(err, {
841 url: url,
842 type: 'TUS_ERROR'
843 }).then(function (_) {// if we can't report the error that sucks
844 });
845 }
846 };
847
848 _proto.install = function install() {
849 this.uppy.addPreProcessor(this._prepareUpload);
850 this.uppy.addPostProcessor(this._afterUpload); // We may need to close socket.io connections on error.
851
852 this.uppy.on('error', this._onError); // Handle cancellation.
853
854 this.uppy.on('cancel-all', this._onCancelAll); // For error reporting.
855
856 this.uppy.on('upload-error', this._onTusError);
857
858 if (this.opts.importFromUploadURLs) {
859 // No uploader needed when importing; instead we take the upload URL from an existing uploader.
860 this.uppy.on('upload-success', this._onFileUploadURLAvailable);
861 } else {
862 this.uppy.use(Tus, {
863 // Disable tus-js-client fingerprinting, otherwise uploading the same file at different times
864 // will upload to an outdated Assembly, and we won't get socket events for it.
865 //
866 // To resume a Transloadit upload, we need to reconnect to the websocket, and the state that's
867 // required to do that is not saved by tus-js-client's fingerprinting. We need the tus URL,
868 // the Assembly URL, and the WebSocket URL, at least. We also need to know _all_ the files that
869 // were added to the Assembly, so we can properly complete it. All that state is handled by
870 // Golden Retriever. So, Golden Retriever is required to do resumability with the Transloadit plugin,
871 // and we disable Tus's default resume implementation to prevent bad behaviours.
872 storeFingerprintForResuming: false,
873 resume: false,
874 // Disable Companion's retry optimisation; we need to change the endpoint on retry
875 // so it can't just reuse the same tus.Upload instance server-side.
876 useFastRemoteRetry: false,
877 // Only send Assembly metadata to the tus endpoint.
878 metaFields: ['assembly_url', 'filename', 'fieldname'],
879 // Pass the limit option to @uppy/tus
880 limit: this.opts.limit
881 });
882 }
883
884 this.uppy.on('restore:get-data', this._getPersistentData);
885 this.uppy.on('restored', this._onRestored);
886 this.setPluginState({
887 // Contains Assembly status objects, indexed by their ID.
888 assemblies: {},
889 // Contains arrays of Assembly IDs, indexed by the upload ID that they belong to.
890 uploadsAssemblies: {},
891 // Contains file data from Transloadit, indexed by their Transloadit-assigned ID.
892 files: {},
893 // Contains result data from Transloadit.
894 results: []
895 }); // We cannot cancel individual files because Assemblies tend to contain many files.
896
897 var _this$uppy$getState = this.uppy.getState(),
898 capabilities = _this$uppy$getState.capabilities;
899
900 this.uppy.setState({
901 capabilities: _extends({}, capabilities, {
902 individualCancellation: false
903 })
904 });
905 };
906
907 _proto.uninstall = function uninstall() {
908 this.uppy.removePreProcessor(this._prepareUpload);
909 this.uppy.removePostProcessor(this._afterUpload);
910 this.uppy.off('error', this._onError);
911
912 if (this.opts.importFromUploadURLs) {
913 this.uppy.off('upload-success', this._onFileUploadURLAvailable);
914 }
915
916 var _this$uppy$getState2 = this.uppy.getState(),
917 capabilities = _this$uppy$getState2.capabilities;
918
919 this.uppy.setState({
920 capabilities: _extends({}, capabilities, {
921 individualCancellation: true
922 })
923 });
924 };
925
926 _proto.getAssembly = function getAssembly(id) {
927 var _this$getPluginState4 = this.getPluginState(),
928 assemblies = _this$getPluginState4.assemblies;
929
930 return assemblies[id];
931 };
932
933 _proto.getAssemblyFiles = function getAssemblyFiles(assemblyID) {
934 return this.uppy.getFiles().filter(function (file) {
935 return file && file.transloadit && file.transloadit.assembly === assemblyID;
936 });
937 };
938
939 return Transloadit;
940}(Plugin), _class.VERSION = "1.6.8", _temp);
941module.exports.COMPANION = COMPANION;
942module.exports.UPPY_SERVER = COMPANION;
943module.exports.COMPANION_PATTERN = ALLOWED_COMPANION_PATTERN;
\No newline at end of file