Commit 12be3276 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Display speech to text python modules version, improve config feedback

parent ddeedfa6
......@@ -7,6 +7,17 @@ installed = {pkg.key for pkg in pkg_resources.working_set}
missing = required - installed
print ("Missing pachages: ", missing)
if missing and len(sys.argv) > 1 :
# install missing modules
print ("Installing missing pachages: ", missing)
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL)
elif len(sys.argv) > 1 :
if sys.argv[0] == 'upgrade':
# update modules
print ("Updating pachages: ", required)
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', '--upgrade', *required], stdout=subprocess.DEVNULL)
else:
# check modules version
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'show', *required])
......@@ -65,6 +65,7 @@
#include <QSize>
#include <QThread>
#include <QTimer>
#include <QtConcurrent>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
......@@ -1750,33 +1751,30 @@ void KdenliveSettingsDialog::initSpeechPage()
m_configSpeech.speech_info->setWordWrap(true);
m_configSpeech.check_vosk->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
m_configSpeech.check_vosk->setToolTip(i18n("Check VOSK installation"));
connect(m_configSpeech.check_vosk, &QToolButton::clicked, [this]() {
connect(m_configSpeech.check_vosk, &QPushButton::clicked, [this]() {
m_configSpeech.check_vosk->setEnabled(false);
KdenliveSettings::setVosk_found(false);
KdenliveSettings::setVosk_srt_found(false);
checkVoskDependencies();
m_configSpeech.check_vosk->setEnabled(true);
});
connect(this, &KdenliveSettingsDialog::showSpeechMessage, this, &KdenliveSettingsDialog::doShowSpeechMessage);
connect(m_voskAction, &QAction::triggered, [this]() {
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
if (pyExec.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("Cannot find python3, please install it on your system."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Cannot find python3, please install it on your system."), KMessageWidget::Warning);
return;
} else if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
QProcess checkJob;
QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
if (speechScript.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("The speech script was not found, check your install."));
m_configSpeech.speech_info->show();
doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
return;
} else {
m_voskAction->setEnabled(false);
doShowSpeechMessage(i18n("Installing modules..."), KMessageWidget::Information);
m_configSpeech.speech_info->setMessageType(KMessageWidget::Information);
m_configSpeech.speech_info->setText(i18n("Installing modules..."));
m_configSpeech.speech_info->show();
qApp->processEvents();
checkJob.start(pyExec, {speechScript, QStringLiteral("install")});
checkJob.waitForFinished();
}
......@@ -1827,17 +1825,13 @@ void KdenliveSettingsDialog::checkVoskDependencies()
pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
}
if (pyExec.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("Cannot find python3, please install it on your system."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Cannot find python3, please install it on your system."), KMessageWidget::Warning);
return;
} else if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
QProcess checkJob;
QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
if (speechScript.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("The speech script was not found, check your install."));
m_configSpeech.speech_info->show();
doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
return;
} else {
checkJob.start(pyExec, {speechScript});
......@@ -1857,38 +1851,61 @@ void KdenliveSettingsDialog::checkVoskDependencies()
missingModules.append(i18n("The SRT python module is required for automated subtitling."));
}
if (!missingModules.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(missingModules);
m_voskAction->setEnabled(true);
m_configSpeech.speech_info->addAction(m_voskAction);
m_configSpeech.speech_info->show();
doShowSpeechMessage(missingModules, KMessageWidget::Warning);
} else {
if (m_configSpeech.listWidget->count() == 0) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Information);
m_configSpeech.speech_info->setText(i18n("Please add a speech model."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
} else {
m_configSpeech.speech_info->removeAction(m_voskAction);
m_configSpeech.speech_info->setMessageType(KMessageWidget::Positive);
m_configSpeech.speech_info->setText(i18n("Speech to text is properly configured."));
m_configSpeech.speech_info->show();
QtConcurrent::run(this, &KdenliveSettingsDialog::checkVoskVersion, pyExec);
}
}
pCore->updateVoskAvailability();
}
} else {
if (m_configSpeech.listWidget->count() == 0) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Information);
m_configSpeech.speech_info->setText(i18n("Please add a speech model."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
} else {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Positive);
m_configSpeech.speech_info->setText(i18n("Speech to text is properly configured."));
m_configSpeech.speech_info->show();
m_configSpeech.speech_info->removeAction(m_voskAction);
QtConcurrent::run(this, &KdenliveSettingsDialog::checkVoskVersion, pyExec);
}
}
}
void KdenliveSettingsDialog::checkVoskVersion(const QString pyExec)
{
emit showSpeechMessage(i18n("Checking configuration..."), KMessageWidget::Information);
QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
QProcess checkJob;
checkJob.start(pyExec, {speechScript, QStringLiteral("check")});
checkJob.waitForFinished();
QString output = checkJob.readAllStandardOutput();
QStringList versions = output.split(QStringLiteral("Version: "));
versions.takeFirst();
QStringList appVersion;
for (const QString v : versions) {
QString res = v.simplified();
res = res.section(QLatin1Char(' '), 0, 0);
appVersion << res;
}
QString message;
if (appVersion.count() == 2) {
message = i18n("Speech to text is configured. Vosk %1, Srt %2", appVersion.at(0), appVersion.at(1));
} else {
message = i18n("Speech to text is properly configured.");
}
emit showSpeechMessage(message, KMessageWidget::Positive);
}
void KdenliveSettingsDialog::doShowSpeechMessage(const QString &message, int messageType)
{
m_configSpeech.speech_info->setMessageType(static_cast<KMessageWidget::MessageType>(messageType));
m_configSpeech.speech_info->setText(message);
m_configSpeech.speech_info->animatedShow();
}
void KdenliveSettingsDialog::getDictionary()
{
QUrl url = KUrlRequesterDialog::getUrl(QUrl(), this, i18n("Enter url for the new dictionary"));
......@@ -1898,9 +1915,7 @@ void KdenliveSettingsDialog::getDictionary()
QString tmpFile;
if (!url.isLocalFile()) {
KIO::FileCopyJob *copyjob = KIO::file_copy(url, QUrl::fromLocalFile(QDir::temp().absoluteFilePath(url.fileName())));
m_configSpeech.speech_info->setMessageType(KMessageWidget::Information);
m_configSpeech.speech_info->setText(i18n("Downloading model..."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Downloading model..."), KMessageWidget::Information);
connect(copyjob, &KIO::FileCopyJob::result, this, &KdenliveSettingsDialog::downloadModelFinished);
} else {
processArchive(url.toLocalFile());
......@@ -1911,17 +1926,13 @@ void KdenliveSettingsDialog::getDictionary()
void KdenliveSettingsDialog::removeDictionary()
{
if (!KdenliveSettings::vosk_folder_path().isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("We do not allow deleting custom folder models, please do it manually."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("We do not allow deleting custom folder models, please do it manually."), KMessageWidget::Warning);
return;
}
QString modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(modelDirectory);
if (!dir.cd(QStringLiteral("speechmodels"))) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("Cannot access dictionary folder"));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning);
return;
}
......@@ -1979,24 +1990,21 @@ void KdenliveSettingsDialog::processArchive(const QString archiveFile)
dir = QDir(modelDirectory);
dir.mkdir(QStringLiteral("speechmodels"));
if (!dir.cd(QStringLiteral("speechmodels"))) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
m_configSpeech.speech_info->setText(i18n("Cannot access dictionary folder"));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning);
return;
}
} else {
dir = QDir(modelDirectory);
}
if (archive->open(QIODevice::ReadOnly)) {
m_configSpeech.speech_info->setText(i18n("Extracting archive..."));
doShowSpeechMessage(i18n("Extracting archive..."), KMessageWidget::Information);
const KArchiveDirectory *archiveDir = archive->directory();
if (!archiveDir->copyTo(dir.absolutePath())) {
qDebug()<<"=== Error extracting archive!!";
} else {
QFile::remove(archiveFile);
emit parseDictionaries();
m_configSpeech.speech_info->setMessageType(KMessageWidget::Positive);
m_configSpeech.speech_info->setText(i18n("New dictionary installed"));
doShowSpeechMessage(i18n("New dictionary installed."), KMessageWidget::Positive);
}
} else {
// Test if it is a folder
......@@ -2040,9 +2048,7 @@ void KdenliveSettingsDialog::slotParseVoskDictionaries()
if (!final.isEmpty() && KdenliveSettings::vosk_found() && KdenliveSettings::vosk_srt_found()) {
m_configSpeech.speech_info->animatedHide();
} else if (final.isEmpty()) {
m_configSpeech.speech_info->setMessageType(KMessageWidget::Information);
m_configSpeech.speech_info->setText(i18n("Please add a speech model."));
m_configSpeech.speech_info->animatedShow();
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
}
pCore->voskModelUpdate(final);
}
......
......@@ -97,6 +97,7 @@ private slots:
void downloadModelFinished(KJob* job);
void processArchive(const QString path);
void checkVoskDependencies();
void doShowSpeechMessage(const QString &message, int messageType);
private:
KPageWidgetItem *m_page1;
......@@ -139,7 +140,10 @@ private:
static bool getBlackMagicOutputDeviceList(QComboBox *devicelist, bool force = false);
/** @brief Init QtMultimedia audio record settings */
bool initAudioRecDevice();
/** @brief Init Speech to text settings */
void initSpeechPage();
/** @brief Check version of installed python modules for speech to text */
void checkVoskVersion(const QString pyExec);
signals:
void customChanged();
......@@ -159,6 +163,8 @@ signals:
void updateMonitorBg();
/** @brief Trigger parsing of the speech models folder */
void parseDictionaries();
/** @brief Show an info message regarding speech to text status */
void showSpeechMessage(const QString &message, int messageType);
};
#endif
......@@ -45,25 +45,19 @@ SpeechDialog::SpeechDialog(const std::shared_ptr<TimelineItemModel> &timeline, Q
setupUi(this);
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Process"));
speech_info->hide();
vosk_config->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
vosk_config->setToolTip(i18n("Configure speech recognition"));
connect(vosk_config, &QToolButton::clicked, [this]() {
m_voskConfig = new QAction(i18n("Configure"), this);
connect(m_voskConfig, &QAction::triggered, [this]() {
pCore->window()->slotPreferences(8);
});
m_availableConnection = connect(pCore.get(), &Core::updateVoskAvailability, this, &SpeechDialog::updateAvailability);
m_modelsConnection = connect(pCore.get(), &Core::voskModelUpdate, [&](QStringList models) {
language_box->clear();
language_box->addItems(models);
updateAvailability();
if (models.isEmpty()) {
speech_info->addAction(m_voskConfig);
speech_info->setMessageType(KMessageWidget::Information);
speech_info->setText(i18n("Please install speech recognition models"));
speech_info->animatedShow();
vosk_config->setVisible(true);
} else {
if (KdenliveSettings::vosk_found() && KdenliveSettings::vosk_srt_found()) {
vosk_config->setVisible(false);
}
if (!KdenliveSettings::vosk_srt_model().isEmpty() && models.contains(KdenliveSettings::vosk_srt_model())) {
int ix = language_box->findText(KdenliveSettings::vosk_srt_model());
if (ix > -1) {
......@@ -90,26 +84,27 @@ SpeechDialog::SpeechDialog(const std::shared_ptr<TimelineItemModel> &timeline, Q
SpeechDialog::~SpeechDialog()
{
QObject::disconnect(m_availableConnection);
QObject::disconnect(m_modelsConnection);
}
void SpeechDialog::updateAvailability()
{
bool enabled = KdenliveSettings::vosk_found() && KdenliveSettings::vosk_srt_found() && language_box->count() > 0;
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(enabled);
vosk_config->setVisible(!enabled);
}
void SpeechDialog::slotProcessSpeech(QPoint zone)
{
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
if (pyExec.isEmpty()) {
speech_info->removeAction(m_voskConfig);
speech_info->setMessageType(KMessageWidget::Warning);
speech_info->setText(i18n("Cannot find python3, please install it on your system."));
speech_info->animatedShow();
return;
}
if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
speech_info->setMessageType(KMessageWidget::Warning);
speech_info->setText(i18n("Please configure speech to text."));
speech_info->animatedShow();
speech_info->addAction(m_voskConfig);
return;
}
speech_info->removeAction(m_voskConfig);
speech_info->setMessageType(KMessageWidget::Information);
speech_info->setText(i18n("Starting audio export"));
speech_info->show();
......
......@@ -27,6 +27,8 @@
#include <QProcess>
#include <QTemporaryFile>
class QAction;
/**
* @class SpeechDialog
* @brief A dialog for editing markers and guides.
......@@ -47,14 +49,13 @@ private:
int m_duration;
std::unique_ptr<QTemporaryFile> m_tmpAudio;
std::unique_ptr<QTemporaryFile> m_tmpSrt;
QMetaObject::Connection m_availableConnection;
QMetaObject::Connection m_modelsConnection;
QAction *m_voskConfig;
void parseVoskDictionaries();
private slots:
void slotProcessSpeech(QPoint zone);
void slotProcessSpeechStatus(QProcess::ExitStatus status, const QString &srtFile, const QPoint zone);
void updateAvailability();
void slotProcessProgress();
};
......
......@@ -11,7 +11,14 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0" colspan="4">
<item row="6" column="1">
<widget class="QToolButton" name="button_delete">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="5">
<widget class="QListWidget" name="listWidget">
<property name="acceptDrops">
<bool>true</bool>
......@@ -34,30 +41,27 @@
</property>
</spacer>
</item>
<item row="6" column="3">
<widget class="QToolButton" name="check_vosk">
<item row="1" column="0" colspan="5">
<widget class="QCheckBox" name="custom_vosk_folder">
<property name="text">
<string>...</string>
<string>Custom models folder</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QToolButton" name="button_add">
<item row="3" column="0" colspan="5">
<widget class="QLabel" name="models_url">
<property name="text">
<string>...</string>
<string/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QToolButton" name="button_delete">
<item row="6" column="0">
<widget class="QToolButton" name="button_add">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="KMessageWidget" name="speech_info"/>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="text">
......@@ -65,14 +69,7 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="QLabel" name="models_url">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0" colspan="4">
<item row="2" column="0" colspan="5">
<widget class="KUrlRequester" name="vosk_folder">
<property name="enabled">
<bool>false</bool>
......@@ -82,10 +79,13 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QCheckBox" name="custom_vosk_folder">
<item row="7" column="0" colspan="5">
<widget class="KMessageWidget" name="speech_info"/>
</item>
<item row="6" column="3">
<widget class="QPushButton" name="check_vosk">
<property name="text">
<string>Custom models folder</string>
<string>Check configuration</string>
</property>
</widget>
</item>
......
......@@ -109,13 +109,6 @@
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QToolButton" name="vosk_config">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
......
  • data/scripts/checkvosk.py line 17: It should be "updating packages", shouldn't it?

Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment