tool_upload
Ingests a staged browser upload into a media component: moves the temporary file into the original tier, generates the type-specific derivatives, and writes the fresh files_info back to the record — then exposes a poll wire for the asynchronous AV transcode.
What it does / why & when to use it
Uploading a media file to Dédalo happens in two phases. First the browser streams the file to a server temporary directory through the service_upload service (chunked transfer, validation, progress bar). That leaves a validated file in a scratch location but nothing in the record. tool_upload is the second phase: process_uploaded_file takes the staged file, moves it into the component's original quality slot, builds every derived quality the model needs (image/pdf/svg/3d synchronously; AV transcode handed to the media job manager), and persists the updated files_info plus the original-file bookkeeping so the client renders the new media rather than the stale placeholder.
Concrete heritage scenario: a cataloguer drops a 180 MB TIFF onto an object's image component. service_upload streams it up and fires its completion event; tool_upload moves the TIFF into original, runs ImageMagick to produce the default, 404 and thumb web qualities (never upscaling, CMYK→sRGB), and stores the files_info so the record's image preview updates in place. For an AV component the video derivatives are transcoded in the background; the client polls get_job_status until the job finishes, then re-scans through tool_media_versions' sync_files.
Use it as: the ingest step behind the media component's upload button and behind other tools that upload a generated file (e.g. tool_image_rotation's background-removal PNG). It is not something an operator invokes directly by name — it is the server action the upload UI calls once a file has been staged.
How it works (server + client)
Server (tools/tool_upload/server/index.ts). Two API actions:
-
process_uploaded_file— declaratively gatedpermission: 'record', minLevel: 2(write). It resolves the media tool context (resolveMediaToolContext,src/core/media/tool_support.ts), reads the staged file descriptor fromoptions.file_data(key_dir,tmp_name,extension), and calls the ingest orchestratorprocessUploadedFile(src/core/media/ingest/process_uploaded_file.ts). The orchestrator runsaddFileto move the staged file into theoriginaltier (re-validating the extension), then dispatches per model:regenerateImage/regeneratePdf/regenerateSvg/regenerate3dinline (awaited — the file copies and encodes are asynchronous, so a large derivative never blocks the event loop), orsubmitAvTranscodeforcomponent_av(which returns a job id). It re-scansfiles_infoand returns it. The handler then callspersistUploadedMedia(src/core/media/tools/files_info_persist.ts) to write the freshfiles_infoand theoriginal_*name keys back to the record — without this the disk would hold the new file but the stored media data would still point at the old one. -
get_job_status— mounted asMEDIA_JOB_STATUS_ACTION(src/core/tools/job_status.ts),permission: null. It serves the client-shapedJobStatusFramefor one media transcodejob_id(thejob_idreturned byprocess_uploaded_filefor AV).permission: nullis deliberate: dispatch gates 1–4 already require an authenticated user authorized for the tool, and the job id is an unguessable capability minted by an action that passed therecordwrite gate. It is mounted ontool_uploadbecause that is the tool that mints the media job ids.
Client (tools/tool_upload/js/). tool_upload.js is the instance; on init it subscribes to upload_file_done_<id> (published by service_upload when a file is fully staged), and on build it instantiates the service_upload child that renders the drag-and-drop file picker (restricting file types from caller.context.features.allowed_extensions). When the upload completes, upload_done (render_tool_upload.js) shows a spinner and calls process_uploaded_file_controller, which assembles the options and dispatches the exported free function process_uploaded_file — a dd_tools_api / tool_request call with retries: 1 and a 3600 * 1000 ms timeout (large files / slow transcodes). On success, when the caller is a component, it builds a fresh instance of that component and renders a live preview so the operator sees the result without reloading; server error messages are shown with textContent (not innerHTML) to avoid HTML injection from file paths. process_uploaded_file is exported so other tools can trigger headless ingest without a full tool_upload instance (it guards that its caller.model === 'tool_upload').
Actions & options
| Action | Gate | Background | Options it reads | Returns |
|---|---|---|---|---|
process_uploaded_file |
record, level 2 |
— (AV work runs in the media job manager, not a background fork) | tipo, section_tipo, section_id; file_data ({ key_dir, tmp_name, extension, name, size, type, error }); key_dir; process_options; caller_type; quality; target_dir |
{ result, msg, errors, original_file_name, extension, files_info, job_id } — job_id is the AV transcode id (else null) |
get_job_status |
null (dispatch gates 1–4 only) |
— | job_id |
Top-level JobStatusFrame fields (pid, pfile, is_running, data, errors, total_time) plus the tool envelope; { result:false, errors:['job_not_found'] } on an unknown id |
file_data is the descriptor service_upload writes for the staged file; caller_type ('tool' or 'component', from the client's caller.context.type) lets the client branch its post-processing (component callers render a preview; tool callers do not). quality and target_dir are optional placement overrides used by callers that redirect where the file lands (e.g. 'modified' for the background-removal output).
How it is registered & surfaced
tools/tool_upload/register.json is a column-keyed dump (a seeded matrix-row snapshot, not a hand-authored file); importTools() passes it through as-is (see register.json reference). Essentials it carries:
dd1326name =tool_upload;dd799label = "Upload" (per project language);dd612description = "Upload selected file to the server".dd1327version =2.0.3;dd1328minimum Dédalo version =6.0.0;dd1644developer = "Dédalo team".dd1330affected_models → the media component models. The tool surfaces oncomponent_image,component_av,component_pdf,component_3d,component_svgandcomponent_json(each liststool_uploadin itssamples/context.jsontools array).dd1335properties ={ "open_as": "window", "windowFeatures": { "top": 0, "left": "return screen.width -760", "width": 760, "height": 708 } }→ the tool opens in its own window.dd1372labels are empty ({}); the UI strings come from the sharedservice_uploadservice and the framework labels.dd1331show_in_inspector anddd1332show_in_component carry the surfacing flags for the matched component.
Surfacing is element-driven in getElementTools (src/core/tools/registry.ts): because affected_models names the media models, the upload button attaches to those components. In practice it is the ingest engine behind the component's own upload flow, and it is also invoked programmatically by other tools through the exported process_uploaded_file free function.
Examples
Headless ingest from another tool (the shape tool_image_rotation sends after background removal), via the exported process_uploaded_file:
const api_response = await process_uploaded_file({
file_data : file_data, // descriptor returned by service_upload
process_options : null,
caller : { type: 'tool', model: 'tool_upload' },
tipo : self_caller.tipo, // e.g. 'rsc29' (image component)
section_tipo : self_caller.section_tipo, // e.g. 'rsc170'
section_id : self_caller.section_id, // e.g. 1
caller_type : self_caller.context.type, // 'tool' or 'component'
quality : 'modified',
target_dir : null
})
// api_response → { result:true, msg:'ok', errors:[], original_file_name, extension, files_info, job_id }
Poll an AV transcode job started by process_uploaded_file:
const rqo = {
dd_api : 'dd_tools_api',
action : 'tool_request',
source : create_source(self, 'get_job_status'), // → tool_upload::get_job_status
options : { job_id: 'av_transcode_1234_5' }
}
const response = await data_manager.request({ body: rqo })
// response → { result:true, msg:'ok', is_running:false, data:{...}, errors:[], total_time: 12.4, ... }
The file_data descriptor service_upload stages (shape read by process_uploaded_file):
{
"error": 0,
"extension": "tiff",
"name": "proclamacio.tiff",
"size": 184922784,
"tmp_name": "/hd/media/upload/service_upload/tmp/image/upl_a1b2c3",
"type": "image/tiff"
}
Related
- tool_media_versions — manages the qualities
process_uploaded_filebuilds (build, delete, conform headers,sync_filesto re-scan after an AV transcode finishes). - tool_image_rotation — rotates/crops the derived tiers, and calls this tool's
process_uploaded_fileto re-derive after background removal. - tool_import_files — bulk media ingest (many files + their wrapper records) rather than a single component upload.
- Media pipeline — the end-to-end lifecycle (upload → master → derivatives → protection → diffusion) this action sits at the head of; the
service_uploadservice handles phase one. - Creating new tools · Server contract · Security — the tool model,
apiActions, theMEDIA_JOB_STATUS_ACTIONwire, and the permission rules this tool follows. - Source:
tools/tool_upload/server/index.ts,tools/tool_upload/register.json,tools/tool_upload/js/{tool_upload,render_tool_upload}.js,tools/tool_upload/css/tool_upload.less. Ingest core:src/core/media/ingest/process_uploaded_file.ts; job wire:src/core/tools/job_status.ts.