Files
kniip/.cursor/plans/zipline_upload_df938514.plan.md
T
2026-11-07 12:19:45 +01:00

97 lines
6.8 KiB
Markdown

---
name: Zipline upload
overview: Een Zipline 4-uploadbestemming toevoegen die je configureert door het ShareX-bestand (.sxcu) of het Shell Script-bestand (.sh) uit Zipline te importeren. Kniip uploadt daarna zelf via HttpClient, met een aparte knop Uploaden in de editor en in het screencastvenster. De link komt op het klembord.
todos:
- id: config
content: UploaderConfig + UploaderImporter (sxcu- en curl/sh-parser) in src/Kniip/Upload
status: completed
- id: client
content: ZiplineUploader met HttpClient multipart, foutafhandeling en JSON-padlezer
status: completed
- id: settings
content: AppSettings.Uploader, DPAPI-versleuteling token in SettingsStore, tabblad Uploaden (importeren/testen/verwijderen)
status: completed
- id: clipboard
content: ClipboardWriter.SetText toevoegen
status: completed
- id: editor
content: Uploadknop + Ctrl+U in EditorWindow, zichtbaar alleen met config; ToolbarPanel-volgorde controleren
status: completed
- id: screencast
content: "Uploadknop in ScreencastWindow: export naar temp, upload met voortgang/annuleren, link naar klembord"
status: completed
- id: build
content: Bouwen en testen met een echte .sxcu en .sh uit Zipline 4
status: completed
isProject: false
---
# Upload naar Zipline 4
## Hoe het werkt
```mermaid
flowchart LR
sxcu["ShareX .sxcu"] --> importer[UploaderImporter]
sh["Shell script .sh"] --> importer
importer --> config["UploaderConfig in AppSettings"]
editorBtn["Editor: Uploaden"] --> client[ZiplineUploader]
castBtn["Screencast: Uploaden"] --> client
config --> client
client -->|"POST multipart"| server["Zipline /api/upload"]
server -->|"JSON files[0].url"| client
client --> clip["URL naar klembord + melding"]
```
Zipline 4 gebruikt een vast patroon: `POST https://host/api/upload` met de header `authorization: <token>`, extra headers zoals `x-zipline-format`, een multipart-veld `file`, en als antwoord `{"files":[{"url": ...}]}`. Beide exportbestanden bevatten precies die gegevens. Kniip leest ze daarom alleen in en voert niets uit.
## Nieuwe bestanden in `src/Kniip/Upload/`
- `UploaderConfig.cs` bevat de velden `Name`, `RequestUrl`, `Headers` (een dictionary), `FileFormName` (standaard `"file"`) en `UrlPath` (standaard `"files[0].url"`). Daarnaast staat er een eigenschap `IsConfigured`.
- `UploaderImporter.cs`:
- `FromSxcu(string json)`: leest met `System.Text.Json` de velden `RequestURL`, `Headers`, `FileFormName` en `URL` uit. Van `URL` wordt `{json:files[0].url}` omgezet naar het pad `files[0].url`. Een `.sxcu` met `Body` anders dan `MultipartFormData` wordt geweigerd.
- `FromShellScript(string text)`: zoekt de `curl`-regel. Daaruit haalt hij de `http(s)://`-URL, elke `-H "naam: waarde"` als header (behalve `content-type`), en de veldnaam uit `-F naam=@...`. Het antwoordpad komt uit `jq -r <pad>` als dat er staat, anders de standaardwaarde. Regelvervolgingen (`\` aan het einde van een regel) en aanhalingstekens worden gewoon afgehandeld.
- Kan het bestand niet worden gelezen, dan geeft de importer een duidelijke Nederlandse foutmelding.
- `ZiplineUploader.cs`: één statische `HttpClient` en de methode `Task<string> UploadAsync(UploaderConfig, Stream, string fileName, string contentType, IProgress<double>?, CancellationToken)`. Die bouwt de `MultipartFormDataContent`, voegt de headers toe en geeft bij een HTTP-fout de statuscode en de `error`-tekst van Zipline terug. Daarna leest hij de URL via een eenvoudige padlezer (`a.b[0].c`) uit het JSON-antwoord.
## Instellingen
- In [`src/Kniip/Settings/AppSettings.cs`](src/Kniip/Settings/AppSettings.cs) komt `public UploaderConfig? Uploader { get; set; }` bij.
- De `authorization`-header is een geheime token. Die wordt in de settings-JSON met DPAPI versleuteld voor de huidige gebruiker, via `ProtectedData.Protect` en het NuGet-pakket `System.Security.Cryptography.ProtectedData`. Dat gebeurt in [`src/Kniip/Settings/SettingsStore.cs`](src/Kniip/Settings/SettingsStore.cs) bij het opslaan en laden.
- In [`src/Kniip/Settings/SettingsWindow.xaml`](src/Kniip/Settings/SettingsWindow.xaml) komt een nieuw tabblad **Uploaden** met:
- Een knop "Importeren..." met een `OpenFileDialog` en het filter `ShareX-uploader (*.sxcu)|*.sxcu|Shell script (*.sh)|*.sh`.
- Een regel die de huidige bestemming toont: de servernaam uit de URL, zonder token.
- De knoppen "Testen" (uploadt een klein PNG'tje en toont de link) en "Verwijderen".
- Een hinttekst: "Maak het bestand in Zipline via Instellingen > Generate Uploaders (ShareX of Shell Script)."
- De afhandeling komt in [`src/Kniip/Settings/SettingsWindow.xaml.cs`](src/Kniip/Settings/SettingsWindow.xaml.cs). De nieuwe configuratie wordt pas bij Opslaan vastgelegd, net als bij de andere instellingen.
## Editor
- In [`src/Kniip/Editor/EditorWindow.xaml`](src/Kniip/Editor/EditorWindow.xaml) komt een nieuwe toolbarknop `ItemId="Upload"` (icoon `&#xE898;`, titel "Uploaden"), tussen Opslaan en Kopiëren.
- In [`src/Kniip/Editor/EditorWindow.xaml.cs`](src/Kniip/Editor/EditorWindow.xaml.cs) komt `Upload_Click` plus de sneltoets Ctrl+U:
- `CommitTextEdit()`, daarna `DocumentRenderer.Export(_doc)` en vervolgens `ImageExporter.EncodePng`.
- `UploadAsync` met de bestandsnaam `Knipsel yyyy-MM-dd HHmmss.png`.
- Het resultaat gaat naar `ClipboardWriter.SetText(url)` en `ShowTemporaryHint("Geüpload, link gekopieerd: ...")`.
- Tijdens het uploaden is de knop uitgeschakeld.
- De knop is ingeklapt zolang `Uploader` niet is ingesteld. Dat wordt opnieuw gecontroleerd in `OnActivated`, net zoals `ApplySaveButton` dat doet.
- Ik controleer in `ToolbarPanel` dat een nieuw `ItemId` ook verschijnt als er al een `ToolbarOrder` is opgeslagen. Zo niet, dan komen onbekende items achteraan.
## Screencast
- In [`src/Kniip/Recording/ScreencastWindow.xaml`](src/Kniip/Recording/ScreencastWindow.xaml) komt een knop `UploadButton` naast Kopiëren.
- In [`src/Kniip/Recording/ScreencastWindow.xaml.cs`](src/Kniip/Recording/ScreencastWindow.xaml.cs) komt `Upload_Click`. Dat volgt hetzelfde patroon als `Copy_Click`:
- Eerst `ExportAsync` naar een tijdelijke map.
- Daarna `UploadAsync` met `video/mp4` of `image/gif`. De voortgang loopt via dezelfde `Progress`-balk en annuleren gaat via `_export`.
- Daarna gaat de URL naar het klembord en toont `SavedText` de link.
- `SetBusy` schakelt ook `UploadButton` uit.
## Hulpfunctie
- In [`src/Kniip/Editor/ImageExporter.cs`](src/Kniip/Editor/ImageExporter.cs) krijgt `ClipboardWriter` de methode `SetText(string)`, met dezelfde aanpak voor een bezet klembord als `SetFile`.
## Buiten de scope
- Automatisch uploaden bij een nieuw knipsel.
- Een "opslaan en uploaden"-keuze voor de knop Opslaan.
- Het echt uitvoeren van het shellscript.
- Andere uploaders dan Zipline. De importer werkt in principe ook voor andere ShareX-configuraties met multipart, maar dat wordt niet getest.