refactor(config): niente configurazione cablata — tutto parametrizzato (v0.3.0)

Prima di pubblicare: eliminati dai sorgenti tutti i valori "cablati".

- settings: default neutri (URL, voce, engine vuoti = "decide il server");
  nuove chiavi ttsVoices/ttsEngines (preset dei selettori) e vadMaxMs
  (durata massima registrazione, prima fissa a 60 s nel codice)
- ApiClient: guard su baseUrl vuoto (checkAuth/login si comportano senza
  rete), voce/engine omessi nella richiesta TTS se vuoti, Content-Type
  audio coerente col formato reale del file (ogg/wav/mp3/m4a/opus)
- SettingsPage: selettori voce/engine costruiti dai preset configurabili
  (Repeater dentro ContextMenu = pattern provato su tide) + campi per
  modificare i preset; LoginPage richiede indirizzo server non vuoto
- VoiceRecorder: tetto di registrazione da configurazione
- README: sezione "Configuration" completa; docs neutralizzate (niente
  URL/valori personali: repo public-friendly)
This commit is contained in:
2026-09-12 12:42:06 +02:00
parent 558a384339
commit a5932f0025
11 changed files with 232 additions and 64 deletions
+29 -3
View File
@@ -120,6 +120,13 @@ void ApiClient::clearError()
void ApiClient::checkAuth()
{
// Senza indirizzo configurato non c'e' nulla da verificare: si parte
// dalla pagina di login, che lo richiede.
if (m_settings->baseUrl().isEmpty()) {
setLoggedIn(false);
emit authChecked(false);
return;
}
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/auth/status")));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
@@ -144,6 +151,10 @@ void ApiClient::checkAuth()
void ApiClient::login(const QString &password)
{
if (m_settings->baseUrl().isEmpty()) {
setLastError(QStringLiteral("Server address not configured"));
return;
}
setLastError(QString());
setBusy(true);
QJsonObject body;
@@ -429,7 +440,19 @@ void ApiClient::transcribeFile(const QString &path)
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QStringLiteral("audio/ogg")));
// Content-Type coerente col formato reale (ogg = dettatura manuale,
// wav = modalita' dialogo, ...).
QString mime = QStringLiteral("audio/ogg");
const QString suffix = QFileInfo(path).suffix().toLower();
if (suffix == QLatin1String("wav"))
mime = QStringLiteral("audio/wav");
else if (suffix == QLatin1String("mp3"))
mime = QStringLiteral("audio/mpeg");
else if (suffix == QLatin1String("m4a"))
mime = QStringLiteral("audio/mp4");
else if (suffix == QLatin1String("opus"))
mime = QStringLiteral("audio/opus");
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mime));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant(QStringLiteral("form-data; name=\"file\"; filename=\"%1\"")
.arg(QFileInfo(path).fileName())));
@@ -477,8 +500,11 @@ void ApiClient::speak(const QString &text)
setLastError(QString());
QJsonObject body;
body.insert(QStringLiteral("text"), spoken);
body.insert(QStringLiteral("voice"), m_settings->ttsVoice());
body.insert(QStringLiteral("engine"), m_settings->ttsEngine());
// Voce/engine vuoti = default del server: i parametri si omettono.
if (!m_settings->ttsVoice().isEmpty())
body.insert(QStringLiteral("voice"), m_settings->ttsVoice());
if (!m_settings->ttsEngine().isEmpty())
body.insert(QStringLiteral("engine"), m_settings->ttsEngine());
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/tts")),
QJsonDocument(body).toJson(QJsonDocument::Compact));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {