mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-02-07 15:08:24 +00:00
H264 Downloadable codec
This commit is contained in:
parent
85ca6d79ce
commit
425751413d
29 changed files with 1262 additions and 58 deletions
|
|
@ -433,8 +433,11 @@ void App::initCore() {
|
|||
QMetaObject::invokeMethod(
|
||||
mLinphoneThread->getThreadId(),
|
||||
[this]() mutable {
|
||||
lInfo() << log().arg("Updating downloaded codec files");
|
||||
Utils::updateCodecs(); // removing codec updates suffic (.in) before the core is created.
|
||||
lInfo() << log().arg("Starting Core");
|
||||
CoreModel::getInstance()->start();
|
||||
Utils::loadDownloadedCodecs();
|
||||
auto coreStarted = CoreModel::getInstance()->getCore()->getGlobalState() == linphone::GlobalState::On;
|
||||
lDebug() << log().arg("Creating SettingsModel");
|
||||
SettingsModel::create();
|
||||
|
|
@ -541,6 +544,8 @@ void App::initCore() {
|
|||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
Utils::checkDownloadedCodecsUpdates();
|
||||
|
||||
mEngine->load(url);
|
||||
});
|
||||
},
|
||||
|
|
@ -631,6 +636,7 @@ void App::initCppInterfaces() {
|
|||
qmlRegisterType<PayloadTypeGui>(Constants::MainQmlUri, 1, 0, "PayloadTypeGui");
|
||||
qmlRegisterType<PayloadTypeProxy>(Constants::MainQmlUri, 1, 0, "PayloadTypeProxy");
|
||||
qmlRegisterType<PayloadTypeCore>(Constants::MainQmlUri, 1, 0, "PayloadTypeCore");
|
||||
qmlRegisterType<PayloadTypeCore>(Constants::MainQmlUri, 1, 0, "DownloadablePayloadTypeCore");
|
||||
|
||||
LinphoneEnums::registerMetaTypes();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
core/address-books/carddav/CarddavList.cpp
|
||||
|
||||
core/payload-type/PayloadTypeCore.cpp
|
||||
core/payload-type/DownloadablePayloadTypeCore.cpp
|
||||
core/payload-type/PayloadTypeGui.cpp
|
||||
core/payload-type/PayloadTypeProxy.cpp
|
||||
core/payload-type/PayloadTypeList.cpp
|
||||
|
|
|
|||
142
Linphone/core/payload-type/DownloadablePayloadTypeCore.cpp
Normal file
142
Linphone/core/payload-type/DownloadablePayloadTypeCore.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DownloadablePayloadTypeCore.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "tool/file/FileDownloader.hpp"
|
||||
#include "tool/file/FileExtractor.hpp"
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(DownloadablePayloadTypeCore)
|
||||
|
||||
QSharedPointer<DownloadablePayloadTypeCore> DownloadablePayloadTypeCore::create(PayloadTypeCore::Family family,
|
||||
const QString &mimeType,
|
||||
const QString &encoderDescription,
|
||||
const QString &downloadUrl,
|
||||
const QString &installName,
|
||||
const QString &checkSum) {
|
||||
auto sharedPointer = QSharedPointer<DownloadablePayloadTypeCore>(
|
||||
new DownloadablePayloadTypeCore(family, mimeType, encoderDescription, downloadUrl, installName, checkSum),
|
||||
&QObject::deleteLater);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
DownloadablePayloadTypeCore::DownloadablePayloadTypeCore(PayloadTypeCore::Family family,
|
||||
const QString &mimeType,
|
||||
const QString &encoderDescription,
|
||||
const QString &downloadUrl,
|
||||
const QString &installName,
|
||||
const QString &checkSum)
|
||||
: PayloadTypeCore() {
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
|
||||
mFamily = family;
|
||||
mMimeType = mimeType;
|
||||
mEnabled = false;
|
||||
mDownloadable = true;
|
||||
|
||||
mEncoderDescription = encoderDescription;
|
||||
mDownloadUrl = downloadUrl;
|
||||
mInstallName = installName;
|
||||
mCheckSum = checkSum;
|
||||
}
|
||||
|
||||
DownloadablePayloadTypeCore::~DownloadablePayloadTypeCore() {
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
}
|
||||
|
||||
void DownloadablePayloadTypeCore::downloadAndExtract(bool isUpdate) {
|
||||
lInfo() << log().arg("Downloading `%1` codec...").arg(mMimeType);
|
||||
auto codecsFolder = Paths::getCodecsDirPath();
|
||||
QString versionFilePath = codecsFolder + mMimeType + ".txt";
|
||||
QFile versionFile(versionFilePath);
|
||||
|
||||
FileDownloader *fileDownloader = new FileDownloader(this);
|
||||
fileDownloader->setUrl(QUrl(mDownloadUrl));
|
||||
fileDownloader->setDownloadFolder(codecsFolder);
|
||||
|
||||
FileExtractor *fileExtractor = new FileExtractor(fileDownloader);
|
||||
fileExtractor->setExtractFolder(codecsFolder);
|
||||
fileExtractor->setExtractName(mInstallName + (isUpdate ? ".in" : ""));
|
||||
|
||||
QObject::connect(fileDownloader, &FileDownloader::downloadFinished,
|
||||
[this, fileDownloader, fileExtractor, checksum = mCheckSum](const QString &filePath) {
|
||||
fileExtractor->setFile(filePath);
|
||||
QString fileChecksum = Utils::getFileChecksum(filePath);
|
||||
if (checksum.isEmpty() || fileChecksum == checksum) fileExtractor->extract();
|
||||
else {
|
||||
lWarning() << log().arg("File cannot be downloaded : Bad checksum : ") << fileChecksum;
|
||||
fileDownloader->remove();
|
||||
fileDownloader->deleteLater();
|
||||
emit downloadError();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(fileDownloader, &FileDownloader::downloadFailed, [this, fileDownloader]() {
|
||||
fileDownloader->deleteLater();
|
||||
emit downloadError();
|
||||
});
|
||||
|
||||
QObject::connect(fileExtractor, &FileExtractor::extractFinished,
|
||||
[this, fileDownloader, fileExtractor, versionFilePath, downloadUrl = mDownloadUrl]() {
|
||||
QFile versionFile(versionFilePath);
|
||||
if (!versionFile.open(QIODevice::WriteOnly)) {
|
||||
lWarning() << log().arg("Unable to write codec version in: `%1`.").arg(versionFilePath);
|
||||
emit extractError();
|
||||
} else if (versionFile.write(Utils::appStringToCoreString(downloadUrl).c_str(),
|
||||
downloadUrl.length()) == -1) {
|
||||
fileExtractor->remove();
|
||||
versionFile.close();
|
||||
versionFile.remove();
|
||||
emit extractError();
|
||||
} else emit success();
|
||||
fileDownloader->remove();
|
||||
fileDownloader->deleteLater();
|
||||
});
|
||||
|
||||
QObject::connect(fileExtractor, &FileExtractor::extractFailed, [this, fileDownloader]() {
|
||||
fileDownloader->remove();
|
||||
fileDownloader->deleteLater();
|
||||
emit extractError();
|
||||
});
|
||||
|
||||
fileDownloader->download();
|
||||
}
|
||||
|
||||
bool DownloadablePayloadTypeCore::shouldDownloadUpdate() {
|
||||
auto codecsFolder = Paths::getCodecsDirPath();
|
||||
QString versionFilePath = codecsFolder + mMimeType + ".txt";
|
||||
QFile versionFile(versionFilePath);
|
||||
|
||||
if (!versionFile.exists() && !QFileInfo::exists(codecsFolder + mInstallName)) {
|
||||
lWarning() << log().arg("Codec `%1` is not installed.").arg(versionFilePath);
|
||||
return false;
|
||||
}
|
||||
if (!versionFile.open(QIODevice::ReadOnly)) {
|
||||
lWarning() << log().arg("Codec `%1` : unable to read codec version, attempting download.").arg(versionFilePath);
|
||||
return true;
|
||||
} else if (!QString::compare(QTextStream(&versionFile).readAll(), mDownloadUrl, Qt::CaseInsensitive)) {
|
||||
lInfo() << log().arg("Codec `%1` is installed and up to date.").arg(versionFilePath);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
71
Linphone/core/payload-type/DownloadablePayloadTypeCore.hpp
Normal file
71
Linphone/core/payload-type/DownloadablePayloadTypeCore.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOADABLE_PAYLOAD_TYPE_CORE_H_
|
||||
#define DOWNLOADABLE_PAYLOAD_TYPE_CORE_H_
|
||||
|
||||
#include "PayloadTypeCore.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
class DownloadablePayloadTypeCore : public PayloadTypeCore {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void downloadAndExtract(bool isUpdate = false);
|
||||
bool shouldDownloadUpdate();
|
||||
|
||||
static QSharedPointer<DownloadablePayloadTypeCore> create(PayloadTypeCore::Family family,
|
||||
const QString &mime,
|
||||
const QString &encoderDescription,
|
||||
const QString &downloadUrl,
|
||||
const QString &installName,
|
||||
const QString &checkSum);
|
||||
|
||||
DownloadablePayloadTypeCore(PayloadTypeCore::Family family,
|
||||
const QString &mimeType,
|
||||
const QString &encoderDescription,
|
||||
const QString &downloadUrl,
|
||||
const QString &installName,
|
||||
const QString &checkSum);
|
||||
|
||||
~DownloadablePayloadTypeCore();
|
||||
void setSelf(QSharedPointer<DownloadablePayloadTypeCore> me);
|
||||
|
||||
signals:
|
||||
void success();
|
||||
void downloadError();
|
||||
void extractError();
|
||||
void installedChanged();
|
||||
void versionChanged();
|
||||
|
||||
private:
|
||||
QString mDownloadUrl;
|
||||
QString mInstallName;
|
||||
QString mCheckSum;
|
||||
bool mInstalled;
|
||||
QString mVersion;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
Q_DECLARE_METATYPE(DownloadablePayloadTypeCore *)
|
||||
#endif
|
||||
|
|
@ -23,16 +23,16 @@
|
|||
|
||||
DEFINE_ABSTRACT_OBJECT(PayloadTypeCore)
|
||||
|
||||
QSharedPointer<PayloadTypeCore> PayloadTypeCore::create(const std::shared_ptr<linphone::PayloadType> &payloadType,
|
||||
Family family) {
|
||||
QSharedPointer<PayloadTypeCore> PayloadTypeCore::create(Family family,
|
||||
const std::shared_ptr<linphone::PayloadType> &payloadType) {
|
||||
auto sharedPointer =
|
||||
QSharedPointer<PayloadTypeCore>(new PayloadTypeCore(payloadType, family), &QObject::deleteLater);
|
||||
QSharedPointer<PayloadTypeCore>(new PayloadTypeCore(family, payloadType), &QObject::deleteLater);
|
||||
sharedPointer->setSelf(sharedPointer);
|
||||
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
PayloadTypeCore::PayloadTypeCore(const std::shared_ptr<linphone::PayloadType> &payloadType, Family family)
|
||||
PayloadTypeCore::PayloadTypeCore(Family family, const std::shared_ptr<linphone::PayloadType> &payloadType)
|
||||
: QObject(nullptr) {
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
|
|
@ -42,6 +42,7 @@ PayloadTypeCore::PayloadTypeCore(const std::shared_ptr<linphone::PayloadType> &p
|
|||
INIT_CORE_MEMBER(ClockRate, mPayloadTypeModel)
|
||||
INIT_CORE_MEMBER(MimeType, mPayloadTypeModel)
|
||||
INIT_CORE_MEMBER(RecvFmtp, mPayloadTypeModel)
|
||||
INIT_CORE_MEMBER(EncoderDescription, mPayloadTypeModel)
|
||||
}
|
||||
|
||||
PayloadTypeCore::~PayloadTypeCore() {
|
||||
|
|
@ -62,3 +63,7 @@ PayloadTypeCore::Family PayloadTypeCore::getFamily() {
|
|||
QString PayloadTypeCore::getMimeType() {
|
||||
return mMimeType;
|
||||
}
|
||||
|
||||
bool PayloadTypeCore::getDownloadable() {
|
||||
return mDownloadable;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,25 +33,32 @@ class PayloadTypeCore : public QObject, public AbstractObject {
|
|||
|
||||
Q_ENUMS(Family)
|
||||
Q_PROPERTY(Family family MEMBER mFamily CONSTANT)
|
||||
DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled)
|
||||
DECLARE_CORE_MEMBER(int, clockRate, ClockRate)
|
||||
DECLARE_CORE_MEMBER(QString, mimeType, MimeType)
|
||||
DECLARE_CORE_MEMBER(QString, recvFmtp, RecvFmtp)
|
||||
|
||||
public:
|
||||
|
||||
enum Family { None, Audio, Video, Text };
|
||||
|
||||
static QSharedPointer<PayloadTypeCore> create(const std::shared_ptr<linphone::PayloadType> &payloadType,
|
||||
Family family);
|
||||
PayloadTypeCore(const std::shared_ptr<linphone::PayloadType> &payloadType, Family family);
|
||||
static QSharedPointer<PayloadTypeCore> create(Family family,
|
||||
const std::shared_ptr<linphone::PayloadType> &payloadType);
|
||||
|
||||
PayloadTypeCore(Family family, const std::shared_ptr<linphone::PayloadType> &payloadType);
|
||||
PayloadTypeCore() {};
|
||||
~PayloadTypeCore();
|
||||
|
||||
void setSelf(QSharedPointer<PayloadTypeCore> me);
|
||||
Family getFamily();
|
||||
bool getDownloadable();
|
||||
QString getMimeType();
|
||||
|
||||
private:
|
||||
protected:
|
||||
Family mFamily;
|
||||
bool mDownloadable = false;
|
||||
DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled)
|
||||
DECLARE_CORE_MEMBER(QString, mimeType, MimeType)
|
||||
DECLARE_CORE_MEMBER(QString, encoderDescription, EncoderDescription)
|
||||
|
||||
private:
|
||||
std::shared_ptr<PayloadTypeModel> mPayloadTypeModel;
|
||||
QSharedPointer<SafeConnection<PayloadTypeCore, PayloadTypeModel>> mPayloadTypeModelConnection;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@
|
|||
*/
|
||||
|
||||
#include "PayloadTypeList.hpp"
|
||||
#include "DownloadablePayloadTypeCore.hpp"
|
||||
#include "PayloadTypeGui.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "model/object/VariantObject.hpp"
|
||||
#include <QSharedPointer>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
|
@ -37,12 +39,12 @@ QSharedPointer<PayloadTypeList> PayloadTypeList::create() {
|
|||
}
|
||||
|
||||
PayloadTypeList::PayloadTypeList(QObject *parent) : ListProxy(parent) {
|
||||
mustBeInMainThread(getClassName());
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
PayloadTypeList::~PayloadTypeList() {
|
||||
mustBeInMainThread("~" + getClassName());
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
mModelConnection = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -52,21 +54,40 @@ void PayloadTypeList::setSelf(QSharedPointer<PayloadTypeList> me) {
|
|||
mModelConnection->makeConnectToCore(&PayloadTypeList::lUpdate, [this]() {
|
||||
mModelConnection->invokeToModel([this]() {
|
||||
QList<QSharedPointer<PayloadTypeCore>> *payloadTypes = new QList<QSharedPointer<PayloadTypeCore>>();
|
||||
mustBeInLinphoneThread(getClassName());
|
||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||
|
||||
Utils::loadDownloadedCodecs();
|
||||
|
||||
// Audio
|
||||
for (auto payloadType : CoreModel::getInstance()->getCore()->getAudioPayloadTypes()) {
|
||||
auto model = PayloadTypeCore::create(payloadType, PayloadTypeCore::Family::Audio);
|
||||
payloadTypes->push_back(model);
|
||||
auto core = PayloadTypeCore::create(PayloadTypeCore::Family::Audio, payloadType);
|
||||
payloadTypes->push_back(core);
|
||||
}
|
||||
for (auto payloadType : CoreModel::getInstance()->getCore()->getVideoPayloadTypes()) {
|
||||
auto model = PayloadTypeCore::create(payloadType, PayloadTypeCore::Family::Video);
|
||||
payloadTypes->push_back(model);
|
||||
|
||||
// Video
|
||||
auto videoCodecs = CoreModel::getInstance()->getCore()->getVideoPayloadTypes();
|
||||
for (auto payloadType : videoCodecs) {
|
||||
auto core = PayloadTypeCore::create(PayloadTypeCore::Family::Video, payloadType);
|
||||
payloadTypes->push_back(core);
|
||||
}
|
||||
|
||||
// Downloadable Video
|
||||
for (auto downloadableVideoCodec : Utils::getDownloadableVideoPayloadTypes()) {
|
||||
if (find_if(videoCodecs.begin(), videoCodecs.end(),
|
||||
[downloadableVideoCodec](const std::shared_ptr<linphone::PayloadType> &codec) {
|
||||
return Utils::coreStringToAppString(codec->getMimeType()) ==
|
||||
downloadableVideoCodec->getMimeType();
|
||||
}) == videoCodecs.end())
|
||||
payloadTypes->append(downloadableVideoCodec.dynamicCast<PayloadTypeCore>());
|
||||
}
|
||||
|
||||
// Text
|
||||
for (auto payloadType : CoreModel::getInstance()->getCore()->getTextPayloadTypes()) {
|
||||
auto model = PayloadTypeCore::create(payloadType, PayloadTypeCore::Family::Text);
|
||||
payloadTypes->push_back(model);
|
||||
auto core = PayloadTypeCore::create(PayloadTypeCore::Family::Text, payloadType);
|
||||
payloadTypes->push_back(core);
|
||||
}
|
||||
mModelConnection->invokeToCore([this, payloadTypes]() {
|
||||
mustBeInMainThread(getClassName());
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
resetData<PayloadTypeCore>(*payloadTypes);
|
||||
delete payloadTypes;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class PayloadTypeList : public ListProxy, public AbstractObject {
|
|||
|
||||
public:
|
||||
static QSharedPointer<PayloadTypeList> create();
|
||||
|
||||
|
||||
PayloadTypeList(QObject *parent = Q_NULLPTR);
|
||||
~PayloadTypeList();
|
||||
|
||||
|
|
@ -49,6 +49,7 @@ signals:
|
|||
|
||||
private:
|
||||
QSharedPointer<SafeConnection<PayloadTypeList, CoreModel>> mModelConnection;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,21 @@ void PayloadTypeProxy::setFamily(PayloadTypeCore::Family data) {
|
|||
}
|
||||
}
|
||||
|
||||
bool PayloadTypeProxy::isDownloadable() const {
|
||||
return dynamic_cast<SortFilterList *>(sourceModel())->mDownloadable;
|
||||
}
|
||||
|
||||
void PayloadTypeProxy::setDownloadable(bool data) {
|
||||
auto list = dynamic_cast<SortFilterList *>(sourceModel());
|
||||
if (list->mDownloadable != data) {
|
||||
list->mDownloadable = data;
|
||||
downloadableChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool PayloadTypeProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||
auto payload = qobject_cast<PayloadTypeList *>(sourceModel())->getAt<PayloadTypeCore>(sourceRow);
|
||||
return payload->getFamily() == mFamily;
|
||||
return payload->getFamily() == mFamily && payload->getDownloadable() == mDownloadable;
|
||||
}
|
||||
|
||||
bool PayloadTypeProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
|
||||
|
|
@ -55,3 +67,7 @@ bool PayloadTypeProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, c
|
|||
|
||||
return l->getMimeType() < r->getMimeType();
|
||||
}
|
||||
|
||||
void PayloadTypeProxy::reload() {
|
||||
emit mPayloadTypeList->lUpdate();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,18 +31,24 @@ class PayloadTypeProxy : public LimitProxy, public AbstractObject {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(PayloadTypeCore::Family family READ getFamily WRITE setFamily NOTIFY familyChanged)
|
||||
Q_PROPERTY(bool downloadable READ isDownloadable WRITE setDownloadable NOTIFY downloadableChanged)
|
||||
|
||||
public:
|
||||
DECLARE_SORTFILTER_CLASS(PayloadTypeCore::Family mFamily;)
|
||||
DECLARE_SORTFILTER_CLASS(PayloadTypeCore::Family mFamily; bool mDownloadable;)
|
||||
|
||||
Q_INVOKABLE void reload();
|
||||
|
||||
PayloadTypeProxy(QObject *parent = Q_NULLPTR);
|
||||
~PayloadTypeProxy();
|
||||
|
||||
PayloadTypeCore::Family getFamily() const;
|
||||
void setFamily(PayloadTypeCore::Family data);
|
||||
bool isDownloadable() const;
|
||||
void setDownloadable(bool data);
|
||||
|
||||
signals:
|
||||
void familyChanged();
|
||||
void downloadableChanged();
|
||||
|
||||
protected:
|
||||
QSharedPointer<PayloadTypeList> mPayloadTypeList;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
|
|||
INIT_CORE_MEMBER(SyncLdapContacts, settingsModel)
|
||||
INIT_CORE_MEMBER(Ipv6Enabled, settingsModel)
|
||||
INIT_CORE_MEMBER(ConfigLocale, settingsModel)
|
||||
INIT_CORE_MEMBER(DownloadFolder, settingsModel)
|
||||
}
|
||||
|
||||
SettingsCore::~SettingsCore() {
|
||||
|
|
@ -348,6 +349,8 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
|
|||
Ipv6Enabled)
|
||||
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
|
||||
configLocale, ConfigLocale)
|
||||
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
|
||||
downloadFolder, DownloadFolder)
|
||||
|
||||
auto coreModelConnection = QSharedPointer<SafeConnection<SettingsCore, CoreModel>>(
|
||||
new SafeConnection<SettingsCore, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
|
||||
|
|
@ -523,3 +526,9 @@ bool SettingsCore::getSyncLdapContacts() const {
|
|||
QString SettingsCore::getConfigLocale() const {
|
||||
return mConfigLocale;
|
||||
}
|
||||
|
||||
QString SettingsCore::getDownloadFolder() const {
|
||||
auto path = mDownloadFolder;
|
||||
if (mDownloadFolder.isEmpty()) path = Paths::getDownloadDirPath();
|
||||
return QDir::cleanPath(path) + QDir::separator();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ public:
|
|||
DECLARE_CORE_GETSET_MEMBER(QVariantList, audioCodecs, AudioCodecs)
|
||||
DECLARE_CORE_GETSET_MEMBER(QVariantList, videoCodecs, VideoCodecs)
|
||||
DECLARE_CORE_GETSET(QString, configLocale, ConfigLocale)
|
||||
DECLARE_CORE_GETSET(QString, downloadFolder, DownloadFolder)
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
|||
|
|
@ -36,3 +36,4 @@ DEFINE_GETSET_ENABLE(PayloadTypeModel, enabled, Enabled, mPayloadType)
|
|||
DEFINE_GET(PayloadTypeModel, int, ClockRate, mPayloadType)
|
||||
DEFINE_GET_STRING(PayloadTypeModel, MimeType, mPayloadType)
|
||||
DEFINE_GET_STRING(PayloadTypeModel, RecvFmtp, mPayloadType)
|
||||
DEFINE_GET_STRING(PayloadTypeModel, EncoderDescription, mPayloadType)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public:
|
|||
int getClockRate() const;
|
||||
QString getMimeType() const;
|
||||
QString getRecvFmtp() const;
|
||||
QString getEncoderDescription() const;
|
||||
|
||||
DECLARE_GETSET(bool, enabled, Enabled)
|
||||
|
||||
|
|
|
|||
|
|
@ -571,6 +571,7 @@ void SettingsModel::notifyConfigReady(){
|
|||
DEFINE_NOTIFY_CONFIG_READY(exitOnClose, ExitOnClose)
|
||||
DEFINE_NOTIFY_CONFIG_READY(syncLdapContacts, SyncLdapContacts)
|
||||
DEFINE_NOTIFY_CONFIG_READY(configLocale, ConfigLocale)
|
||||
DEFINE_NOTIFY_CONFIG_READY(downloadFolder, DownloadFolder)
|
||||
}
|
||||
|
||||
DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, disableChatFeature, DisableChatFeature, "disable_chat_feature", true)
|
||||
|
|
@ -673,4 +674,9 @@ DEFINE_GETSET_CONFIG_STRING(SettingsModel,
|
|||
ConfigLocale,
|
||||
"locale",
|
||||
"")
|
||||
DEFINE_GETSET_CONFIG_STRING(SettingsModel,
|
||||
downloadFolder,
|
||||
DownloadFolder,
|
||||
"download_folder",
|
||||
"")
|
||||
// clang-format on
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ public:
|
|||
DECLARE_GETSET(bool, syncLdapContacts, SyncLdapContacts)
|
||||
DECLARE_GETSET(bool, ipv6Enabled, Ipv6Enabled)
|
||||
DECLARE_GETSET(QString, configLocale, ConfigLocale)
|
||||
DECLARE_GETSET(QString, downloadFolder, DownloadFolder)
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
|
||||
tool/request/RequestDialog.cpp
|
||||
tool/request/AuthenticationDialog.cpp
|
||||
|
||||
tool/file/FileDownloader.cpp
|
||||
tool/file/FileExtractor.cpp
|
||||
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
|
|
|||
|
|
@ -159,3 +159,4 @@ constexpr char Constants::LinphoneBZip2_exe[];
|
|||
constexpr char Constants::LinphoneBZip2_dll[];
|
||||
constexpr char Constants::DefaultRlsUri[];
|
||||
constexpr char Constants::DefaultLogsEmail[];
|
||||
constexpr char Constants::DownloadDefaultFileName[];
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ public:
|
|||
// 4 = RTP bundle mode
|
||||
// 5 = Video Conference URI
|
||||
// 6 = Publish expires
|
||||
static constexpr char DownloadDefaultFileName[] = "download";
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// CISCO
|
||||
//--------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "core/conference/ConferenceInfoGui.hpp"
|
||||
#include "core/friend/FriendGui.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "core/payload-type/DownloadablePayloadTypeCore.hpp"
|
||||
#include "model/object/VariantObject.hpp"
|
||||
#include "model/tool/ToolModel.hpp"
|
||||
#include "tool/providers/AvatarProvider.hpp"
|
||||
|
|
@ -33,13 +34,18 @@
|
|||
#include <limits.h>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDesktopServices>
|
||||
#include <QDirIterator>
|
||||
#include <QHostAddress>
|
||||
#include <QImageReader>
|
||||
#include <QLibrary>
|
||||
#include <QQuickWindow>
|
||||
#include <QRandomGenerator>
|
||||
#include <QRegularExpression>
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(Utils)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
char *Utils::rstrstr(const char *a, const char *b) {
|
||||
|
|
@ -1403,3 +1409,77 @@ QString Utils::boldTextPart(const QString &text, const QString ®ex) {
|
|||
if (splittedText.size() > 0) result.append(splittedText[splittedText.size() - 1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
QString Utils::getFileChecksum(const QString &filePath) {
|
||||
QFile file(filePath);
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QCryptographicHash hash(QCryptographicHash::Sha256);
|
||||
if (hash.addData(&file)) {
|
||||
return hash.result().toHex();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Codecs download
|
||||
|
||||
QList<QSharedPointer<DownloadablePayloadTypeCore>> Utils::getDownloadableVideoPayloadTypes() {
|
||||
QList<QSharedPointer<DownloadablePayloadTypeCore>> payloadTypes;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
auto ciscoH264 = DownloadablePayloadTypeCore::create(PayloadTypeCore::Family::Video, "H264",
|
||||
Constants::H264Description, Constants::PluginUrlH264,
|
||||
Constants::H264InstallName, Constants::PluginH264Check);
|
||||
payloadTypes.push_back(ciscoH264);
|
||||
#endif
|
||||
return payloadTypes;
|
||||
}
|
||||
|
||||
void Utils::checkDownloadedCodecsUpdates() {
|
||||
for (auto codec : getDownloadableVideoPayloadTypes()) {
|
||||
if (codec->shouldDownloadUpdate()) codec->downloadAndExtract(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Load downloaded codecs like OpenH264 (needs to be after core is created and has loaded its plugins, as
|
||||
// reloadMsPlugins modifies plugin path for the factory)
|
||||
void Utils::loadDownloadedCodecs() {
|
||||
mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO));
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
QDirIterator it(Paths::getCodecsDirPath());
|
||||
while (it.hasNext()) {
|
||||
QFileInfo info(it.next());
|
||||
const QString filename(info.fileName());
|
||||
if (QLibrary::isLibrary(filename)) {
|
||||
qInfo() << QStringLiteral("Loading `%1` symbols...").arg(filename);
|
||||
if (!QLibrary(info.filePath()).load()) // lib.load())
|
||||
qWarning() << QStringLiteral("Failed to load `%1` symbols.").arg(filename);
|
||||
else qInfo() << QStringLiteral("Loaded `%1` symbols...").arg(filename);
|
||||
}
|
||||
}
|
||||
CoreModel::getInstance()->getCore()->reloadMsPlugins("");
|
||||
#endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
}
|
||||
|
||||
// Removes .in suffix from downloaded updates.
|
||||
// Updates are downloaded with .in suffix as they can't overwrite already loaded plugin
|
||||
// they are loaded at next app startup.
|
||||
|
||||
void Utils::updateCodecs() {
|
||||
mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO));
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
static const QString codecSuffix = QStringLiteral(".%1").arg(Constants::LibraryExtension);
|
||||
|
||||
QDirIterator it(Paths::getCodecsDirPath());
|
||||
while (it.hasNext()) {
|
||||
QFileInfo info(it.next());
|
||||
if (info.suffix() == QLatin1String("in")) {
|
||||
QString codecName = info.completeBaseName();
|
||||
if (codecName.endsWith(codecSuffix)) {
|
||||
QString codecPath = info.dir().path() + QDir::separator() + codecName;
|
||||
QFile::remove(codecPath);
|
||||
QFile::rename(info.filePath(), codecPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <QString>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include "tool/LinphoneEnums.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -47,8 +48,9 @@ class QQuickWindow;
|
|||
class VariantObject;
|
||||
class CallGui;
|
||||
class ConferenceInfoGui;
|
||||
class DownloadablePayloadTypeCore;
|
||||
|
||||
class Utils : public QObject {
|
||||
class Utils : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Utils(QObject *parent = nullptr) : QObject(parent) {
|
||||
|
|
@ -131,10 +133,15 @@ public:
|
|||
Q_INVOKABLE void playDtmf(const QString &dtmf);
|
||||
Q_INVOKABLE bool isInteger(const QString &text);
|
||||
Q_INVOKABLE QString boldTextPart(const QString &text, const QString ®ex);
|
||||
Q_INVOKABLE static QString getFileChecksum(const QString &filePath);
|
||||
|
||||
static QString getApplicationProduct();
|
||||
static QString getOsProduct();
|
||||
static QString computeUserAgent();
|
||||
static QList<QSharedPointer<DownloadablePayloadTypeCore>> getDownloadableVideoPayloadTypes();
|
||||
static void checkDownloadedCodecsUpdates();
|
||||
static void loadDownloadedCodecs();
|
||||
static void updateCodecs();
|
||||
|
||||
static inline QString coreStringToAppString(const std::string &str) {
|
||||
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
|
||||
|
|
@ -165,6 +172,9 @@ public:
|
|||
|
||||
return (volume - VuMin) / (VuMax - VuMin);
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#define lDebug() qDebug().noquote()
|
||||
|
|
|
|||
290
Linphone/tool/file/FileDownloader.cpp
Normal file
290
Linphone/tool/file/FileDownloader.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "core/App.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
|
||||
#include "FileDownloader.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static QString getDownloadFilePath(const QString &folder, const QUrl &url, const bool &overwrite) {
|
||||
QString defaultFileName = QString(Constants::DownloadDefaultFileName);
|
||||
QFileInfo fileInfo(url.path());
|
||||
QString fileName = fileInfo.fileName();
|
||||
if (fileName.isEmpty()) fileName = defaultFileName;
|
||||
|
||||
fileName.prepend(folder);
|
||||
if (overwrite && QFile::exists(fileName)) QFile::remove(fileName);
|
||||
if (!QFile::exists(fileName)) return fileName;
|
||||
|
||||
// Already exists, don't overwrite.
|
||||
QString baseName = fileInfo.completeBaseName();
|
||||
if (baseName.isEmpty()) baseName = defaultFileName;
|
||||
|
||||
QString suffix = fileInfo.suffix();
|
||||
if (!suffix.isEmpty()) suffix.prepend(".");
|
||||
|
||||
for (int i = 1; true; ++i) {
|
||||
fileName = folder + baseName + "(" + QString::number(i) + ")" + suffix;
|
||||
if (!QFile::exists(fileName)) break;
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
static bool isHttpRedirect(QNetworkReply *reply) {
|
||||
Q_CHECK_PTR(reply);
|
||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
|
||||
statusCode == 308;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void FileDownloader::download() {
|
||||
if (mDownloading) {
|
||||
qWarning() << "Unable to download file. Already downloading!";
|
||||
return;
|
||||
}
|
||||
setDownloading(true);
|
||||
|
||||
QNetworkRequest request(mUrl);
|
||||
mNetworkReply = mManager.get(request);
|
||||
|
||||
QNetworkReply *data = mNetworkReply.data();
|
||||
|
||||
QObject::connect(data, &QNetworkReply::readyRead, this, &FileDownloader::handleReadyData);
|
||||
QObject::connect(data, &QNetworkReply::finished, this, &FileDownloader::handleDownloadFinished);
|
||||
QObject::connect(data, &QNetworkReply::errorOccurred, this, &FileDownloader::handleError);
|
||||
QObject::connect(data, &QNetworkReply::downloadProgress, this, &FileDownloader::handleDownloadProgress);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
QObject::connect(data, &QNetworkReply::sslErrors, this, &FileDownloader::handleSslErrors);
|
||||
#endif
|
||||
|
||||
if (mDownloadFolder.isEmpty()) {
|
||||
mDownloadFolder = App::getInstance()->getSettings()->getDownloadFolder();
|
||||
emit downloadFolderChanged(mDownloadFolder);
|
||||
}
|
||||
|
||||
Q_ASSERT(!mDestinationFile.isOpen());
|
||||
mDestinationFile.setFileName(
|
||||
getDownloadFilePath(QDir::cleanPath(mDownloadFolder) + QDir::separator(), mUrl, mOverwriteFile));
|
||||
if (!mDestinationFile.open(QIODevice::WriteOnly)) emitOutputError();
|
||||
else {
|
||||
mTimeoutReadBytes = 0;
|
||||
mTimeout.start();
|
||||
}
|
||||
}
|
||||
|
||||
bool FileDownloader::remove() {
|
||||
return mDestinationFile.exists() && !mDestinationFile.isOpen() && mDestinationFile.remove();
|
||||
}
|
||||
|
||||
void FileDownloader::emitOutputError() {
|
||||
qWarning() << QStringLiteral("Could not write into `%1` (%2).")
|
||||
.arg(mDestinationFile.fileName())
|
||||
.arg(mDestinationFile.errorString());
|
||||
mNetworkReply->abort();
|
||||
}
|
||||
|
||||
void FileDownloader::cleanDownloadEnd() {
|
||||
mTimeout.stop();
|
||||
mNetworkReply->deleteLater();
|
||||
setDownloading(false);
|
||||
}
|
||||
|
||||
void FileDownloader::handleReadyData() {
|
||||
QByteArray data = mNetworkReply->readAll();
|
||||
if (mDestinationFile.write(data) == -1) emitOutputError();
|
||||
}
|
||||
|
||||
void FileDownloader::handleDownloadFinished() {
|
||||
if (mNetworkReply->error() != QNetworkReply::NoError) return;
|
||||
|
||||
if (isHttpRedirect(mNetworkReply)) {
|
||||
qWarning() << QStringLiteral("Request was redirected.");
|
||||
mDestinationFile.remove();
|
||||
cleanDownloadEnd();
|
||||
emit downloadFailed();
|
||||
} else {
|
||||
qInfo() << QStringLiteral("Download of %1 finished to %2").arg(mUrl.toString(), mDestinationFile.fileName());
|
||||
mDestinationFile.close();
|
||||
cleanDownloadEnd();
|
||||
QString fileChecksum = Utils::getFileChecksum(mDestinationFile.fileName());
|
||||
if (mCheckSum.isEmpty() || fileChecksum == mCheckSum) emit downloadFinished(mDestinationFile.fileName());
|
||||
else {
|
||||
qCritical() << "File cannot be downloaded : Bad checksum " << fileChecksum;
|
||||
mDestinationFile.remove();
|
||||
emit downloadFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileDownloader::handleError(QNetworkReply::NetworkError code) {
|
||||
if (code != QNetworkReply::OperationCanceledError)
|
||||
qWarning()
|
||||
<< QStringLiteral("Download of %1 failed: %2").arg(mUrl.toString()).arg(mNetworkReply->errorString());
|
||||
mDestinationFile.remove();
|
||||
|
||||
cleanDownloadEnd();
|
||||
|
||||
emit downloadFailed();
|
||||
}
|
||||
|
||||
void FileDownloader::handleSslErrors(const QList<QSslError> &sslErrors) {
|
||||
#if QT_CONFIG(ssl)
|
||||
for (const QSslError &error : sslErrors)
|
||||
qWarning() << QStringLiteral("SSL error: %1").arg(error.errorString());
|
||||
#else
|
||||
Q_UNUSED(sslErrors);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileDownloader::handleTimeout() {
|
||||
if (mReadBytes == mTimeoutReadBytes) {
|
||||
qWarning() << QStringLiteral("Download of %1 failed: timeout.").arg(mUrl.toString());
|
||||
mNetworkReply->abort();
|
||||
} else mTimeoutReadBytes = mReadBytes;
|
||||
}
|
||||
|
||||
void FileDownloader::handleDownloadProgress(qint64 readBytes, qint64 totalBytes) {
|
||||
setReadBytes(readBytes);
|
||||
setTotalBytes(totalBytes);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
QUrl FileDownloader::getUrl() const {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
void FileDownloader::setUrl(const QUrl &url) {
|
||||
if (mDownloading) {
|
||||
qWarning() << QStringLiteral("Unable to set url, a file is downloading.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUrl != url) {
|
||||
mUrl = url;
|
||||
if (!QSslSocket::supportsSsl() && mUrl.scheme() == "https") {
|
||||
qWarning() << "Https has been requested but SSL is not supported. Fallback to http. Install manually "
|
||||
"OpenSSL libraries in your PATH.";
|
||||
mUrl.setScheme("http");
|
||||
}
|
||||
emit urlChanged(mUrl);
|
||||
}
|
||||
}
|
||||
|
||||
QString FileDownloader::getDownloadFolder() const {
|
||||
return mDownloadFolder;
|
||||
}
|
||||
|
||||
void FileDownloader::setDownloadFolder(const QString &downloadFolder) {
|
||||
if (mDownloading) {
|
||||
qWarning() << QStringLiteral("Unable to set download folder, a file is downloading.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDownloadFolder != downloadFolder) {
|
||||
mDownloadFolder = downloadFolder;
|
||||
emit downloadFolderChanged(mDownloadFolder);
|
||||
}
|
||||
}
|
||||
|
||||
QString FileDownloader::getDestinationFileName() const {
|
||||
return mDestinationFile.fileName();
|
||||
}
|
||||
|
||||
void FileDownloader::setOverwriteFile(const bool &overwrite) {
|
||||
mOverwriteFile = overwrite;
|
||||
}
|
||||
|
||||
QString
|
||||
FileDownloader::synchronousDownload(const QUrl &url, const QString &destinationFolder, const bool &overwriteFile) {
|
||||
QString filePath;
|
||||
FileDownloader downloader;
|
||||
if (url.isRelative()) qWarning() << "FileDownloader: The specified URL is not valid";
|
||||
else {
|
||||
bool isOver = false;
|
||||
bool *pIsOver = &isOver;
|
||||
downloader.setUrl(url);
|
||||
downloader.setOverwriteFile(overwriteFile);
|
||||
downloader.setDownloadFolder(destinationFolder);
|
||||
connect(&downloader, &FileDownloader::downloadFinished, [pIsOver]() mutable { *pIsOver = true; });
|
||||
connect(&downloader, &FileDownloader::downloadFailed, [pIsOver]() mutable { *pIsOver = true; });
|
||||
downloader.download();
|
||||
if (QTest::qWaitFor([&]() { return isOver; }, DefaultTimeout)) {
|
||||
filePath = downloader.getDestinationFileName();
|
||||
if (!QFile::exists(filePath)) {
|
||||
filePath = "";
|
||||
qWarning() << "FileDownloader: Cannot download the specified file";
|
||||
}
|
||||
}
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
QString FileDownloader::getChecksum() const {
|
||||
return mCheckSum;
|
||||
}
|
||||
|
||||
void FileDownloader::setChecksum(const QString &code) {
|
||||
if (mCheckSum != code) {
|
||||
mCheckSum = code;
|
||||
emit checksumChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 FileDownloader::getReadBytes() const {
|
||||
return mReadBytes;
|
||||
}
|
||||
|
||||
void FileDownloader::setReadBytes(qint64 readBytes) {
|
||||
if (mReadBytes != readBytes) {
|
||||
mReadBytes = readBytes;
|
||||
emit readBytesChanged(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 FileDownloader::getTotalBytes() const {
|
||||
return mTotalBytes;
|
||||
}
|
||||
|
||||
void FileDownloader::setTotalBytes(qint64 totalBytes) {
|
||||
if (mTotalBytes != totalBytes) {
|
||||
mTotalBytes = totalBytes;
|
||||
emit totalBytesChanged(totalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileDownloader::getDownloading() const {
|
||||
return mDownloading;
|
||||
}
|
||||
|
||||
void FileDownloader::setDownloading(bool downloading) {
|
||||
if (mDownloading != downloading) {
|
||||
mDownloading = downloading;
|
||||
emit downloadingChanged(downloading);
|
||||
}
|
||||
}
|
||||
126
Linphone/tool/file/FileDownloader.hpp
Normal file
126
Linphone/tool/file/FileDownloader.hpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILE_DOWNLOADER_H_
|
||||
#define FILE_DOWNLOADER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QtNetwork>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class QSslError;
|
||||
|
||||
class FileDownloader : public QObject {
|
||||
Q_OBJECT;
|
||||
|
||||
// TODO: Add an error property to use in UI.
|
||||
|
||||
Q_PROPERTY(QUrl url READ getUrl WRITE setUrl NOTIFY urlChanged);
|
||||
Q_PROPERTY(QString downloadFolder READ getDownloadFolder WRITE setDownloadFolder NOTIFY downloadFolderChanged);
|
||||
Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged);
|
||||
Q_PROPERTY(qint64 totalBytes READ getTotalBytes NOTIFY totalBytesChanged);
|
||||
Q_PROPERTY(bool downloading READ getDownloading NOTIFY downloadingChanged);
|
||||
Q_PROPERTY(QString checksum READ getChecksum WRITE setChecksum NOTIFY checksumChanged);
|
||||
|
||||
public:
|
||||
FileDownloader(QObject *parent = Q_NULLPTR) : QObject(parent) {
|
||||
// See: https://bugreports.qt.io/browse/QTBUG-57390
|
||||
mTimeout.setInterval(DefaultTimeout);
|
||||
QObject::connect(&mTimeout, &QTimer::timeout, this, &FileDownloader::handleTimeout);
|
||||
}
|
||||
|
||||
~FileDownloader() {
|
||||
if (mNetworkReply) mNetworkReply->abort();
|
||||
}
|
||||
|
||||
Q_INVOKABLE void download();
|
||||
Q_INVOKABLE bool remove();
|
||||
|
||||
QUrl getUrl() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
QString getDownloadFolder() const;
|
||||
void setDownloadFolder(const QString &downloadFolder);
|
||||
|
||||
QString getDestinationFileName() const;
|
||||
|
||||
void setOverwriteFile(const bool &overwrite);
|
||||
static QString
|
||||
synchronousDownload(const QUrl &url,
|
||||
const QString &destinationFolder,
|
||||
const bool &overwriteFile); // Return the filpath. Empty if nof file could be downloaded
|
||||
|
||||
QString getChecksum() const;
|
||||
void setChecksum(const QString &code);
|
||||
|
||||
signals:
|
||||
void urlChanged(const QUrl &url);
|
||||
void downloadFolderChanged(const QString &downloadFolder);
|
||||
void readBytesChanged(qint64 readBytes);
|
||||
void totalBytesChanged(qint64 totalBytes);
|
||||
void downloadingChanged(bool downloading);
|
||||
void downloadFinished(const QString &filePath);
|
||||
void downloadFailed();
|
||||
void checksumChanged();
|
||||
|
||||
private:
|
||||
qint64 getReadBytes() const;
|
||||
void setReadBytes(qint64 readBytes);
|
||||
|
||||
qint64 getTotalBytes() const;
|
||||
void setTotalBytes(qint64 totalBytes);
|
||||
|
||||
bool getDownloading() const;
|
||||
void setDownloading(bool downloading);
|
||||
|
||||
void emitOutputError();
|
||||
|
||||
void cleanDownloadEnd();
|
||||
|
||||
void handleReadyData();
|
||||
void handleDownloadFinished();
|
||||
|
||||
void handleError(QNetworkReply::NetworkError code);
|
||||
void handleSslErrors(const QList<QSslError> &errors);
|
||||
void handleTimeout();
|
||||
void handleDownloadProgress(qint64 readBytes, qint64 totalBytes);
|
||||
|
||||
QUrl mUrl;
|
||||
QString mDownloadFolder;
|
||||
QFile mDestinationFile;
|
||||
QString mCheckSum;
|
||||
|
||||
qint64 mReadBytes = 0;
|
||||
qint64 mTotalBytes = 0;
|
||||
bool mDownloading = false;
|
||||
bool mOverwriteFile = false;
|
||||
|
||||
QPointer<QNetworkReply> mNetworkReply;
|
||||
QNetworkAccessManager mManager;
|
||||
|
||||
qint64 mTimeoutReadBytes;
|
||||
QTimer mTimeout;
|
||||
|
||||
static constexpr int DefaultTimeout = 5000;
|
||||
};
|
||||
|
||||
#endif // FILE_DOWNLOADER_H_
|
||||
237
Linphone/tool/file/FileExtractor.cpp
Normal file
237
Linphone/tool/file/FileExtractor.cpp
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#include "FileDownloader.hpp"
|
||||
#include "FileExtractor.hpp"
|
||||
#include "core/path/Paths.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
FileExtractor::FileExtractor(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
FileExtractor::~FileExtractor() {
|
||||
}
|
||||
|
||||
void FileExtractor::extract() {
|
||||
if (mExtracting) {
|
||||
qWarning() << "Unable to extract file. Already extracting!";
|
||||
return;
|
||||
}
|
||||
setExtracting(true);
|
||||
QFileInfo fileInfo(mFile);
|
||||
if (!fileInfo.isReadable()) {
|
||||
emitExtractFailed(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
mDestinationFile = QDir::cleanPath(mExtractFolder) + QDir::separator() +
|
||||
(mExtractName.isEmpty() ? fileInfo.completeBaseName() : mExtractName);
|
||||
if (QFile::exists(mDestinationFile) && !QFile::remove(mDestinationFile)) {
|
||||
emitOutputError();
|
||||
return;
|
||||
}
|
||||
if (mTimer == nullptr) {
|
||||
mTimer = new QTimer(this);
|
||||
QObject::connect(mTimer, &QTimer::timeout, this, &FileExtractor::handleExtraction);
|
||||
}
|
||||
#ifdef WIN32
|
||||
// Test the presence of bzip2 in the system
|
||||
QProcess process;
|
||||
process.closeReadChannel(QProcess::StandardOutput);
|
||||
process.closeReadChannel(QProcess::StandardError);
|
||||
process.start("bzip2.exe", QStringList("--help"));
|
||||
// int result = QProcess::execute("bzip2.exe", QStringList("--help"));
|
||||
if (process.error() != QProcess::FailedToStart ||
|
||||
QProcess::execute(Paths::getToolsDirPath() + "\\bzip2.exe", QStringList()) != -2) {
|
||||
mTimer->start();
|
||||
} else { // Download bzip2
|
||||
qWarning() << "bzip2 was not found. Downloading it.";
|
||||
QTimer *timer = mTimer;
|
||||
FileDownloader *fileDownloader = new FileDownloader();
|
||||
int downloadStep = 0;
|
||||
fileDownloader->setUrl(QUrl(Constants::LinphoneBZip2_exe));
|
||||
fileDownloader->setDownloadFolder(Paths::getToolsDirPath());
|
||||
QObject::connect(fileDownloader, &FileDownloader::totalBytesChanged, this, &FileExtractor::setTotalBytes);
|
||||
QObject::connect(fileDownloader, &FileDownloader::readBytesChanged, this, &FileExtractor::setReadBytes);
|
||||
|
||||
QObject::connect(fileDownloader, &FileDownloader::downloadFinished,
|
||||
[fileDownloader, timer, downloadStep, this]() mutable {
|
||||
if (downloadStep++ == 0) {
|
||||
fileDownloader->setUrl(QUrl(Constants::LinphoneBZip2_dll));
|
||||
fileDownloader->download();
|
||||
} else {
|
||||
fileDownloader->deleteLater();
|
||||
QObject::disconnect(fileDownloader, &FileDownloader::totalBytesChanged, this,
|
||||
&FileExtractor::setTotalBytes);
|
||||
QObject::disconnect(fileDownloader, &FileDownloader::readBytesChanged, this,
|
||||
&FileExtractor::setReadBytes);
|
||||
timer->start();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(fileDownloader, &FileDownloader::downloadFailed, [fileDownloader, this]() {
|
||||
fileDownloader->deleteLater();
|
||||
emitExtractorFailed();
|
||||
});
|
||||
fileDownloader->download();
|
||||
}
|
||||
#else
|
||||
mTimer->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FileExtractor::remove() {
|
||||
return QFile::exists(mDestinationFile) && QFile::remove(mDestinationFile);
|
||||
}
|
||||
|
||||
QString FileExtractor::getFile() const {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
void FileExtractor::setFile(const QString &file) {
|
||||
if (mExtracting) {
|
||||
qWarning() << QStringLiteral("Unable to set file, a file is extracting.");
|
||||
return;
|
||||
}
|
||||
if (mFile != file) {
|
||||
mFile = file;
|
||||
emit fileChanged(mFile);
|
||||
}
|
||||
}
|
||||
|
||||
QString FileExtractor::getExtractFolder() const {
|
||||
return mExtractFolder;
|
||||
}
|
||||
|
||||
void FileExtractor::setExtractFolder(const QString &extractFolder) {
|
||||
if (mExtracting) {
|
||||
qWarning() << QStringLiteral("Unable to set extract folder, a file is extracting.");
|
||||
return;
|
||||
}
|
||||
if (mExtractFolder != extractFolder) {
|
||||
mExtractFolder = extractFolder;
|
||||
emit extractFolderChanged(mExtractFolder);
|
||||
}
|
||||
}
|
||||
|
||||
QString FileExtractor::getExtractName() const {
|
||||
return mExtractName;
|
||||
}
|
||||
|
||||
void FileExtractor::setExtractName(const QString &extractName) {
|
||||
if (mExtracting) {
|
||||
qWarning() << QStringLiteral("Unable to set extract name, a file is extracting.");
|
||||
return;
|
||||
}
|
||||
if (mExtractName != extractName) {
|
||||
mExtractName = extractName;
|
||||
emit extractNameChanged(mExtractName);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileExtractor::getExtracting() const {
|
||||
return mExtracting;
|
||||
}
|
||||
|
||||
void FileExtractor::setExtracting(bool extracting) {
|
||||
if (mExtracting != extracting) {
|
||||
mExtracting = extracting;
|
||||
emit extractingChanged(extracting);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 FileExtractor::getReadBytes() const {
|
||||
return mReadBytes;
|
||||
}
|
||||
|
||||
void FileExtractor::setReadBytes(qint64 readBytes) {
|
||||
mReadBytes = readBytes;
|
||||
emit readBytesChanged(readBytes);
|
||||
}
|
||||
|
||||
qint64 FileExtractor::getTotalBytes() const {
|
||||
return mTotalBytes;
|
||||
}
|
||||
|
||||
void FileExtractor::setTotalBytes(qint64 totalBytes) {
|
||||
mTotalBytes = totalBytes;
|
||||
emit totalBytesChanged(totalBytes);
|
||||
}
|
||||
void FileExtractor::clean() {
|
||||
if (mTimer) {
|
||||
mTimer->stop();
|
||||
mTimer->deleteLater();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
setExtracting(false);
|
||||
}
|
||||
|
||||
void FileExtractor::emitExtractorFailed() {
|
||||
qWarning() << QStringLiteral("Unable to extract file `%1`. bzip2 is unavailable, please install it.").arg(mFile);
|
||||
clean();
|
||||
emit extractFailed();
|
||||
}
|
||||
void FileExtractor::emitExtractFailed(int error) {
|
||||
qWarning() << QStringLiteral("Unable to extract file with bzip2: `%1` (code: %2).").arg(mFile).arg(error);
|
||||
clean();
|
||||
emit extractFailed();
|
||||
}
|
||||
|
||||
void FileExtractor::emitExtractFinished() {
|
||||
clean();
|
||||
emit extractFinished();
|
||||
}
|
||||
|
||||
void FileExtractor::emitOutputError() {
|
||||
qWarning() << QStringLiteral("Could not write into `%1`.").arg(mDestinationFile);
|
||||
clean();
|
||||
emit extractFailed();
|
||||
}
|
||||
|
||||
void FileExtractor::handleExtraction() {
|
||||
QString tempDestination = mDestinationFile + "." + QFileInfo(mFile).suffix();
|
||||
QStringList args;
|
||||
args.push_back("-dq");
|
||||
args.push_back(tempDestination);
|
||||
QFile::copy(mFile, tempDestination);
|
||||
#ifdef WIN32
|
||||
int result = QProcess::execute("bzip2.exe", args);
|
||||
if (result == -2) result = QProcess::execute(Paths::getToolsDirPath() + "\\bzip2.exe", args);
|
||||
#else
|
||||
int result = QProcess::execute("bzip2", args);
|
||||
#endif
|
||||
if (QFile::exists(tempDestination)) QFile::remove(tempDestination);
|
||||
if (result == 0) {
|
||||
setReadBytes(getTotalBytes());
|
||||
emitExtractFinished();
|
||||
} else if (result > 0) emitExtractFailed(result);
|
||||
else if (result == -2) emitExtractorFailed();
|
||||
else emitOutputError();
|
||||
}
|
||||
104
Linphone/tool/file/FileExtractor.hpp
Normal file
104
Linphone/tool/file/FileExtractor.hpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILE_EXTRACTOR_H_
|
||||
#define FILE_EXTRACTOR_H_
|
||||
|
||||
#include <QFile>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class QTimer;
|
||||
|
||||
// Supports only bzip file.
|
||||
class FileExtractor : public QObject {
|
||||
|
||||
Q_OBJECT;
|
||||
|
||||
// TODO: Add an error property to use in UI.
|
||||
|
||||
Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged);
|
||||
Q_PROPERTY(QString extractFolder READ getExtractFolder WRITE setExtractFolder NOTIFY extractFolderChanged);
|
||||
Q_PROPERTY(QString extractName READ getExtractName WRITE setExtractName NOTIFY extractNameChanged);
|
||||
Q_PROPERTY(bool extracting READ getExtracting NOTIFY extractingChanged);
|
||||
Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged);
|
||||
Q_PROPERTY(qint64 totalBytes READ getTotalBytes NOTIFY totalBytesChanged);
|
||||
|
||||
public:
|
||||
FileExtractor(QObject *parent = nullptr);
|
||||
~FileExtractor();
|
||||
|
||||
Q_INVOKABLE void extract();
|
||||
Q_INVOKABLE bool remove();
|
||||
|
||||
QString getFile() const;
|
||||
void setFile(const QString &file);
|
||||
|
||||
QString getExtractFolder() const;
|
||||
void setExtractFolder(const QString &extractFolder);
|
||||
|
||||
QString getExtractName() const;
|
||||
void setExtractName(const QString &extractName);
|
||||
|
||||
signals:
|
||||
void fileChanged(const QString &file);
|
||||
|
||||
void extractFolderChanged(const QString &extractFolder);
|
||||
void extractNameChanged(const QString &extractName);
|
||||
|
||||
void readBytesChanged(qint64 readBytes);
|
||||
void totalBytesChanged(qint64 totalBytes);
|
||||
|
||||
void extractingChanged(bool extracting);
|
||||
void extractFinished();
|
||||
void extractFailed();
|
||||
|
||||
private:
|
||||
qint64 getReadBytes() const;
|
||||
void setReadBytes(qint64 readBytes);
|
||||
|
||||
qint64 getTotalBytes() const;
|
||||
void setTotalBytes(qint64 totalBytes);
|
||||
|
||||
bool getExtracting() const;
|
||||
void setExtracting(bool extracting);
|
||||
|
||||
void clean();
|
||||
|
||||
void emitExtractFinished();
|
||||
void emitExtractorFailed(); // Used when bzip2 cannot be used
|
||||
void emitExtractFailed(int error);
|
||||
void emitOutputError();
|
||||
|
||||
void handleExtraction();
|
||||
|
||||
QString mFile;
|
||||
QString mExtractFolder;
|
||||
QString mExtractName;
|
||||
QString mDestinationFile;
|
||||
|
||||
bool mExtracting = false;
|
||||
qint64 mReadBytes = 0;
|
||||
qint64 mTotalBytes = 0;
|
||||
|
||||
QTimer *mTimer = nullptr;
|
||||
};
|
||||
|
||||
#endif // FILE_EXTRACTOR_H_
|
||||
|
|
@ -12,6 +12,12 @@ RowLayout {
|
|||
property bool enabled: true
|
||||
spacing : 20 * DefaultStyle.dp
|
||||
Layout.minimumHeight: 32 * DefaultStyle.dp
|
||||
signal checkedChanged(bool checked)
|
||||
|
||||
function setChecked(value) {
|
||||
switchButton.checked = value
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
text: titleText
|
||||
|
|
@ -34,9 +40,8 @@ RowLayout {
|
|||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
checked: propertyOwner[mainItem.propertyName]
|
||||
enabled: mainItem.enabled
|
||||
onToggled: {
|
||||
binding.when = true
|
||||
}
|
||||
onCheckedChanged: mainItem.checkedChanged(checked)
|
||||
onToggled: binding.when = true
|
||||
}
|
||||
Binding {
|
||||
id: binding
|
||||
|
|
|
|||
|
|
@ -684,34 +684,33 @@ function computeAvatarSize (container, maxSize, ratio) {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function openCodecOnlineInstallerDialog (window, codecInfo, cb) {
|
||||
var VideoCodecsModel = Linphone.VideoCodecsModel
|
||||
window.attachVirtualWindow(buildCommonDialogUri('ConfirmDialog'), {
|
||||
descriptionText: qsTr('downloadCodecDescription')
|
||||
.replace('%1', codecInfo.mime)
|
||||
.replace('%2', codecInfo.encoderDescription)
|
||||
}, function (status) {
|
||||
if (status) {
|
||||
window.attachVirtualWindow(buildLinphoneDialogUri('OnlineInstallerDialog'), {
|
||||
downloadUrl: codecInfo.downloadUrl,
|
||||
extract: true,
|
||||
installFolder: VideoCodecsModel.codecsFolder,
|
||||
installName: codecInfo.installName,
|
||||
mime: codecInfo.mime,
|
||||
checksum: codecInfo.checksum
|
||||
}, function (status) {
|
||||
if (status) {
|
||||
VideoCodecsModel.reload()
|
||||
}
|
||||
if (cb) {
|
||||
cb(window)
|
||||
}
|
||||
})
|
||||
}
|
||||
else if (cb) {
|
||||
cb(window)
|
||||
}
|
||||
})
|
||||
function openCodecOnlineInstallerDialog (mainWindow, coreObject, cancelCallBack, successCallBack) {
|
||||
mainWindow.showConfirmationLambdaPopup("",
|
||||
qsTr("Installation de codec"),
|
||||
qsTr("Télécharger le codec ") + capitalizeFirstLetter(coreObject.mimeType) + " ("+coreObject.encoderDescription+")"+" ?",
|
||||
function (confirmed) {
|
||||
if (confirmed) {
|
||||
coreObject.success.connect(function() {
|
||||
if (successCallBack)
|
||||
successCallBack()
|
||||
mainWindow.closeLoadingPopup()
|
||||
mainWindow.showInformationPopup(qsTr("Succès"), qsTr("Le codec a été téléchargé avec succès."), true)
|
||||
})
|
||||
coreObject.extractError.connect(function() {
|
||||
mainWindow.closeLoadingPopup()
|
||||
mainWindow.showInformationPopup(qsTr("Erreur"), qsTr("Le codec n'a pas pu être sauvegardé."), true)
|
||||
})
|
||||
coreObject.downloadError.connect(function() {
|
||||
mainWindow.closeLoadingPopup()
|
||||
mainWindow.showInformationPopup(qsTr("Erreur"), qsTr("Le codec n'a pas pu être téléchargé."), true)
|
||||
})
|
||||
mainWindow.showLoadingPopup(qsTr("Téléchargement en cours ..."))
|
||||
coreObject.downloadAndExtract()
|
||||
} else
|
||||
if (cancelCallBack)
|
||||
cancelCallBack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function printObject(o) {
|
||||
|
|
|
|||
|
|
@ -150,16 +150,43 @@ AbstractSettingsLayout {
|
|||
Layout.leftMargin: 64 * DefaultStyle.dp
|
||||
Repeater {
|
||||
model: PayloadTypeProxy {
|
||||
id: videoPayloadTypeProxy
|
||||
family: PayloadTypeCore.Video
|
||||
}
|
||||
SwitchSetting {
|
||||
Layout.fillWidth: true
|
||||
titleText: Utils.capitalizeFirstLetter(modelData.core.mimeType)
|
||||
subTitleText: modelData.core.recvFmtp
|
||||
subTitleText: modelData.core.encoderDescription
|
||||
propertyName: "enabled"
|
||||
propertyOwner: modelData.core
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: PayloadTypeProxy {
|
||||
id: downloadableVideoPayloadTypeProxy
|
||||
family: PayloadTypeCore.Video
|
||||
downloadable: true
|
||||
}
|
||||
SwitchSetting {
|
||||
Layout.fillWidth: true
|
||||
titleText: Utils.capitalizeFirstLetter(modelData.core.mimeType)
|
||||
subTitleText: modelData.core.encoderDescription
|
||||
onCheckChanged: function(checked) {
|
||||
if (checked)
|
||||
UtilsCpp.getMainWindow().showConfirmationLambdaPopup("",
|
||||
qsTr("Installation"),
|
||||
qsTr("Télécharger le codec ") + Utils.capitalizeFirstLetter(modelData.core.mimeType) + " ("+modelData.core.encoderDescription+")"+" ?",
|
||||
function (confirmed) {
|
||||
if (confirmed) {
|
||||
UtilsCpp.getMainWindow().showLoadingPopup(qsTr("Téléchargement en cours ..."))
|
||||
modelData.core.downloadAndExtract()
|
||||
} else
|
||||
setChecked(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import QtQuick.Controls.Basic
|
|||
import Linphone
|
||||
import UtilsCpp
|
||||
import SettingsCpp
|
||||
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils
|
||||
|
||||
AbstractWindow {
|
||||
id: mainWindow
|
||||
|
|
@ -140,6 +141,7 @@ AbstractWindow {
|
|||
onGoToRegister: mainWindowStackView.replace(registerPage)
|
||||
onConnectionSucceed: {
|
||||
openMainPage()
|
||||
proposeH264CodecsDownload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +158,7 @@ AbstractWindow {
|
|||
|
||||
onConnectionSucceed: {
|
||||
openMainPage()
|
||||
proposeH264CodecsDownload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,4 +228,25 @@ AbstractWindow {
|
|||
// StackView.onActivated: connectionSecured(0) // TODO : connect to cpp part when ready
|
||||
}
|
||||
}
|
||||
|
||||
// H264 Cisco codec download
|
||||
PayloadTypeProxy {
|
||||
id: downloadableVideoPayloadTypeProxy
|
||||
family: PayloadTypeCore.Video
|
||||
downloadable: true
|
||||
}
|
||||
Repeater {
|
||||
id: codecDownloader
|
||||
model: null
|
||||
Item {
|
||||
Component.onCompleted: {
|
||||
if (modelData.core.mimeType == "H264")
|
||||
Utils.openCodecOnlineInstallerDialog(mainWindow, modelData.core)
|
||||
}
|
||||
}
|
||||
}
|
||||
function proposeH264CodecsDownload() {
|
||||
codecDownloader.model = downloadableVideoPayloadTypeProxy
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue