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:
+29
-3
@@ -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]() {
|
||||
|
||||
+2
-2
@@ -19,9 +19,9 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||
// AppConfigLocation = ~/.config/harbour/hermes (zona persistente della sandbox).
|
||||
app->setOrganizationName(QStringLiteral("harbour"));
|
||||
app->setApplicationName(QStringLiteral("hermes"));
|
||||
app->setApplicationVersion(QStringLiteral("0.2.1"));
|
||||
app->setApplicationVersion(QStringLiteral("0.3.0"));
|
||||
|
||||
qDebug() << "harbour-hermes v0.2.1 build" << __DATE__ << __TIME__;
|
||||
qDebug() << "harbour-hermes v0.3.0 build" << __DATE__ << __TIME__;
|
||||
|
||||
Settings settings;
|
||||
ApiClient api(&settings);
|
||||
|
||||
+57
-5
@@ -1,10 +1,11 @@
|
||||
#include "settings.h"
|
||||
|
||||
// Default: l'istanza pubblica del WebUI usa questo host (HTTPS, cert valido).
|
||||
// L'utente puo' cambiarlo dalle impostazioni (LAN, VPN, altro dominio).
|
||||
static const char *kDefaultBaseUrl = "https://hermes.hackatoniclife.com";
|
||||
static const char *kDefaultTtsVoice = "it-IT-ElsaNeural";
|
||||
static const char *kDefaultTtsEngine = "edge";
|
||||
// Nessuna configurazione personale e' compilata nel binario: i default sono
|
||||
// neutri e TUTTO si imposta dalle Impostazioni dell'app (QSettings in
|
||||
// ~/.config/harbour/hermes.conf). Vuoto = "decide il server".
|
||||
static const char *kDefaultBaseUrl = "";
|
||||
static const char *kDefaultTtsVoice = "";
|
||||
static const char *kDefaultTtsEngine = "";
|
||||
|
||||
Settings::Settings(QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -109,6 +110,57 @@ void Settings::setVadThreshold(int threshold)
|
||||
emit vadThresholdChanged();
|
||||
}
|
||||
|
||||
int Settings::vadMaxMs() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("vadMaxMs"), 60000).toInt();
|
||||
}
|
||||
|
||||
void Settings::setVadMaxMs(int ms)
|
||||
{
|
||||
if (ms == vadMaxMs())
|
||||
return;
|
||||
m_settings.setValue(QStringLiteral("vadMaxMs"), ms);
|
||||
emit vadMaxMsChanged();
|
||||
}
|
||||
|
||||
QStringList Settings::ttsVoices() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("ttsVoices")).toStringList();
|
||||
}
|
||||
|
||||
void Settings::setTtsVoices(const QStringList &list)
|
||||
{
|
||||
QStringList clean;
|
||||
for (const QString &v : list) {
|
||||
const QString t = v.trimmed();
|
||||
if (!t.isEmpty() && !clean.contains(t))
|
||||
clean << t;
|
||||
}
|
||||
if (clean == ttsVoices())
|
||||
return;
|
||||
m_settings.setValue(QStringLiteral("ttsVoices"), clean);
|
||||
emit ttsVoicesChanged();
|
||||
}
|
||||
|
||||
QStringList Settings::ttsEngines() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("ttsEngines")).toStringList();
|
||||
}
|
||||
|
||||
void Settings::setTtsEngines(const QStringList &list)
|
||||
{
|
||||
QStringList clean;
|
||||
for (const QString &v : list) {
|
||||
const QString t = v.trimmed();
|
||||
if (!t.isEmpty() && !clean.contains(t))
|
||||
clean << t;
|
||||
}
|
||||
if (clean == ttsEngines())
|
||||
return;
|
||||
m_settings.setValue(QStringLiteral("ttsEngines"), clean);
|
||||
emit ttsEnginesChanged();
|
||||
}
|
||||
|
||||
QString Settings::defaultWorkspace() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("defaultWorkspace"), QString()).toString();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
// Impostazioni persistenti dell'app.
|
||||
// QSettings con OrganizationName "harbour" / ApplicationName "hermes" (impostati
|
||||
@@ -25,6 +26,12 @@ class Settings : public QObject
|
||||
NOTIFY vadSilenceMsChanged)
|
||||
Q_PROPERTY(int vadThreshold READ vadThreshold WRITE setVadThreshold
|
||||
NOTIFY vadThresholdChanged)
|
||||
Q_PROPERTY(int vadMaxMs READ vadMaxMs WRITE setVadMaxMs
|
||||
NOTIFY vadMaxMsChanged)
|
||||
Q_PROPERTY(QStringList ttsVoices READ ttsVoices WRITE setTtsVoices
|
||||
NOTIFY ttsVoicesChanged)
|
||||
Q_PROPERTY(QStringList ttsEngines READ ttsEngines WRITE setTtsEngines
|
||||
NOTIFY ttsEnginesChanged)
|
||||
Q_PROPERTY(QString defaultWorkspace READ defaultWorkspace WRITE setDefaultWorkspace
|
||||
NOTIFY defaultWorkspaceChanged)
|
||||
|
||||
@@ -52,6 +59,16 @@ public:
|
||||
int vadThreshold() const;
|
||||
void setVadThreshold(int threshold);
|
||||
|
||||
int vadMaxMs() const;
|
||||
void setVadMaxMs(int ms);
|
||||
|
||||
// Preset per i selettori voce/engine (liste configurabili, default vuote).
|
||||
QStringList ttsVoices() const;
|
||||
void setTtsVoices(const QStringList &list);
|
||||
|
||||
QStringList ttsEngines() const;
|
||||
void setTtsEngines(const QStringList &list);
|
||||
|
||||
QString defaultWorkspace() const;
|
||||
void setDefaultWorkspace(const QString &ws);
|
||||
|
||||
@@ -63,6 +80,9 @@ signals:
|
||||
void dictationAutoSendChanged();
|
||||
void vadSilenceMsChanged();
|
||||
void vadThresholdChanged();
|
||||
void vadMaxMsChanged();
|
||||
void ttsVoicesChanged();
|
||||
void ttsEnginesChanged();
|
||||
void defaultWorkspaceChanged();
|
||||
|
||||
private:
|
||||
|
||||
@@ -174,8 +174,8 @@ void VoiceRecorder::onSilenceCheck()
|
||||
return;
|
||||
}
|
||||
|
||||
// Tetto di sicurezza anti-registrazione infinita.
|
||||
if (elapsed >= 60000) {
|
||||
// Tetto di sicurezza anti-registrazione infinita (configurabile: vadMaxMs).
|
||||
if (elapsed >= qint64(m_settings->vadMaxMs())) {
|
||||
qDebug() << "[voice] stop automatico: durata massima raggiunta";
|
||||
finalize();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user