UNPKG

@uppy/transloadit

Version:

The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more

819 lines (801 loc) 32.7 kB
function _classPrivateFieldLooseBase(e, t) { if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance"); return e; } var id = 0; function _classPrivateFieldLooseKey(e) { return "__private_" + id++ + "_" + e; } import hasProperty from '@uppy/utils/lib/hasProperty'; import ErrorWithCause from '@uppy/utils/lib/ErrorWithCause'; import { RateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'; import Tus from '@uppy/tus'; import { BasePlugin } from '@uppy/core'; import Assembly from './Assembly.js'; import Client from './Client.js'; import AssemblyWatcher from './AssemblyWatcher.js'; import locale from './locale.js'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore We don't want TS to generate types for the package.json const packageJson = { "version": "4.2.1" }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const defaultOptions = { service: 'https://api2.transloadit.com', errorReporting: true, waitForEncoding: false, waitForMetadata: false, alwaysRunAssembly: false, importFromUploadURLs: false, limit: 20, retryDelays: [7000, 10000, 15000, 20000], clientName: null }; /** * State we want to store in Golden Retriever to be able to recover uploads. */ const sendErrorToConsole = originalErr => err => { const error = new ErrorWithCause('Failed to send error to the client', { cause: err }); // eslint-disable-next-line no-console console.error(error, originalErr); }; function validateParams(params) { if (params == null) { throw new Error('Transloadit: The `params` option is required.'); } if (typeof params === 'string') { try { // eslint-disable-next-line no-param-reassign params = JSON.parse(params); } catch (err) { // Tell the user that this is not an Uppy bug! throw new ErrorWithCause('Transloadit: The `params` option is a malformed JSON string.', { cause: err }); } } if (!params.auth || !params.auth.key) { throw new Error('Transloadit: The `params.auth.key` option is required. ' + 'You can find your Transloadit API key at https://transloadit.com/c/template-credentials'); } } const COMPANION_URL = 'https://api2.transloadit.com/companion'; // Regex matching acceptable postMessage() origins for authentication feedback from companion. const COMPANION_ALLOWED_HOSTS = /\.transloadit\.com$/; // Regex used to check if a Companion address is run by Transloadit. const TL_COMPANION = /https?:\/\/api2(?:-\w+)?\.transloadit\.com\/companion/; /** * Upload files to Transloadit using Tus. */ var _rateLimitedQueue = /*#__PURE__*/_classPrivateFieldLooseKey("rateLimitedQueue"); var _watcher = /*#__PURE__*/_classPrivateFieldLooseKey("watcher"); var _getClientVersion = /*#__PURE__*/_classPrivateFieldLooseKey("getClientVersion"); var _attachAssemblyMetadata = /*#__PURE__*/_classPrivateFieldLooseKey("attachAssemblyMetadata"); var _createAssembly = /*#__PURE__*/_classPrivateFieldLooseKey("createAssembly"); var _createAssemblyWatcher = /*#__PURE__*/_classPrivateFieldLooseKey("createAssemblyWatcher"); var _shouldWaitAfterUpload = /*#__PURE__*/_classPrivateFieldLooseKey("shouldWaitAfterUpload"); var _reserveFiles = /*#__PURE__*/_classPrivateFieldLooseKey("reserveFiles"); var _onFileUploadURLAvailable = /*#__PURE__*/_classPrivateFieldLooseKey("onFileUploadURLAvailable"); var _findFile = /*#__PURE__*/_classPrivateFieldLooseKey("findFile"); var _onFileUploadComplete = /*#__PURE__*/_classPrivateFieldLooseKey("onFileUploadComplete"); var _onResult = /*#__PURE__*/_classPrivateFieldLooseKey("onResult"); var _onAssemblyFinished = /*#__PURE__*/_classPrivateFieldLooseKey("onAssemblyFinished"); var _cancelAssembly = /*#__PURE__*/_classPrivateFieldLooseKey("cancelAssembly"); var _onCancelAll = /*#__PURE__*/_classPrivateFieldLooseKey("onCancelAll"); var _getPersistentData = /*#__PURE__*/_classPrivateFieldLooseKey("getPersistentData"); var _onRestored = /*#__PURE__*/_classPrivateFieldLooseKey("onRestored"); var _connectAssembly = /*#__PURE__*/_classPrivateFieldLooseKey("connectAssembly"); var _prepareUpload = /*#__PURE__*/_classPrivateFieldLooseKey("prepareUpload"); var _afterUpload = /*#__PURE__*/_classPrivateFieldLooseKey("afterUpload"); var _closeAssemblyIfExists = /*#__PURE__*/_classPrivateFieldLooseKey("closeAssemblyIfExists"); var _onError = /*#__PURE__*/_classPrivateFieldLooseKey("onError"); var _onTusError = /*#__PURE__*/_classPrivateFieldLooseKey("onTusError"); export default class Transloadit extends BasePlugin { constructor(uppy, opts) { super(uppy, { ...defaultOptions, ...opts }); Object.defineProperty(this, _connectAssembly, { value: _connectAssembly2 }); Object.defineProperty(this, _cancelAssembly, { value: _cancelAssembly2 }); /** * When an Assembly has finished processing, get the final state * and emit it. */ Object.defineProperty(this, _onAssemblyFinished, { value: _onAssemblyFinished2 }); Object.defineProperty(this, _onResult, { value: _onResult2 }); Object.defineProperty(this, _onFileUploadComplete, { value: _onFileUploadComplete2 }); Object.defineProperty(this, _findFile, { value: _findFile2 }); /** * Used when `importFromUploadURLs` is enabled: reserves all files in * the Assembly. */ Object.defineProperty(this, _reserveFiles, { value: _reserveFiles2 }); Object.defineProperty(this, _shouldWaitAfterUpload, { value: _shouldWaitAfterUpload2 }); Object.defineProperty(this, _createAssemblyWatcher, { value: _createAssemblyWatcher2 }); Object.defineProperty(this, _createAssembly, { value: _createAssembly2 }); /** * Attach metadata to files to configure the Tus plugin to upload to Transloadit. * Also use Transloadit's Companion * * See: https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus */ Object.defineProperty(this, _attachAssemblyMetadata, { value: _attachAssemblyMetadata2 }); Object.defineProperty(this, _getClientVersion, { value: _getClientVersion2 }); Object.defineProperty(this, _rateLimitedQueue, { writable: true, value: void 0 }); Object.defineProperty(this, _watcher, { writable: true, value: void 0 }); this.restored = null; /** * Used when `importFromUploadURLs` is enabled: adds files to the Assembly * once they have been fully uploaded. */ Object.defineProperty(this, _onFileUploadURLAvailable, { writable: true, value: rawFile => { var _file$transloadit; const file = this.uppy.getFile(rawFile.id); if (!(file != null && (_file$transloadit = file.transloadit) != null && _file$transloadit.assembly)) { return; } const { status } = this.assembly; this.client.addFile(status, file).catch(err => { this.uppy.log(err); this.uppy.emit('transloadit:import-error', status, file.id, err); }); } }); /** * When all files are removed, cancel in-progress Assemblies. */ Object.defineProperty(this, _onCancelAll, { writable: true, value: async () => { if (!this.assembly) return; try { await _classPrivateFieldLooseBase(this, _cancelAssembly)[_cancelAssembly](this.assembly.status); } catch (err) { this.uppy.log(err); } } }); /** * Custom state serialization for the Golden Retriever plugin. * It will pass this back to the `_onRestored` function. */ Object.defineProperty(this, _getPersistentData, { writable: true, value: setData => { if (this.assembly) { setData({ [this.id]: { assemblyResponse: this.assembly.status } }); } } }); Object.defineProperty(this, _onRestored, { writable: true, value: pluginData => { const savedState = pluginData && pluginData[this.id] ? pluginData[this.id] : {}; const previousAssembly = savedState.assemblyResponse; if (!previousAssembly) { // Nothing to restore. return; } // Convert loaded Assembly statuses to a Transloadit plugin state object. const restoreState = () => { const files = {}; const results = []; const { assembly_id: id } = previousAssembly; previousAssembly.uploads.forEach(uploadedFile => { const file = _classPrivateFieldLooseBase(this, _findFile)[_findFile](uploadedFile); files[uploadedFile.id] = { id: file.id, assembly: id, uploadedFile }; }); const state = this.getPluginState(); Object.keys(previousAssembly.results).forEach(stepName => { for (const result of previousAssembly.results[stepName]) { const file = state.files[result.original_id]; result.localId = file ? file.id : null; results.push({ id: result.id, result, stepName, assembly: id }); } }); this.assembly = new Assembly(previousAssembly, _classPrivateFieldLooseBase(this, _rateLimitedQueue)[_rateLimitedQueue]); this.assembly.status = previousAssembly; this.setPluginState({ files, results }); return files; }; // Set up the Assembly instances and AssemblyWatchers for existing Assemblies. const restoreAssemblies = ids => { _classPrivateFieldLooseBase(this, _createAssemblyWatcher)[_createAssemblyWatcher](previousAssembly.assembly_id); _classPrivateFieldLooseBase(this, _connectAssembly)[_connectAssembly](this.assembly, ids); }; // Force-update Assembly to check for missed events. const updateAssembly = () => { var _this$assembly; return (_this$assembly = this.assembly) == null ? void 0 : _this$assembly.update(); }; // Restore all Assembly state. this.restored = (async () => { const files = restoreState(); restoreAssemblies(Object.keys(files)); await updateAssembly(); this.restored = null; })(); this.restored.catch(err => { this.uppy.log('Failed to restore', err); }); } }); Object.defineProperty(this, _prepareUpload, { writable: true, value: async fileIDs => { var _assemblyOptions$fiel; const assemblyOptions = typeof this.opts.assemblyOptions === 'function' ? await this.opts.assemblyOptions() : this.opts.assemblyOptions; (_assemblyOptions$fiel = assemblyOptions.fields) != null ? _assemblyOptions$fiel : assemblyOptions.fields = {}; validateParams(assemblyOptions.params); try { var _this$assembly2; const assembly = // this.assembly can already be defined if we recovered files with Golden Retriever (this.#onRestored) (_this$assembly2 = this.assembly) != null ? _this$assembly2 : await _classPrivateFieldLooseBase(this, _createAssembly)[_createAssembly](fileIDs, assemblyOptions); if (assembly == null) throw new Error('All files were canceled after assembly was created'); if (this.opts.importFromUploadURLs) { await _classPrivateFieldLooseBase(this, _reserveFiles)[_reserveFiles](assembly, fileIDs); } fileIDs.forEach(fileID => { const file = this.uppy.getFile(fileID); this.uppy.emit('preprocess-complete', file); }); _classPrivateFieldLooseBase(this, _createAssemblyWatcher)[_createAssemblyWatcher](assembly.status.assembly_id); _classPrivateFieldLooseBase(this, _connectAssembly)[_connectAssembly](assembly, fileIDs); } catch (err) { fileIDs.forEach(fileID => { const file = this.uppy.getFile(fileID); // Clear preprocessing state when the Assembly could not be created, // otherwise the UI gets confused about the lingering progress keys this.uppy.emit('preprocess-complete', file); this.uppy.emit('upload-error', file, err); }); throw err; } } }); Object.defineProperty(this, _afterUpload, { writable: true, value: async (fileIDs, uploadID) => { try { var _this$assembly3, _this$assembly6; // If we're still restoring state, wait for that to be done. await this.restored; const files = fileIDs.map(fileID => this.uppy.getFile(fileID)) // Only use files without errors .filter(file => !file.error); const assemblyID = (_this$assembly3 = this.assembly) == null ? void 0 : _this$assembly3.status.assembly_id; const closeSocketConnections = () => { var _this$assembly4; (_this$assembly4 = this.assembly) == null || _this$assembly4.close(); }; // If we don't have to wait for encoding metadata or results, we can close // the socket immediately and finish the upload. if (!_classPrivateFieldLooseBase(this, _shouldWaitAfterUpload)[_shouldWaitAfterUpload]()) { var _this$assembly5; closeSocketConnections(); const status = (_this$assembly5 = this.assembly) == null ? void 0 : _this$assembly5.status; if (status != null) { this.uppy.addResultData(uploadID, { transloadit: [status] }); } return; } // If no Assemblies were created for this upload, we also do not have to wait. // There's also no sockets or anything to close, so just return immediately. if (!assemblyID) { this.uppy.addResultData(uploadID, { transloadit: [] }); return; } const incompleteFiles = files.filter(file => !hasProperty(this.completedFiles, file.id)); incompleteFiles.forEach(file => { this.uppy.emit('postprocess-progress', file, { mode: 'indeterminate', message: this.i18n('encoding') }); }); await _classPrivateFieldLooseBase(this, _watcher)[_watcher].promise; // assembly is now done processing! closeSocketConnections(); const status = (_this$assembly6 = this.assembly) == null ? void 0 : _this$assembly6.status; if (status != null) { this.uppy.addResultData(uploadID, { transloadit: [status] }); } } finally { // in case allowMultipleUploadBatches is true and the user wants to upload again, // we need to allow a new assembly to be created. // see https://github.com/transloadit/uppy/issues/5397 this.assembly = undefined; } } }); Object.defineProperty(this, _closeAssemblyIfExists, { writable: true, value: () => { var _this$assembly7; (_this$assembly7 = this.assembly) == null || _this$assembly7.close(); } }); Object.defineProperty(this, _onError, { writable: true, value: err => { _classPrivateFieldLooseBase(this, _closeAssemblyIfExists)[_closeAssemblyIfExists](); this.assembly = undefined; this.client.submitError(err) // if we can't report the error that sucks .catch(sendErrorToConsole(err)); } }); Object.defineProperty(this, _onTusError, { writable: true, value: (_, err) => { var _err$message; _classPrivateFieldLooseBase(this, _closeAssemblyIfExists)[_closeAssemblyIfExists](); if (err != null && (_err$message = err.message) != null && _err$message.startsWith('tus: ')) { var _originalRequest; const endpoint = (_originalRequest = err.originalRequest) == null || (_originalRequest = _originalRequest.getUnderlyingObject()) == null ? void 0 : _originalRequest.responseURL; this.client.submitError(err, { endpoint }) // if we can't report the error that sucks .catch(sendErrorToConsole(err)); } } }); this.type = 'uploader'; this.id = this.opts.id || 'Transloadit'; this.defaultLocale = locale; _classPrivateFieldLooseBase(this, _rateLimitedQueue)[_rateLimitedQueue] = new RateLimitedQueue(this.opts.limit); this.i18nInit(); this.client = new Client({ service: this.opts.service, client: _classPrivateFieldLooseBase(this, _getClientVersion)[_getClientVersion](), errorReporting: this.opts.errorReporting, rateLimitedQueue: _classPrivateFieldLooseBase(this, _rateLimitedQueue)[_rateLimitedQueue] }); // Contains a file IDs that have completed postprocessing before the upload // they belong to has entered the postprocess stage. this.completedFiles = Object.create(null); } install() { this.uppy.addPreProcessor(_classPrivateFieldLooseBase(this, _prepareUpload)[_prepareUpload]); this.uppy.addPostProcessor(_classPrivateFieldLooseBase(this, _afterUpload)[_afterUpload]); // We may need to close socket.io connections on error. this.uppy.on('error', _classPrivateFieldLooseBase(this, _onError)[_onError]); // Handle cancellation. this.uppy.on('cancel-all', _classPrivateFieldLooseBase(this, _onCancelAll)[_onCancelAll]); this.uppy.on('upload-error', _classPrivateFieldLooseBase(this, _onTusError)[_onTusError]); if (this.opts.importFromUploadURLs) { // No uploader needed when importing; instead we take the upload URL from an existing uploader. this.uppy.on('upload-success', _classPrivateFieldLooseBase(this, _onFileUploadURLAvailable)[_onFileUploadURLAvailable]); } else { // we don't need it here. // the regional endpoint from the Transloadit API before we can set it. this.uppy.use(Tus, { // Disable tus-js-client fingerprinting, otherwise uploading the same file at different times // will upload to an outdated Assembly, and we won't get socket events for it. // // To resume a Transloadit upload, we need to reconnect to the websocket, and the state that's // required to do that is not saved by tus-js-client's fingerprinting. We need the tus URL, // the Assembly URL, and the WebSocket URL, at least. We also need to know _all_ the files that // were added to the Assembly, so we can properly complete it. All that state is handled by // Golden Retriever. So, Golden Retriever is required to do resumability with the Transloadit plugin, // and we disable Tus's default resume implementation to prevent bad behaviours. storeFingerprintForResuming: false, // Send all metadata to Transloadit. Metadata set by the user // ends up as in the template as `file.user_meta` allowedMetaFields: true, // Pass the limit option to @uppy/tus limit: this.opts.limit, rateLimitedQueue: _classPrivateFieldLooseBase(this, _rateLimitedQueue)[_rateLimitedQueue], retryDelays: this.opts.retryDelays }); } this.uppy.on('restore:get-data', _classPrivateFieldLooseBase(this, _getPersistentData)[_getPersistentData]); this.uppy.on('restored', _classPrivateFieldLooseBase(this, _onRestored)[_onRestored]); this.setPluginState({ // Contains file data from Transloadit, indexed by their Transloadit-assigned ID. files: {}, // Contains result data from Transloadit. results: [] }); // We cannot cancel individual files because Assemblies tend to contain many files. const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, individualCancellation: false } }); } uninstall() { this.uppy.removePreProcessor(_classPrivateFieldLooseBase(this, _prepareUpload)[_prepareUpload]); this.uppy.removePostProcessor(_classPrivateFieldLooseBase(this, _afterUpload)[_afterUpload]); this.uppy.off('error', _classPrivateFieldLooseBase(this, _onError)[_onError]); if (this.opts.importFromUploadURLs) { this.uppy.off('upload-success', _classPrivateFieldLooseBase(this, _onFileUploadURLAvailable)[_onFileUploadURLAvailable]); } const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, individualCancellation: true } }); } getAssembly() { var _this$assembly8; return (_this$assembly8 = this.assembly) == null ? void 0 : _this$assembly8.status; } getAssemblyFiles(assemblyID) { return this.uppy.getFiles().filter(file => { var _file$transloadit2; return (file == null || (_file$transloadit2 = file.transloadit) == null ? void 0 : _file$transloadit2.assembly) === assemblyID; }); } } function _getClientVersion2() { const list = [ // @ts-expect-error VERSION comes from babel, TS does not understand `uppy-core:${this.uppy.constructor.VERSION}`, // @ts-expect-error VERSION comes from babel, TS does not understand `uppy-transloadit:${this.constructor.VERSION}`, `uppy-tus:${Tus.VERSION}`]; const addPluginVersion = (pluginName, versionName) => { const plugin = this.uppy.getPlugin(pluginName); if (plugin) { // @ts-expect-error VERSION comes from babel, TS does not understand list.push(`${versionName}:${plugin.constructor.VERSION}`); } }; if (this.opts.importFromUploadURLs) { addPluginVersion('XHRUpload', 'uppy-xhr-upload'); addPluginVersion('AwsS3', 'uppy-aws-s3'); addPluginVersion('AwsS3Multipart', 'uppy-aws-s3-multipart'); } addPluginVersion('Dropbox', 'uppy-dropbox'); addPluginVersion('Box', 'uppy-box'); addPluginVersion('Facebook', 'uppy-facebook'); addPluginVersion('GoogleDrive', 'uppy-google-drive'); addPluginVersion('GooglePhotos', 'uppy-google-photos'); addPluginVersion('GoogleDrivePicker', 'uppy-google-drive-picker'); addPluginVersion('GooglePhotosPicker', 'uppy-google-photos-picker'); addPluginVersion('Instagram', 'uppy-instagram'); addPluginVersion('OneDrive', 'uppy-onedrive'); addPluginVersion('Zoom', 'uppy-zoom'); addPluginVersion('Url', 'uppy-url'); if (this.opts.clientName != null) { list.push(this.opts.clientName); } return list.join(','); } function _attachAssemblyMetadata2(file, status) { // Add the metadata parameters Transloadit needs. const meta = { ...file.meta, assembly_url: status.assembly_url, filename: file.name, fieldname: 'file' }; // Add Assembly-specific Tus endpoint. const tus = { ...file.tus, endpoint: status.tus_url, // Include X-Request-ID headers for better debugging. addRequestId: true }; // Set Companion location. We only add this, if 'file' has the attribute // remote, because this is the criteria to identify remote files. // We only replace the hostname for Transloadit's companions, so that // people can also self-host them while still using Transloadit for encoding. let { remote } = file; if (file.remote && TL_COMPANION.test(file.remote.companionUrl)) { const newHost = status.companion_url.replace(/\/$/, ''); const path = file.remote.url.replace(file.remote.companionUrl, '').replace(/^\//, ''); remote = { ...file.remote, companionUrl: newHost, url: `${newHost}/${path}` }; } // Store the Assembly ID this file is in on the file under the `transloadit` key. const newFile = { ...file, transloadit: { assembly: status.assembly_id } }; // Only configure the Tus plugin if we are uploading straight to Transloadit (the default). if (!this.opts.importFromUploadURLs) { Object.assign(newFile, { meta, tus, remote }); } return newFile; } async function _createAssembly2(fileIDs, assemblyOptions) { this.uppy.log('[Transloadit] Create Assembly'); try { const newAssembly = await this.client.createAssembly({ ...assemblyOptions, expectedFiles: fileIDs.length }); const files = this.uppy.getFiles().filter(_ref => { let { id } = _ref; return fileIDs.includes(id); }); if (files.length === 0 && fileIDs.length !== 0) { // All files have been removed, cancelling. await this.client.cancelAssembly(newAssembly); return null; } const assembly = new Assembly(newAssembly, _classPrivateFieldLooseBase(this, _rateLimitedQueue)[_rateLimitedQueue]); const { status } = assembly; const assemblyID = status.assembly_id; const updatedFiles = {}; files.forEach(file => { updatedFiles[file.id] = _classPrivateFieldLooseBase(this, _attachAssemblyMetadata)[_attachAssemblyMetadata](file, status); }); this.uppy.setState({ files: { ...this.uppy.getState().files, ...updatedFiles } }); this.uppy.emit('transloadit:assembly-created', status, fileIDs); this.uppy.log(`[Transloadit] Created Assembly ${assemblyID}`); return assembly; } catch (err) { // TODO: use AssemblyError? const wrapped = new ErrorWithCause(`${this.i18n('creatingAssemblyFailed')}: ${err.message}`, { cause: err }); if ('details' in err) { // @ts-expect-error details is not in the Error type wrapped.details = err.details; } if ('assembly' in err) { // @ts-expect-error assembly is not in the Error type wrapped.assembly = err.assembly; } throw wrapped; } } function _createAssemblyWatcher2(idOrArrayOfIds) { // AssemblyWatcher tracks completion states of all Assemblies in this upload. const ids = Array.isArray(idOrArrayOfIds) ? idOrArrayOfIds : [idOrArrayOfIds]; const watcher = new AssemblyWatcher(this.uppy, ids); watcher.on('assembly-complete', id => { const files = this.getAssemblyFiles(id); files.forEach(file => { this.completedFiles[file.id] = true; this.uppy.emit('postprocess-complete', file); }); }); watcher.on('assembly-error', (id, error) => { // Clear postprocessing state for all our files. const filesFromAssembly = this.getAssemblyFiles(id); filesFromAssembly.forEach(file => { // TODO Maybe make a postprocess-error event here? this.uppy.emit('upload-error', file, error); this.uppy.emit('postprocess-complete', file); }); // Reset `tus` key in the file state, so when the upload is retried, // old tus upload is not re-used — Assebmly expects a new upload, can't currently // re-use the old one. See: https://github.com/transloadit/uppy/issues/4412 // and `onReceiveUploadUrl` in @uppy/tus const files = { ...this.uppy.getState().files }; filesFromAssembly.forEach(file => delete files[file.id].tus); this.uppy.setState({ files }); this.uppy.emit('error', error); }); _classPrivateFieldLooseBase(this, _watcher)[_watcher] = watcher; } function _shouldWaitAfterUpload2() { return this.opts.waitForEncoding || this.opts.waitForMetadata; } function _reserveFiles2(assembly, fileIDs) { return Promise.all(fileIDs.map(fileID => { const file = this.uppy.getFile(fileID); return this.client.reserveFile(assembly.status, file); })); } function _findFile2(uploadedFile) { const files = this.uppy.getFiles(); for (let i = 0; i < files.length; i++) { const file = files[i]; // Completed file upload. if (file.uploadURL === uploadedFile.tus_upload_url) { return file; } // In-progress file upload. if (file.tus && file.tus.uploadUrl === uploadedFile.tus_upload_url) { return file; } if (!uploadedFile.is_tus_file) { // Fingers-crossed check for non-tus uploads, eg imported from S3. if (file.name === uploadedFile.name && file.size === uploadedFile.size) { return file; } } } return undefined; } function _onFileUploadComplete2(assemblyId, uploadedFile) { const state = this.getPluginState(); const file = _classPrivateFieldLooseBase(this, _findFile)[_findFile](uploadedFile); if (!file) { this.uppy.log('[Transloadit] Couldn’t find the file, it was likely removed in the process'); return; } this.setPluginState({ files: { ...state.files, [uploadedFile.id]: { assembly: assemblyId, id: file.id, uploadedFile } } }); this.uppy.emit('transloadit:upload', uploadedFile, this.getAssembly()); } function _onResult2(assemblyId, stepName, result) { const state = this.getPluginState(); const file = state.files[result.original_id]; // The `file` may not exist if an import robot was used instead of a file upload. result.localId = file ? file.id : null; // eslint-disable-line no-param-reassign const entry = { result, stepName, id: result.id, assembly: assemblyId }; this.setPluginState({ results: [...state.results, entry] }); this.uppy.emit('transloadit:result', stepName, result, this.getAssembly()); } function _onAssemblyFinished2(assembly) { const url = assembly.status.assembly_ssl_url; this.client.getAssemblyStatus(url).then(finalStatus => { // eslint-disable-next-line no-param-reassign assembly.status = finalStatus; this.uppy.emit('transloadit:complete', finalStatus); }); } async function _cancelAssembly2(assembly) { await this.client.cancelAssembly(assembly); // TODO bubble this through AssemblyWatcher so its event handlers can clean up correctly this.uppy.emit('transloadit:assembly-cancelled', assembly); this.assembly = undefined; } function _connectAssembly2(assembly, ids) { const { status } = assembly; const id = status.assembly_id; this.assembly = assembly; assembly.on('upload', file => { _classPrivateFieldLooseBase(this, _onFileUploadComplete)[_onFileUploadComplete](id, file); }); assembly.on('error', error => { error.assembly = assembly.status; // eslint-disable-line no-param-reassign this.uppy.emit('transloadit:assembly-error', assembly.status, error); }); assembly.on('executing', () => { this.uppy.emit('transloadit:assembly-executing', assembly.status); }); assembly.on('execution-progress', details => { this.uppy.emit('transloadit:execution-progress', details); if (details.progress_combined != null) { // TODO: Transloadit emits progress information for the entire Assembly combined // (progress_combined) and for each imported/uploaded file (progress_per_original_file). // Uppy's current design requires progress to be set for each file, which is then // averaged to get the total progress (see calculateProcessingProgress.js). // Therefore, we currently set the combined progres for every file, so that this is // the same value that is displayed to the end user, although we have more accurate // per-file progress as well. We cannot use this here or otherwise progress from // imported files would not be counted towards the total progress because imported // files are not registered with Uppy. for (const file of this.uppy.getFilesByIds(ids)) { this.uppy.emit('postprocess-progress', file, { mode: 'determinate', value: details.progress_combined / 100, message: this.i18n('encoding') }); } } }); if (this.opts.waitForEncoding) { assembly.on('result', (stepName, result) => { _classPrivateFieldLooseBase(this, _onResult)[_onResult](id, stepName, result); }); } if (this.opts.waitForEncoding) { assembly.on('finished', () => { _classPrivateFieldLooseBase(this, _onAssemblyFinished)[_onAssemblyFinished](assembly); }); } else if (this.opts.waitForMetadata) { assembly.on('metadata', () => { _classPrivateFieldLooseBase(this, _onAssemblyFinished)[_onAssemblyFinished](assembly); }); } // No need to connect to the socket if the Assembly has completed by now. // @ts-expect-error ok does not exist on Assembly? if (assembly.ok === 'ASSEMBLY_COMPLETE') { return assembly; } assembly.connect(); return assembly; } Transloadit.VERSION = packageJson.version; export { COMPANION_URL, COMPANION_ALLOWED_HOSTS };