Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Multimedia
Kdenlive
Commits
3225eaad
Commit
3225eaad
authored
Feb 09, 2021
by
Jean-Baptiste Mardelle
Browse files
Move speech recognition settings to Kdenlive's main settings dialog
parent
585e3a55
Pipeline
#50268
canceled with stage
Changes
13
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
data/scripts/CMakeLists.txt
View file @
3225eaad
INSTALL
(
FILES
checkvosk.py
speech.py
speechtotext.py
...
...
data/scripts/checkvosk.py
0 → 100644
View file @
3225eaad
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
:
print
(
"Installing missing pachages: "
,
missing
)
python
=
sys
.
executable
subprocess
.
check_call
([
python
,
'-m'
,
'pip'
,
'install'
,
*
missing
],
stdout
=
subprocess
.
DEVNULL
)
src/core.h
View file @
3225eaad
...
...
@@ -305,6 +305,8 @@ signals:
void
loadingMessageUpdated
(
const
QString
&
,
int
progress
=
0
,
int
max
=
-
1
);
/** @brief Opening finished, close splash screen */
void
closeSplash
();
/** @brief Trigger an update of the the speech models list */
void
voskModelUpdate
(
const
QStringList
models
);
};
#endif
src/dialogs/kdenlivesettingsdialog.cpp
View file @
3225eaad
...
...
@@ -39,9 +39,24 @@
#include "kdenlive_debug.h"
#include "klocalizedstring.h"
#include <KIO/DesktopExecParser>
#include <kio_version.h>
#if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
#include <KIO/OpenUrlJob>
#else
#include <KRun>
#endif
#include <KUrlRequesterDialog>
#include <KArchive>
#include <KZip>
#include <KTar>
#include <KIO/FileCopyJob>
#include <KLineEdit>
#include <KMessageBox>
#include <KOpenWithDialog>
#include <KIO/JobUiDelegate>
#include <KArchiveDirectory>
#include <KService>
#include <QAction>
#include <QDir>
...
...
@@ -159,6 +174,11 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(QMap<QString, QString> mappable_a
m_configColors
.
setupUi
(
p10
);
m_page10
=
addPage
(
p10
,
i18n
(
"Colors"
));
m_page10
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"color-management"
)));
QWidget
*
p11
=
new
QWidget
;
m_configSpeech
.
setupUi
(
p11
);
m_page11
=
addPage
(
p11
,
i18n
(
"Speech To Text"
));
m_page11
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"text-speak"
)));
QWidget
*
p4
=
new
QWidget
;
m_configCapture
.
setupUi
(
p4
);
...
...
@@ -397,6 +417,7 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(QMap<QString, QString> mappable_a
}
initAudioRecDevice
();
initSpeechPage
();
// Config dialog size
KSharedConfigPtr
config
=
KSharedConfig
::
openConfig
();
...
...
@@ -749,6 +770,9 @@ void KdenliveSettingsDialog::showPage(int page, int option)
case
7
:
setCurrentPage
(
m_page7
);
break
;
case
8
:
setCurrentPage
(
m_page11
);
break
;
default:
setCurrentPage
(
m_page1
);
}
...
...
@@ -1718,3 +1742,227 @@ void KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate(int index)
{
KdenliveSettings
::
setAudiocapturesamplerate
(
m_configCapture
.
audiocapturesamplerate
->
itemData
(
index
).
toInt
());
}
void
KdenliveSettingsDialog
::
initSpeechPage
()
{
m_voskAction
=
new
QAction
(
i18n
(
"Install missing dependencies"
),
this
);
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
]()
{
m_configSpeech
.
check_vosk
->
setEnabled
(
false
);
KdenliveSettings
::
setVosk_found
(
false
);
KdenliveSettings
::
setVosk_srt_found
(
false
);
checkVoskDependencies
();
m_configSpeech
.
check_vosk
->
setEnabled
(
true
);
});
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
();
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
();
return
;
}
else
{
m_voskAction
->
setEnabled
(
false
);
m_configSpeech
.
speech_info
->
setMessageType
(
KMessageWidget
::
Information
);
m_configSpeech
.
speech_info
->
setText
(
i18n
(
"Installing modules..."
));
m_configSpeech
.
speech_info
->
show
();
qDebug
()
<<
"==== STARTING INSTALL VOSK DEPS..."
;
checkJob
.
start
(
pyExec
,
{
speechScript
,
QStringLiteral
(
"install"
)});
checkJob
.
waitForFinished
();
qDebug
()
<<
"==== STARTING INSTALL VOSK DEPS... DONE"
;
}
checkVoskDependencies
();
}
});
checkVoskDependencies
();
connect
(
m_configSpeech
.
custom_vosk_folder
,
&
QCheckBox
::
stateChanged
,
[
this
](
int
state
)
{
m_configSpeech
.
vosk_folder
->
setEnabled
(
state
!=
Qt
::
Unchecked
);
});
m_configSpeech
.
models_url
->
setText
(
i18n
(
"Download speech models from: <a href=
\"
https://alphacephei.com/vosk/models
\"
>https://alphacephei.com/vosk/models</a>"
));
connect
(
m_configSpeech
.
models_url
,
&
QLabel
::
linkActivated
,
[
&
](
const
QString
&
contents
)
{
qDebug
()
<<
"=== LINK CLICKED: "
<<
contents
;
#if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
auto
*
job
=
new
KIO
::
OpenUrlJob
(
QUrl
(
contents
));
job
->
setUiDelegate
(
new
KIO
::
JobUiDelegate
(
KJobUiDelegate
::
AutoHandlingEnabled
,
this
));
// methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections
// exist in both classes
job
->
start
();
#else
new
KRun
(
QUrl
(
contents
),
this
);
#endif
});
m_configSpeech
.
button_add
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"list-add"
)));
m_configSpeech
.
button_delete
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"edit-delete"
)));
m_configSpeech
.
button_add
->
setToolTip
(
i18n
(
"Add a new speech model from an archive file"
));
m_configSpeech
.
button_delete
->
setToolTip
(
i18n
(
"Delete the selected speech model"
));
connect
(
m_configSpeech
.
button_add
,
&
QToolButton
::
clicked
,
this
,
&
KdenliveSettingsDialog
::
getDictionary
);
connect
(
this
,
&
KdenliveSettingsDialog
::
parseDictionaries
,
this
,
&
KdenliveSettingsDialog
::
slotParseVoskDictionaries
);
slotParseVoskDictionaries
();
}
void
KdenliveSettingsDialog
::
checkVoskDependencies
()
{
qDebug
()
<<
"==== CHECKING VOSK DEPS..."
;
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
();
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
();
return
;
}
else
{
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_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
();
}
else
{
m_configSpeech
.
speech_info
->
removeAction
(
m_voskAction
);
m_configSpeech
.
speech_info
->
animatedHide
();
}
}
}
else
{
m_configSpeech
.
speech_info
->
animatedHide
();
}
}
void
KdenliveSettingsDialog
::
getDictionary
()
{
QUrl
url
=
KUrlRequesterDialog
::
getUrl
(
QUrl
(),
this
,
i18n
(
"Enter url for the new dictionary"
));
if
(
url
.
isEmpty
())
{
return
;
}
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
();
connect
(
copyjob
,
&
KIO
::
FileCopyJob
::
result
,
this
,
&
KdenliveSettingsDialog
::
processArchive
);
/*if (copyjob->exec()) {
qDebug()<<"=== GOT REST: "<<copyjob->destUrl();
//
} else {
qDebug()<<"=== CANNOT DOWNLOAD";
}*/
}
else
{
//KMessageBox::error(this, KIO::NetAccess::lastErrorString());
//KArchive ar(tmpFile);
}
}
void
KdenliveSettingsDialog
::
processArchive
(
KJob
*
job
)
{
qDebug
()
<<
"=== DOWNLOAD FINISHED!!"
;
if
(
job
->
error
()
==
0
||
job
->
error
()
==
112
)
{
qDebug
()
<<
"=== NO ERROR ON DWNLD!!"
;
KIO
::
FileCopyJob
*
jb
=
static_cast
<
KIO
::
FileCopyJob
*>
(
job
);
if
(
jb
)
{
qDebug
()
<<
"=== JOB FOUND!!"
;
QMimeDatabase
db
;
QString
archiveFile
=
jb
->
destUrl
().
toLocalFile
();
QMimeType
type
=
db
.
mimeTypeForFile
(
archiveFile
);
std
::
unique_ptr
<
KArchive
>
archive
;
if
(
type
.
inherits
(
QStringLiteral
(
"application/zip"
)))
{
archive
.
reset
(
new
KZip
(
archiveFile
));
}
else
{
archive
.
reset
(
new
KTar
(
archiveFile
));
}
QString
modelDirectory
=
QStandardPaths
::
writableLocation
(
QStandardPaths
::
AppDataLocation
);
QDir
dir
(
modelDirectory
);
dir
.
mkdir
(
QStringLiteral
(
"speechmodels"
));
if
(
!
dir
.
cd
(
QStringLiteral
(
"speechmodels"
)))
{
qDebug
()
<<
"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER"
;
m_configSpeech
.
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
m_configSpeech
.
speech_info
->
setText
(
i18n
(
"Cannot access dictionary folder"
));
return
;
}
if
(
archive
->
open
(
QIODevice
::
ReadOnly
))
{
m_configSpeech
.
speech_info
->
setText
(
i18n
(
"Extracting archive..."
));
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"
));
}
}
else
{
qDebug
()
<<
"=== CANNOT OPEN ARCHIVE!!"
;
}
}
else
{
qDebug
()
<<
"=== JOB NOT FOUND!!"
;
m_configSpeech
.
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
m_configSpeech
.
speech_info
->
setText
(
i18n
(
"Download error"
));
}
}
else
{
qDebug
()
<<
"=== GOT JOB ERROR: "
<<
job
->
error
();
m_configSpeech
.
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
m_configSpeech
.
speech_info
->
setText
(
i18n
(
"Download error %1"
,
job
->
errorString
()));
}
}
void
KdenliveSettingsDialog
::
slotParseVoskDictionaries
()
{
m_configSpeech
.
listWidget
->
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"
;
pCore
->
voskModelUpdate
({});
return
;
}
}
else
{
dir
=
QDir
(
modelDirectory
);
}
QStringList
dicts
=
dir
.
entryList
(
QDir
::
Dirs
|
QDir
::
NoDotAndDotDot
);
m_configSpeech
.
listWidget
->
addItems
(
dicts
);
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
(
!
dicts
.
isEmpty
()
&&
KdenliveSettings
::
vosk_found
()
&&
KdenliveSettings
::
vosk_srt_found
())
{
m_configSpeech
.
speech_info
->
animatedHide
();
}
pCore
->
voskModelUpdate
(
dicts
);
}
src/dialogs/kdenlivesettingsdialog.h
View file @
3225eaad
...
...
@@ -34,8 +34,10 @@
#include "ui_configtimeline_ui.h"
#include "ui_configtranscode_ui.h"
#include "ui_configcolors_ui.h"
#include "ui_configspeech_ui.h"
class
ProfileWidget
;
class
KJob
;
class
KdenliveSettingsDialog
:
public
KConfigDialog
{
...
...
@@ -89,7 +91,11 @@ private slots:
void
loadExternalProxyProfiles
();
void
slotUpdateAudioCaptureChannels
(
int
index
);
void
slotUpdateAudioCaptureSampleRate
(
int
index
);
void
slotParseVoskDictionaries
();
void
getDictionary
();
void
processArchive
(
KJob
*
job
);
void
checkVoskDependencies
();
private:
KPageWidgetItem
*
m_page1
;
KPageWidgetItem
*
m_page2
;
...
...
@@ -100,6 +106,7 @@ private:
KPageWidgetItem
*
m_page7
;
KPageWidgetItem
*
m_page8
;
KPageWidgetItem
*
m_page10
;
KPageWidgetItem
*
m_page11
;
Ui
::
ConfigEnv_UI
m_configEnv
;
Ui
::
ConfigMisc_UI
m_configMisc
;
Ui
::
ConfigColors_UI
m_configColors
;
...
...
@@ -110,8 +117,10 @@ private:
Ui
::
ConfigTranscode_UI
m_configTranscode
;
Ui
::
ConfigProject_UI
m_configProject
;
Ui
::
ConfigProxy_UI
m_configProxy
;
Ui
::
ConfigSpeech_UI
m_configSpeech
;
ProfileWidget
*
m_pw
;
KProcess
m_readProcess
;
QAction
*
m_voskAction
;
bool
m_modified
;
bool
m_shuttleModified
;
QMap
<
QString
,
QString
>
m_mappable_actions
;
...
...
@@ -128,6 +137,8 @@ private:
static
bool
getBlackMagicOutputDeviceList
(
QComboBox
*
devicelist
,
bool
force
=
false
);
/** @brief Init QtMultimedia audio record settings */
bool
initAudioRecDevice
();
void
initSpeechPage
();
signals:
void
customChanged
();
void
doResetConsumer
(
bool
fullReset
);
...
...
@@ -144,6 +155,8 @@ signals:
void
resetView
();
/** @brief Monitor background color changed, update monitors */
void
updateMonitorBg
();
/** @brief Trigger parsing of the speech models folder */
void
parseDictionaries
();
};
#endif
src/dialogs/speechdialog.cpp
View file @
3225eaad
...
...
@@ -29,23 +29,10 @@
#include "mlt++/MltTractor.h"
#include "mlt++/MltConsumer.h"
#include <kio_version.h>
#include <QFontDatabase>
#include <QDir>
#include <QProcess>
#include <KLocalizedString>
#include <KUrlRequesterDialog>
#include <KArchive>
#include <KZip>
#include <KTar>
#include <KIO/FileCopyJob>
#if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
#include <KIO/OpenUrlJob>
#else
#include <KRun>
#endif
#include <KIO/JobUiDelegate>
#include <KArchiveDirectory>
#include <KMessageWidget>
SpeechDialog
::
SpeechDialog
(
const
std
::
shared_ptr
<
TimelineItemModel
>
&
timeline
,
QPoint
zone
,
bool
activeTrackOnly
,
bool
selectionOnly
,
QWidget
*
parent
)
...
...
@@ -56,28 +43,20 @@ SpeechDialog::SpeechDialog(const std::shared_ptr<TimelineItemModel> &timeline, Q
setupUi
(
this
);
buttonBox
->
button
(
QDialogButtonBox
::
Apply
)
->
setText
(
i18n
(
"Process"
));
speech_info
->
hide
();
slotParseDictionaries
();
button_add
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"list-add"
)));
button_delete
->
setIcon
(
QIcon
::
fromTheme
(
QStringLiteral
(
"edit-delete"
)));
connect
(
button_add
,
&
QToolButton
::
clicked
,
this
,
&
SpeechDialog
::
getDictionary
);
connect
(
pCore
.
get
(),
&
Core
::
voskModelUpdate
,
[
&
](
QStringList
models
)
{
language_box
->
clear
();
language_box
->
addItems
(
models
);
buttonBox
->
button
(
QDialogButtonBox
::
Apply
)
->
setEnabled
(
!
models
.
isEmpty
());
if
(
models
.
isEmpty
())
{
speech_info
->
setMessageType
(
KMessageWidget
::
Information
);
speech_info
->
setText
(
i18n
(
"Please install speech recognition models"
));
speech_info
->
animatedShow
();
}
});
connect
(
buttonBox
->
button
(
QDialogButtonBox
::
Apply
),
&
QPushButton
::
clicked
,
[
this
,
timeline
,
zone
]()
{
slotProcessSpeech
(
timeline
,
zone
);
});
connect
(
speech_info
,
&
KMessageWidget
::
linkActivated
,
[
&
](
const
QString
&
contents
)
{
qDebug
()
<<
"=== LINK CLICKED: "
<<
contents
;
#if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
auto
*
job
=
new
KIO
::
OpenUrlJob
(
QUrl
(
contents
));
job
->
setUiDelegate
(
new
KIO
::
JobUiDelegate
(
KJobUiDelegate
::
AutoHandlingEnabled
,
this
));
// methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections
// exist in both classes
job
->
start
();
#else
new
KRun
(
QUrl
(
contents
),
this
);
#endif
});
//TODO: check for the python scripts vosk and srt
connect
(
this
,
&
SpeechDialog
::
parseDictionaries
,
this
,
&
SpeechDialog
::
slotParseDictionaries
);
parseVoskDictionaries
();
}
void
SpeechDialog
::
slotProcessSpeech
(
const
std
::
shared_ptr
<
TimelineItemModel
>
&
timeline
,
QPoint
zone
)
...
...
@@ -163,110 +142,21 @@ void SpeechDialog::slotProcessSpeech(const std::shared_ptr<TimelineItemModel> &t
}
}
void
SpeechDialog
::
getDictionary
()
{
QUrl
url
=
KUrlRequesterDialog
::
getUrl
(
QUrl
(),
this
,
i18n
(
"Enter url for the new dictionary"
));
if
(
url
.
isEmpty
())
{
return
;
}
QString
tmpFile
;
if
(
!
url
.
isLocalFile
())
{
KIO
::
FileCopyJob
*
copyjob
=
KIO
::
file_copy
(
url
,
QUrl
::
fromLocalFile
(
QDir
::
temp
().
absoluteFilePath
(
url
.
fileName
())));
speech_info
->
setMessageType
(
KMessageWidget
::
Information
);
speech_info
->
setText
(
i18n
(
"Downloading model..."
));
speech_info
->
animatedShow
();
connect
(
copyjob
,
&
KIO
::
FileCopyJob
::
result
,
this
,
&
SpeechDialog
::
processArchive
);
/*if (copyjob->exec()) {
qDebug()<<"=== GOT REST: "<<copyjob->destUrl();
//
} else {
qDebug()<<"=== CANNOT DOWNLOAD";
}*/
}
else
{
//KMessageBox::error(this, KIO::NetAccess::lastErrorString());
//KArchive ar(tmpFile);
}
}
void
SpeechDialog
::
processArchive
(
KJob
*
job
)
void
SpeechDialog
::
parseVoskDictionaries
()
{
qDebug
()
<<
"=== DOWNLOAD FINISHED!!"
;
if
(
job
->
error
()
==
0
||
job
->
error
()
==
112
)
{
qDebug
()
<<
"=== NO ERROR ON DWNLD!!"
;
KIO
::
FileCopyJob
*
jb
=
static_cast
<
KIO
::
FileCopyJob
*>
(
job
);
if
(
jb
)
{
qDebug
()
<<
"=== JOB FOUND!!"
;
QMimeDatabase
db
;
QString
archiveFile
=
jb
->
destUrl
().
toLocalFile
();
QMimeType
type
=
db
.
mimeTypeForFile
(
archiveFile
);
std
::
unique_ptr
<
KArchive
>
archive
;
if
(
type
.
inherits
(
QStringLiteral
(
"application/zip"
)))
{
archive
.
reset
(
new
KZip
(
archiveFile
));
}
else
{
archive
.
reset
(
new
KTar
(
archiveFile
));
}
QString
modelDirectory
=
QStandardPaths
::
writableLocation
(
QStandardPaths
::
AppDataLocation
);
QDir
dir
(
modelDirectory
);
dir
.
mkdir
(
QStringLiteral
(
"speechmodels"
));
if
(
!
dir
.
cd
(
QStringLiteral
(
"speechmodels"
)))
{
qDebug
()
<<
"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER"
;
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
speech_info
->
setText
(
i18n
(
"Cannot access dictionary folder"
));
return
;
}
if
(
archive
->
open
(
QIODevice
::
ReadOnly
))
{
speech_info
->
setText
(
i18n
(
"Extracting archive..."
));
const
KArchiveDirectory
*
archiveDir
=
archive
->
directory
();
if
(
!
archiveDir
->
copyTo
(
dir
.
absolutePath
()))
{
qDebug
()
<<
"=== Error extracting archive!!"
;
}
else
{
QFile
::
remove
(
archiveFile
);
emit
parseDictionaries
();
speech_info
->
setMessageType
(
KMessageWidget
::
Positive
);
speech_info
->
setText
(
i18n
(
"New dictionary installed"
));
}
}
else
{
qDebug
()
<<
"=== CANNOT OPEN ARCHIVE!!"
;
}
}
else
{
qDebug
()
<<
"=== JOB NOT FOUND!!"
;
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
speech_info
->
setText
(
i18n
(
"Download error"
));
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"
;
pCore
->
voskModelUpdate
({});
return
;
}
}
else
{
qDebug
()
<<
"=== GOT JOB ERROR: "
<<
job
->
error
();
speech_info
->
setMessageType
(
KMessageWidget
::
Warning
);
speech_info
->
setText
(
i18n
(
"Download error %1"
,
job
->
errorString
()));
}
}
void
SpeechDialog
::
slotParseDictionaries
()
{
listWidget
->
clear
();
language_box
->
clear
();
buttonBox
->
button
(
QDialogButtonBox
::
Apply
)
->
setEnabled
(
false
);
QString
modelDirectory
=
QStandardPaths
::
writableLocation
(
QStandardPaths
::
AppDataLocation
);
QDir
dir
(
modelDirectory
);
if
(
!
dir
.
cd
(
QStringLiteral
(
"speechmodels"
)))
{
qDebug
()
<<
"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER"
;
tabWidget
->
setCurrentIndex
(
1
);
speech_info
->
setMessageType
(
KMessageWidget
::
Information
);
speech_info
->
setText
(
i18n
(
"Download dictionaries from: <a href=
\"
https://alphacephei.com/vosk/models
\"
>https://alphacephei.com/vosk/models</a>"
));
speech_info
->
animatedShow
();
return
;
dir
=
QDir
(
modelDirectory
);
}
QStringList
dicts
=
dir
.
entryList
(
QDir
::
Dirs
|
QDir
::
NoDotAndDotDot
);
listWidget
->
addItems
(
dicts
);
language_box
->
addItems
(
dicts
);
if
(
!
dicts
.
isEmpty
())
{
buttonBox
->
button
(
QDialogButtonBox
::
Apply
)
->
setEnabled
(
true
);
speech_info
->
animatedHide
();
}
else
{
tabWidget
->
setCurrentIndex
(
1
);
speech_info
->
setMessageType
(
KMessageWidget
::
Information
);
speech_info
->
setText
(
i18n
(
"Download dictionaries from: <a href=
\"
https://alphacephei.com/vosk/models
\"
>https://alphacephei.com/vosk/models</a>"
));
speech_info
->
animatedShow
();
}
pCore
->
voskModelUpdate
(
dicts
);
}
src/dialogs/speechdialog.h
View file @
3225eaad
...
...
@@ -24,9 +24,6 @@
#include "timeline2/model/timelineitemmodel.hpp"
#include "definitions.h"
class
KJob
;
/**
* @class SpeechDialog
* @brief A dialog for editing markers and guides.
...
...
@@ -40,14 +37,11 @@ class SpeechDialog : public QDialog, public Ui::SpeechDialog_UI
public:
explicit
SpeechDialog
(
const
std
::
shared_ptr
<
TimelineItemModel
>
&
timeline
,
QPoint
zone
,
bool
activeTrackOnly
=
false
,
bool
selectionOnly
=
false
,
QWidget
*
parent
=
nullptr
);
private:
void
parseVoskDictionaries
();
private
slots
:
void
getDictionary
();
void
processArchive
(
KJob
*
job
);
void
slotParseDictionaries
();
void
slotProcessSpeech
(
const
std
::
shared_ptr
<
TimelineItemModel
>
&
timeline
,
QPoint
zone
);
signals:
void
parseDictionaries
();
};
#endif
src/dialogs/textbasededit.cpp
View file @
3225eaad
...
...
@@ -48,6 +48,16 @@ TextBasedEdit::TextBasedEdit(QWidget *parent)
connect
(
button_start
,
&
QPushButton
::
clicked
,
this
,
&
TextBasedEdit
::
startRecognition
);
listWidget
->
setWordWrap
(
true
);
search_frame
->
setVisible
(
false
);
connect
(
pCore
.
get
(),
&
Core
::
voskModelUpdate
,
[
&
](
QStringList
models
)
{
language_box
->
clear
();
language_box
->
addItems
(
models
);
button_start
->
setEnabled
(
!
models
.
isEmpty
());
if
(
models
.
isEmpty
())
{
info_message
->
setMessageType
(
KMessageWidget
::
Information
);