Create runtime-configurable upload targets for the KDE Plasma 6 Share menu. The package ships one generic Upload To... action that loads targets from JSON at runtime. Catbox and Uguu are included by default.
GPL-3.0-or-later.
cmake -S . -B build
cmake --build buildcmake -S . -B build -DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failureThe test suite includes C++ unit and integration tests that use a local in-process HTTP server. It does not contact external upload services.
cmake --install buildPlugin installs to the Purpose plugin directory (${KDE_INSTALL_QTPLUGINDIR}/kf6/purpose).
The package also installs bundled targets under /usr/share/plasma-share-uploader/targets/.
Restart Dolphin/Gwenview/other Purpose-Share-enabled app after installing so the Upload To... Share action shows up.
Targets are loaded at runtime from:
- system defaults:
/usr/share/plasma-share-uploader/targets/*.json - user overrides/custom targets:
~/.config/plasma-share-uploader/targets/*.json - user state:
~/.config/plasma-share-uploader/state.json
Each file contains exactly one target object. Targets are merged by id, and user
targets override system targets with the same id.
state.json may define:
disabledBundledTargets: array of bundled target ids to suppress without modifying the files under/usr/share. User targets with the sameidstill win normally.
Example state.json:
{
"disabledBundledTargets": ["catbox", "uguu"]
}After editing the user config, restart the Share-enabled app to reload the target list.
If target configuration errors are found, the picker shows an indicator. Opening it displays diagnostics by file.
Each target file is a single JSON object with these required fields:
id: lowercase identifier for overrides and merges;[a-z0-9][a-z0-9_-]*.displayName: human-friendly name shown in the picker.description: short description shown under the target name.icon: icon name (e.g.image-x-generic).request: upload configuration (see below).response: how to extract the URL from the server response.
Optional fields:
pluginTypes: accepted for compatibility, but not used by the runtime picker.constraints: target filters such as["mimeType:image/*"]. These are evaluated at runtime against the files being shared.extensions: optional list of file suffixes such as["png", ".jpg"]. If present, every shared file must match one of them.preUpload: ordered list of per-file preprocessing rules to run before upload.
request includes:
url: upload endpoint URL. Supports${ENV:VAR}substitution, and${FILENAME}in URL paths.method: HTTP method.multipart,raw,form_urlencoded, andjsoncurrently supportPOSTandPUTas documented below.query: optional query-string parameter map.headers: optional header map.type(optional):multipart(default),raw,form_urlencoded, orjson.
Request string placeholders:
${ENV:VARNAME}: expand from the environment.${FILENAME}: expand to the local file name.
Multipart uploads:
request.type:multipart(or omitted).request.multipart.fileField: form field name for the file.request.multipart.fields: optional extra form fields (string values only). Field values support${ENV:VARNAME}and${FILENAME}.
Raw uploads:
request.type:raw.request.contentType: optional Content-Type to set for the file body.
Form URL encoded uploads:
request.type:form_urlencoded.request.formUrlencoded.fields: required string map sent asapplication/x-www-form-urlencoded. Field values support${ENV:VARNAME}and${FILENAME}.
JSON uploads:
request.type:json.request.json.fields: required JSON value written asapplication/json. String values inside the JSON body support${ENV:VARNAME}and${FILENAME}.
Headers and query parameters:
request.headers: string map. Values support${ENV:VARNAME}and${FILENAME}.request.query: string map. Values support${ENV:VARNAME}and${FILENAME}.
Targets may optionally define preUpload rules to transform a file before it is uploaded.
Rules are evaluated once per file, in order. The first matching rule is used. If no
rule matches, the original file is uploaded unchanged.
Each preUpload entry must include:
mime: non-empty array of MIME patterns. Supported forms are exact MIME types such asimage/png, wildcard subtype patterns such asimage/*, and*/*to match any MIME type.fileHandling: one of:inplace_copy: copy the original file to a temporary path, substitute${FILE}with that temporary path, run one or more commands in place on the copy, then upload the modified copy.output_file: substitute${FILE}with the original file path and${OUT_FILE}with a temporary output path, run exactly one command, then upload${OUT_FILE}.
commands: non-empty array of command objects.inplace_copyrules may contain one or more commands.output_filerules must contain exactly one command.
Each command object must include:
argv: non-empty array of command arguments. Commands are executed directly without a shell.
Available placeholders in argv:
${FILE}: required in every command.${OUT_FILE}: required foroutput_file, and not allowed forinplace_copy.
Behavior:
- First matching
preUploadrule wins. - No match: upload the original file.
- Non-zero exit, timeout, or missing output file: fail that upload and surface stderr.
- Original user files are never modified.
If you want a catch-all fallback rule, use */* and place it last.
response must include a type:
text_url: response body is the URL.regex: usepatternand optionalgroupto extract URL from response text.json_pointer: usepointer(must start with/) to locate a string URL in a JSON response.header: usenameto read a response header.redirect_url: use the redirect target URL, or the final reply URL if no redirect target is reported.xml_xpath: usexpath(must start with/) to locate a text node in an XML response.
Optional error extraction:
response.error: optional extractor object with the sametypechoices asresponse.- On HTTP error responses, the uploader will try
response.errorbefore falling back to the raw server response text.
Optional variant outputs:
response.thumbnail: optional extractor object for a thumbnail URL.response.deletion: optional extractor object for a deletion URL.
ShareJob output now includes:
url/urlsthumbnailUrl/thumbnailUrlswhen configureddeletionUrl/deletionUrlswhen configuredresults: per-upload objects containingurl, optionalthumbnailUrl, optionaldeletionUrl, andresponse
Each response object contains:
statusCodereasonPhraseresponseUrlheaderswith lowercased header namesresponseText
{
"id": "example",
"displayName": "ExampleHost",
"description": "Upload images to ExampleHost",
"icon": "image-x-generic",
"pluginTypes": ["ShareUrl", "Export"],
"constraints": ["mimeType:image/*"],
"request": {
"url": "https://example.com/upload",
"method": "POST",
"multipart": {
"fields": {
"token": "${ENV:EXAMPLE_TOKEN}"
},
"fileField": "file"
}
},
"preUpload": [
{
"mime": ["image/jpeg", "image/tiff"],
"fileHandling": "inplace_copy",
"commands": [
{
"argv": ["exiv2", "rm", "${FILE}"]
},
{
"argv": ["oxipng", "--strip", "all", "${FILE}"]
}
]
}
],
"response": {
"type": "json_pointer",
"pointer": "/data/url"
}
}Save it as its own file, for example
~/.config/plasma-share-uploader/targets/example.json.
To override a bundled target, give your file the same id as the system target. The
user definition wins at runtime.
