A native Qt6/C++ desktop client for the gallery's POST /upload API, in desktop/ with its own flake (kept separate from the Go server module the same way android/ is). Single-photo parity with the Android app: - multipart upload with preemptive HTTP Basic auth and retry-aware outcome classification (uploadclient) - JPEG/PNG metadata stripped byte-for-byte to drop embedded GPS, EXIF orientation baked in, and a quality/scale valve for the 20M nginx cap; HEIC decodes via the qtimageformats plugin (imageprep) - base URL / username in QSettings, password in the system keychain via qtkeychain; https required and a trailing /upload stripped (settings) - settings and post screens in a QStackedWidget (mainwindow) Native Wayland or xcb is selected at runtime. Bulk upload, EXIF capture-date prefill, and moving image prep off the UI thread are left as follow-ups.
38 lines
1.5 KiB
C++
38 lines
1.5 KiB
C++
#pragma once
|
|
|
|
#include <QByteArray>
|
|
#include <QString>
|
|
|
|
// Turns a picked image into a file the gallery's upload API accepts.
|
|
//
|
|
// Three things must be true of the result: no EXIF (the gallery publishes the
|
|
// uploaded file verbatim as its full-size download, so embedded GPS would be
|
|
// public), a .jpg or .png extension (server.go rejects anything else, HEIC
|
|
// included), and under nginx's 20M body cap.
|
|
//
|
|
// JPEG and PNG take a lossless route — whole metadata segments are dropped
|
|
// without touching the compressed image data — so the published file keeps the
|
|
// original sensor pixels. Anything else, or a file whose EXIF orientation must
|
|
// be baked in, is decoded and re-encoded as JPEG (HEIC via Qt's qtimageformats
|
|
// plugin). The strippers are a direct port of the Android app's.
|
|
class ImagePrep
|
|
{
|
|
public:
|
|
struct Prepared {
|
|
QString filePath;
|
|
QString note; // non-empty when the app departed from a byte-exact copy
|
|
QString error;
|
|
bool ok() const { return error.isEmpty(); }
|
|
};
|
|
|
|
// Writes the upload-ready file into cacheDir as baseName.(jpg|png).
|
|
static Prepared prepare(const QString &sourcePath, const QString &cacheDir,
|
|
const QString &baseName);
|
|
|
|
private:
|
|
static QByteArray stripJpegSegments(const QByteArray &src); // empty on parse failure
|
|
static QByteArray stripPngChunks(const QByteArray &src);
|
|
static bool isJpeg(const QByteArray &b);
|
|
static bool isPng(const QByteArray &b);
|
|
};
|