Commit 315e3bb3 authored by Julius Künzel's avatar Julius Künzel
Browse files

Add python interface

This adds a new python interface class that is now used by text to speech and otioconvertions. One of the main goals is to offer dependency management for otio in a the same why it is already possible for text to speech.

Other achievements are

* reduce duplicated code
* make it easier to add more python based features in the future
* more detailed error messages in some situations

BUG: 423083
parent 4a2e40dd
Pipeline #124451 passed with stage
in 5 minutes and 7 seconds
INSTALL(FILES
checkvosk.py
checkpackages.py
otiointerface.py
speech.py
speechtotext.py
......
# SPDX-FileCopyrightText: 2021 Jean-Baptiste Mardelle <jb@kdenlive.org>
# SPDX-FileCopyrightText: 2022 Julius Künzel <jk.kdedev@smartlab.uber.space>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import sys
import subprocess
import pkg_resources
def print_help():
print("""
Usage: python3 checkpackages.py [mode] [packages]
Where [packages] is a list of python package names separated by blank space
And [mode] one of the following:
--help print this help
--install install missing packages
--upgrade upgrade the packages
--details show details about the packages like eg. version
--check show which of the given packages are not yet installed
""")
if '--help' in sys.argv:
print_help()
sys.exit()
required = set()
for arg in sys.argv[1:]:
if not arg.startswith("--"):
required.add(arg)
if len(required) == 0:
print("Error: You need to provide at least one package name")
print_help()
sys.exit()
installed = {pkg.key for pkg in pkg_resources.working_set}
missing = required - installed
if '--check' in sys.argv:
print("Missing pachages: ", missing)
elif '--install' in sys.argv and missing and len(sys.argv) > 1:
# install missing modules
print("Installing missing packages: ", missing)
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL)
elif '--upgrade' in sys.argv:
# update modules
print("Updating packages: ", required)
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', '--upgrade', *required], stdout=subprocess.DEVNULL)
elif '--details' in sys.argv:
# check modules version
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'show', *required])
else:
print("Error: You need to provide a mode")
print_help()
import sys
import subprocess
import pkg_resources
required = {'vosk', 'srt'}
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 packages: ", missing)
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL)
elif len(sys.argv) > 1 :
if sys.argv[1] == 'upgrade':
# update modules
print ("Updating packages: ", 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])
# SPDX-FileCopyrightText: 2019 Vincent Pinon <vpinon@kde.org>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
from opentimelineio import *
for a in plugins.ActiveManifest().adapters:
if a.has_feature('read') and a.has_feature('write'):
print('*.'+' *.'.join(a.suffixes), end=' ')
......@@ -58,6 +58,7 @@ add_subdirectory(monitor)
add_subdirectory(onlineresources)
add_subdirectory(profiles)
add_subdirectory(project)
add_subdirectory(pythoninterfaces)
add_subdirectory(scopes)
add_subdirectory(simplekeyframes)
add_subdirectory(timeline2)
......
......@@ -352,8 +352,6 @@ signals:
void closeSplash();
/** @brief Trigger an update of the the speech models list */
void voskModelUpdate(const QStringList models);
/** @brief This signal means that VOSK and/or SRT module availability changed*/
void updateVoskAvailability();
/** @brief Update current effect zone */
void updateEffectZone(const QPoint p, bool withUndo);
/** @brief The effect stask is about to be deleted, disconnect everything */
......
......@@ -20,6 +20,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "timeline2/view/timelinecontroller.h"
#include "timeline2/view/timelinewidget.h"
#include "wizard.h"
#include "pythoninterfaces/speechtotext.h"
#ifdef USE_V4L
#include "capture/v4lcapture.h"
......@@ -847,7 +848,7 @@ void KdenliveSettingsDialog::showPage(int page, int option)
break;
case 8:
setCurrentPage(m_page11);
checkVoskDependencies();
m_stt->checkDependencies();
break;
default:
setCurrentPage(m_page1);
......@@ -1448,7 +1449,6 @@ void KdenliveSettingsDialog::slotUpdatev4lDevice()
QStringList pixelformats = info.split('>', Qt::SkipEmptyParts);
#endif
QString itemSize;
QString pixelFormat;
QStringList itemRates;
for (int i = 0; i < pixelformats.count(); ++i) {
QString format = pixelformats.at(i).section(QLatin1Char(':'), 0, 0);
......@@ -1457,7 +1457,7 @@ void KdenliveSettingsDialog::slotUpdatev4lDevice()
#else
QStringList sizes = pixelformats.at(i).split(':', Qt::SkipEmptyParts);
#endif
pixelFormat = sizes.takeFirst();
sizes.takeFirst();
for (int j = 0; j < sizes.count(); ++j) {
itemSize = sizes.at(j).section(QLatin1Char('='), 0, 0);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
......@@ -1835,6 +1835,19 @@ void KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate(int index)
void KdenliveSettingsDialog::initSpeechPage()
{
m_stt = new SpeechToText();
PythonDependencyMessage *msg = new PythonDependencyMessage(this, m_stt);
m_configSpeech.message_layout->addWidget(msg);
connect(m_stt, &SpeechToText::dependenciesAvailable, this, [&](){
if (m_speechListWidget->count() == 0) {
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
}
});
connect(m_stt, &SpeechToText::dependenciesMissing, this, [&](const QStringList &){
m_configSpeech.speech_info->animatedHide();
});
m_voskAction = new QAction(i18n("Install missing dependencies"), this);
m_speechListWidget = new SpeechList(this);
connect(m_speechListWidget, &SpeechList::getDictionary, this, &KdenliveSettingsDialog::getDictionary);
......@@ -1846,48 +1859,14 @@ void KdenliveSettingsDialog::initSpeechPage()
m_configSpeech.check_vosk->setToolTip(i18n("Check VOSK installation"));
connect(m_configSpeech.check_vosk, &QPushButton::clicked, this, [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, [this]() {
#ifdef Q_OS_WIN
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
#else
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
#endif
if (pyExec.isEmpty()) {
doShowSpeechMessage(i18n("Please install python3 and pip on your system."), KMessageWidget::Warning);
return;
}
QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
if (speechScript.isEmpty()) {
doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
return;
}
QProcess checkJob;
if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
m_voskAction->setEnabled(false);
doShowSpeechMessage(i18n("Installing modules..."), KMessageWidget::Information);
qApp->processEvents();
checkJob.start(pyExec, {speechScript, QStringLiteral("install")});
checkJob.waitForFinished();
checkVoskDependencies();
} else {
// upgrade
m_voskUpdated = true;
m_voskAction->setEnabled(false);
doShowSpeechMessage(i18n("Updating modules..."), KMessageWidget::Information);
qApp->processEvents();
checkJob.start(pyExec, {speechScript, QStringLiteral("upgrade")});
checkJob.waitForFinished();
m_configSpeech.speech_info->removeAction(m_voskAction);
checkVoskVersion(pyExec);
m_stt->checkDependencies();
if (m_stt->missingDependencies().isEmpty()) {
m_stt->checkVersions();
}
m_configSpeech.check_vosk->setEnabled(true);
});
checkVoskDependencies();
m_stt->checkDependencies();
connect(m_configSpeech.custom_vosk_folder, &QCheckBox::stateChanged, this, [this](int state) {
m_configSpeech.vosk_folder->setEnabled(state != Qt::Unchecked);
if (state == Qt::Unchecked) {
......@@ -1926,106 +1905,6 @@ void KdenliveSettingsDialog::initSpeechPage()
slotParseVoskDictionaries();
}
void KdenliveSettingsDialog::checkVoskDependencies()
{
#ifdef Q_OS_WIN
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
QString pip3Exec = QStandardPaths::findExecutable(QStringLiteral("pip"));
#else
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
QString pip3Exec = QStandardPaths::findExecutable(QStringLiteral("pip3"));
#endif
if (pyExec.isEmpty()) {
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()) {
doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
return;
} else {
m_configSpeech.speech_info->removeAction(m_voskAction);
checkJob.start(pyExec, {speechScript});
checkJob.waitForFinished();
QString output = checkJob.readAllStandardOutput();
QString missingModules;
if (!output.contains(QLatin1String("vosk"))) {
KdenliveSettings::setVosk_found(true);
} else {
KdenliveSettings::setVosk_found(false);
missingModules = i18n("The VOSK python module is required for speech features. ");
}
if (!output.contains(QLatin1String("srt"))) {
KdenliveSettings::setVosk_srt_found(true);
} else {
KdenliveSettings::setVosk_srt_found(false);
missingModules.append(i18n("The SRT python module is required for automated subtitling."));
}
if (!missingModules.isEmpty()) {
m_voskAction->setEnabled(true);
m_voskAction->setText(i18n("Install missing dependencies"));
m_configSpeech.speech_info->addAction(m_voskAction);
doShowSpeechMessage(missingModules, KMessageWidget::Warning);
} else {
if (m_speechListWidget->count() == 0) {
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
} else if (!pip3Exec.isEmpty()) {
if (!m_voskUpdated) {
// only allow upgrading python modules once
m_voskAction->setText(i18n("Check for update"));
m_configSpeech.speech_info->addAction(m_voskAction);
}
QtConcurrent::run(this, &KdenliveSettingsDialog::checkVoskVersion, pyExec);
}
}
emit pCore->updateVoskAvailability();
}
} else {
if (m_speechListWidget->count() == 0) {
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
} else if (!pip3Exec.isEmpty()) {
if (!m_voskUpdated) {
// only allow upgrading python modules once
m_voskAction->setText(i18n("Check for update"));
m_configSpeech.speech_info->addAction(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 message;
QString output = checkJob.readAllStandardOutput();
QStringList versions = output.split(QStringLiteral("Version: "));
if (versions.count() >= 3) {
bool voskFirst = versions.takeFirst().contains(QLatin1String("vosk"));
QString voskVersion;
QString srtVersion;
QString cut = versions.takeFirst();
if (voskFirst) {
voskVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
cut = versions.takeFirst();
srtVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
} else {
srtVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
cut = versions.takeFirst();
voskVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
}
message = i18n("Speech to text is configured. Vosk %1, Srt %2", voskVersion, srtVersion);
} 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));
......@@ -2145,37 +2024,16 @@ void KdenliveSettingsDialog::processArchive(const QString &archiveFile)
void KdenliveSettingsDialog::slotParseVoskDictionaries()
{
m_speechListWidget->clear();
QString modelDirectory = KdenliveSettings::vosk_folder_path();
QDir dir;
if (modelDirectory.isEmpty()) {
modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
dir = QDir(modelDirectory);
if (!dir.cd(QStringLiteral("speechmodels"))) {
qDebug()<<"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER";
emit pCore->voskModelUpdate({});
return;
}
} else {
dir = QDir(modelDirectory);
}
QStringList dicts = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QStringList final;
for (auto &d : dicts) {
QDir sub(dir.absoluteFilePath(d));
if (sub.exists(QStringLiteral("mfcc.conf")) || (sub.exists(QStringLiteral("conf/mfcc.conf")))) {
final << d;
}
}
QStringList final = m_stt->parseVoskDictionaries();
m_speechListWidget->addItems(final);
if (!KdenliveSettings::vosk_folder_path().isEmpty()) {
m_configSpeech.custom_vosk_folder->setChecked(true);
m_configSpeech.vosk_folder->setUrl(QUrl::fromLocalFile(KdenliveSettings::vosk_folder_path()));
}
if (!final.isEmpty() && KdenliveSettings::vosk_found() && KdenliveSettings::vosk_srt_found()) {
if (!final.isEmpty() && m_stt->missingDependencies().isEmpty()) {
m_configSpeech.speech_info->animatedHide();
} else if (final.isEmpty()) {
doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
}
emit pCore->voskModelUpdate(final);
}
......@@ -24,6 +24,8 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "ui_configcolors_ui.h"
#include "ui_configspeech_ui.h"
#include "pythoninterfaces/speechtotext.h"
class ProfileWidget;
class KJob;
......@@ -100,7 +102,6 @@ private slots:
void removeDictionary();
void downloadModelFinished(KJob* job);
void processArchive(const QString &path);
void checkVoskDependencies();
void doShowSpeechMessage(const QString &message, int messageType);
private:
......@@ -132,6 +133,7 @@ private:
bool m_modified;
bool m_shuttleModified;
bool m_voskUpdated;
SpeechToText *m_stt;
QMap<QString, QString> m_mappable_actions;
QVector<QComboBox *> m_shuttle_buttons;
void initDevices();
......@@ -149,14 +151,13 @@ private:
/** @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();
void doResetConsumer(bool fullReset);
void updateCaptureFolder();
void updateLibraryFolder();
// Screengrab method changed between fullsceen and region, update rec monitor
/** @brief Screengrab method changed between fullsceen and region, update rec monitor */
void updateFullScreenGrab();
/** @brief A settings changed that requires a Kdenlive restart, trigger it */
void restartKdenlive(bool resetConfig = false);
......@@ -169,8 +170,6 @@ 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
/*
SPDX-FileCopyrightText: 2021 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "speechdialog.h"
......@@ -32,6 +32,7 @@ SpeechDialog::SpeechDialog(std::shared_ptr<TimelineItemModel> timeline, QPoint z
{
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
setupUi(this);
m_stt = new SpeechToText();
buttonBox->button(QDialogButtonBox::Apply)->setText(i18n("Process"));
speech_info->hide();
m_voskConfig = new QAction(i18n("Configure"), this);
......@@ -61,7 +62,7 @@ SpeechDialog::SpeechDialog(std::shared_ptr<TimelineItemModel> timeline, QPoint z
connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, [this, zone]() {
slotProcessSpeech(zone);
});
parseVoskDictionaries();
m_stt->parseVoskDictionaries();
frame_progress->setVisible(false);
button_abort->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
connect(button_abort, &QToolButton::clicked, this, [this]() {
......@@ -78,19 +79,8 @@ SpeechDialog::~SpeechDialog()
void SpeechDialog::slotProcessSpeech(QPoint zone)
{
#ifdef Q_OS_WIN
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
#else
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
#endif
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()) {
m_stt->checkDependencies();
if (!m_stt->checkSetup() || !m_stt->missingDependencies().isEmpty()) {
speech_info->setMessageType(KMessageWidget::Warning);
speech_info->setText(i18n("Please configure speech to text."));
speech_info->animatedShow();
......@@ -124,14 +114,6 @@ void SpeechDialog::slotProcessSpeech(QPoint zone)
Mlt::Producer producer(*m_timeline->tractor()->profile(), "xml", sceneList.toUtf8().constData());
qDebug()<<"=== STARTING RENDER B";
Mlt::Consumer xmlConsumer(*m_timeline->tractor()->profile(), "avformat", audio.toUtf8().constData());
QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/speech.py"));
if (speechScript.isEmpty()) {
speech_info->setMessageType(KMessageWidget::Warning);
speech_info->setText(i18n("The speech script was not found, check your install."));
speech_info->animatedShow();
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
return;
}
if (!xmlConsumer.is_valid() || !producer.is_valid()) {
qDebug()<<"=== STARTING CONSUMER ERROR";
if (!producer.is_valid()) {
......@@ -157,21 +139,17 @@ void SpeechDialog::slotProcessSpeech(QPoint zone)
qApp->processEvents();
qDebug()<<"=== STARTING RENDER D";
QString language = language_box->currentText();
qDebug()<<"=== RUNNING SPEECH ANALYSIS: "<<speechScript;
speech_info->setMessageType(KMessageWidget::Information);
speech_info->setText(i18n("Starting speech recognition"));
qApp->processEvents();
QString modelDirectory = KdenliveSettings::vosk_folder_path();
if (modelDirectory.isEmpty()) {
modelDirectory = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("speechmodels"), QStandardPaths::LocateDirectory);
}
QString modelDirectory = m_stt->voskModelPath();
qDebug()<<"==== ANALYSIS SPEECH: "<<modelDirectory<<" - "<<language<<" - "<<audio<<" - "<<speech;
m_speechJob = std::make_unique<QProcess>(this);
connect(m_speechJob.get(), &QProcess::readyReadStandardOutput, this, &SpeechDialog::slotProcessProgress);
connect(m_speechJob.get(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, [this, speech, zone](int, QProcess::ExitStatus status) {
slotProcessSpeechStatus(status, speech, zone);
});
m_speechJob->start(pyExec, {speechScript, modelDirectory, language, audio, speech});
m_speechJob->start(m_stt->pythonExec(), {m_stt->subtitleScript(), modelDirectory, language, audio, speech});
}
void SpeechDialog::slotProcessSpeechStatus(QProcess::ExitStatus status, const QString &srtFile, const QPoint zone)
......@@ -205,29 +183,3 @@ void SpeechDialog::slotProcessProgress()
speech_progress->setValue(static_cast<int>(100 * prog / m_duration));
}
}
void SpeechDialog::parseVoskDictionaries()
{
QString modelDirectory = KdenliveSettings::vosk_folder_path();
QDir dir;
if (modelDirectory.isEmpty()) {
modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
dir = QDir(modelDirectory);
if (!dir.cd(QStringLiteral("speechmodels"))) {
qDebug()<<"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER";
emit pCore->voskModelUpdate({});
return;
}
} else {
dir = QDir(modelDirectory);
}
QStringList dicts = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QStringList final;
for (auto &d : dicts) {
QDir sub(dir.absoluteFilePath(d));
if (sub.exists(QStringLiteral("mfcc.conf")) || (sub.exists(QStringLiteral("conf/mfcc.conf")))) {
final << d;
}
}
emit pCore->voskModelUpdate(final);
}
......@@ -10,6 +10,8 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "ui_speechdialog_ui.h"
#include "timeline2/model/timelineitemmodel.hpp"
#include "definitions.h"
#include "pythoninterfaces/speechtotext.h"
#include <QProcess>
#include <QTemporaryFile>
......@@ -37,7 +39,7 @@ private:
std::unique_ptr<QTemporaryFile> m_tmpSrt;
QMetaObject::Connection m_modelsConnection;
QAction *m_voskConfig;
void parseVoskDictionaries();
SpeechToText *m_stt;
private slots:
void slotProcessSpeech(QPoint zone);
......
......@@ -553,6 +553,7 @@ TextBasedEdit::TextBasedEdit(QWidget *parent)
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
setupUi(this);
setFocusPolicy(Qt::StrongFocus);
m_stt = new SpeechToText();
m_voskConfig = new QAction(i18n("Configure"), this);
connect(m_voskConfig, &QAction::triggered, []() {
pCore->window()->slotPreferences(8);
......@@ -710,7 +711,8 @@ TextBasedEdit::TextBasedEdit(QWidget *parent)
}
search_line->setPalette(palette);
});
parseVoskDictionaries();
m_stt->parseVoskDictionaries();
}
TextBasedEdit::~TextBasedEdit()
......@@ -748,17 +750,8 @@ void TextBasedEdit::startRecognition()
m_errorString.clear();
m_visualEditor->cleanup();
//m_visualEditor->insertHtml(QStringLiteral("<body>"));
#ifdef Q_OS_WIN
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
#else
QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
#endif
if (pyExec.isEmpty()) {
showMessage(i18n("Cannot find python3, please install it on your system."), KMessageWidget::Warning);
return;
}
if (!KdenliveSettings::vosk_found()) {
m_stt->checkDependencies();
if (!m_stt->checkSetup() || !m_stt->missingDependencies({QStringLiteral("vosk")}).isEmpty()) {