Commit 23939aaa authored by Ronan's avatar Ronan

feat(core): supports H264 download

Co-authored-by: DanmeiChen's avatarDanmei Chen <danmei.chen@belledonne-communications.com>
parent 7c24e07f
......@@ -145,6 +145,7 @@ set(SOURCES
src/components/core/CoreHandlers.cpp
src/components/core/CoreManager.cpp
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.cpp
src/components/file/FileDownloader.cpp
src/components/file/FileExtractor.cpp
src/components/notifier/Notifier.cpp
src/components/other/clipboard/Clipboard.cpp
......@@ -163,8 +164,8 @@ set(SOURCES
src/components/timeline/TimelineModel.cpp
src/components/url-handlers/UrlHandlers.cpp
src/utils/LinphoneUtils.cpp
src/utils/Utils.cpp
src/utils/QExifImageHeader.cpp
src/utils/Utils.cpp
)
set(HEADERS
......@@ -202,6 +203,7 @@ set(HEADERS
src/components/core/CoreHandlers.hpp
src/components/core/CoreManager.hpp
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.hpp
src/components/file/FileDownloader.hpp
src/components/file/FileExtractor.hpp
src/components/notifier/Notifier.hpp
src/components/other/clipboard/Clipboard.hpp
......
......@@ -1003,6 +1003,29 @@ your friend&apos;s SIP address or username.</translation>
<translation>New attachment received!</translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation>CONFIRM</translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation>Extracting %1...</translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation>Downloading %1...</translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation>%1 is now installed!</translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation>Failed to install %1!</translation>
</message>
</context>
<context>
<name>OutgoingMessage</name>
<message>
......@@ -1672,4 +1695,11 @@ your friend&apos;s SIP address or username.</translation>
<translation>CONFIRM</translation>
</message>
</context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation>Do you want to download %1 (%2)?</translation>
</message>
</context>
</TS>
......@@ -1001,6 +1001,29 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Pièce jointe reçue !</translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation>CONFIRM</translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation>Extraction de %1...</translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation>Téléchargement de %1...</translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation>Installation de %1 terminée !</translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation>L&apos;installation de %1 a échoué !</translation>
</message>
</context>
<context>
<name>OutgoingMessage</name>
<message>
......@@ -1670,4 +1693,11 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>CONFIRMER</translation>
</message>
</context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation>Voulez-vous installer %1 (%2) ?</translation>
</message>
</context>
</TS>
......@@ -1001,6 +1001,29 @@
<translation>Получен новый файл!</translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OutgoingMessage</name>
<message>
......@@ -1670,4 +1693,11 @@
<translation>ПОДТВЕРДИТЬ</translation>
</message>
</context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
......@@ -1003,6 +1003,29 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
<translation>Yeni ek alındı!</translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OutgoingMessage</name>
<message>
......@@ -1672,4 +1695,11 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
<translation>ONAYLA</translation>
</message>
</context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
......@@ -332,6 +332,7 @@
<file>ui/modules/Linphone/Contact/ContactDescription.qml</file>
<file>ui/modules/Linphone/Contact/Contact.qml</file>
<file>ui/modules/Linphone/Contact/MessagesCounter.qml</file>
<file>ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml</file>
<file>ui/modules/Linphone/Menus/SipAddressesMenu.qml</file>
<file>ui/modules/Linphone/Notifications/NotificationBasic.qml</file>
<file>ui/modules/Linphone/Notifications/NotificationNewVersionAvailable.qml</file>
......@@ -357,6 +358,7 @@
<file>ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/ContactStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/MessagesCounterStyle.qml</file>
<file>ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml</file>
<file>ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml</file>
<file>ui/modules/Linphone/Styles/Notifications/NotificationBasicStyle.qml</file>
<file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml</file>
......
......@@ -176,10 +176,6 @@ void App::initContentApp () {
Cli::executeCommand(command);
});
// Add plugins directory.
addLibraryPath(::Utils::coreStringToAppString(Paths::getPluginsDirPath()));
qInfo() << QStringLiteral("Library paths:") << libraryPaths();
mustBeIconified = mParser->isSet("iconified");
}
......@@ -405,6 +401,7 @@ void App::registerTypes () {
registerType<ConferenceHelperModel>("ConferenceHelperModel");
registerType<ConferenceModel>("ConferenceModel");
registerType<ContactsListProxyModel>("ContactsListProxyModel");
registerType<FileDownloader>("FileDownloader");
registerType<FileExtractor>("FileExtractor");
registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
registerType<SoundPlayer>("SoundPlayer");
......
......@@ -159,7 +159,7 @@ void Logger::log (QtMsgType type, const QMessageLogContext &context, const QStri
contextStr = contextArr.constData();
}
#else
(void)context;
Q_UNUSED(context);
#endif // ifdef QT_MESSAGELOGCONTEXT
QByteArray localMsg = msg.toLocal8Bit();
......
......@@ -40,6 +40,7 @@ namespace {
constexpr char cPathAssistantConfig[] = "/linphone/assistant/";
constexpr char cPathAvatars[] = "/avatars/";
constexpr char cPathCaptures[] = "/Linphone/captures/";
constexpr char cPathCodecs[] = "/codecs/";
constexpr char cPathLogs[] = "/logs/";
constexpr char cPathPlugins[] = "/plugins/";
constexpr char cPathThumbnails[] = "/thumbnails/";
......@@ -181,6 +182,10 @@ string Paths::getCapturesDirPath () {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + cPathCaptures);
}
string Paths::getCodecsDirPath () {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + cPathCodecs);
}
string Paths::getConfigFilePath (const QString &configPath, bool writable) {
const QString path = configPath.isEmpty()
? getAppConfigFilePath()
......@@ -217,10 +222,6 @@ string Paths::getPackageMsPluginsDirPath () {
return getReadableDirPath(getAppPackageMsPluginsDirPath());
}
string Paths::getPluginsDirPath () {
return getReadableDirPath(getAppPluginsDirPath());
}
string Paths::getRootCaFilePath () {
return getReadableFilePath(getAppRootCaFilePath());
}
......
......@@ -34,15 +34,15 @@ namespace Paths {
std::string getAvatarsDirPath ();
std::string getCallHistoryFilePath ();
std::string getCapturesDirPath ();
std::string getCodecsDirPath ();
std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true);
std::string getDownloadDirPath ();
std::string getFactoryConfigFilePath ();
std::string getFriendsListFilePath ();
std::string getDownloadDirPath ();
std::string getLogsDirPath ();
std::string getMessageHistoryFilePath ();
std::string getPackageDataDirPath ();
std::string getPackageMsPluginsDirPath ();
std::string getPluginsDirPath ();
std::string getRootCaFilePath ();
std::string getThumbnailsDirPath ();
std::string getUserCertificatesDirPath ();
......
......@@ -36,6 +36,7 @@
#include "conference/ConferenceModel.hpp"
#include "contacts/ContactsListProxyModel.hpp"
#include "core/CoreManager.hpp"
#include "file/FileDownloader.hpp"
#include "file/FileExtractor.hpp"
#include "presence/OwnPresenceModel.hpp"
#include "settings/AccountSettingsModel.hpp"
......
......@@ -20,17 +20,18 @@
* Author: Ronan Abhamon
*/
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp"
#include "AbstractCodecsModel.hpp"
using namespace std;
// =============================================================================
using namespace std;
static inline shared_ptr<linphone::PayloadType> getCodecFromMap (const QVariantMap &map) {
return map.value("__codec").value<shared_ptr<linphone::PayloadType> >();
return map.value("__codec").value<shared_ptr<linphone::PayloadType>>();
}
// -----------------------------------------------------------------------------
......@@ -65,12 +66,12 @@ void AbstractCodecsModel::enableCodec (int id, bool status) {
Q_ASSERT(id >= 0 && id < mCodecs.count());
QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
codec->enable(status);
map["enabled"] = codec->enabled();
emit dataChanged(index(id, 0), index(id, 0));
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec) {
codec->enable(status);
map["enabled"] = codec->enabled();
emit dataChanged(index(id, 0), index(id, 0));
}
}
void AbstractCodecsModel::moveCodec (int source, int destination) {
......@@ -81,12 +82,12 @@ void AbstractCodecsModel::setBitrate (int id, int bitrate) {
Q_ASSERT(id >= 0 && id < mCodecs.count());
QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
codec->setNormalBitrate(bitrate);
map["bitrate"] = codec->getNormalBitrate();
emit dataChanged(index(id, 0), index(id, 0));
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec) {
codec->setNormalBitrate(bitrate);
map["bitrate"] = codec->getNormalBitrate();
emit dataChanged(index(id, 0), index(id, 0));
}
}
void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
......@@ -94,11 +95,11 @@ void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
codec->setRecvFmtp(::Utils::appStringToCoreString(recvFmtp));
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp());
emit dataChanged(index(id, 0), index(id, 0));
if (codec) {
codec->setRecvFmtp(Utils::appStringToCoreString(recvFmtp));
map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
emit dataChanged(index(id, 0), index(id, 0));
}
}
// -----------------------------------------------------------------------------
......@@ -110,6 +111,8 @@ bool AbstractCodecsModel::moveRows (
const QModelIndex &destinationParent,
int destinationChild
) {
// TODO: Do not move downloadable codecs.
int limit = sourceRow + count - 1;
{
......@@ -139,9 +142,13 @@ bool AbstractCodecsModel::moveRows (
}
// Update linphone codecs list.
list<shared_ptr<linphone::PayloadType> > codecs;
for (const auto &map : mCodecs)
codecs.push_back(::getCodecFromMap(map));
list<shared_ptr<linphone::PayloadType>> codecs;
for (const auto &map : mCodecs) {
// Do not update downloadable codecs.
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec)
codecs.push_back(codec);
}
updateCodecs(codecs);
endMoveRows();
......@@ -157,15 +164,40 @@ void AbstractCodecsModel::addCodec (shared_ptr<linphone::PayloadType> &codec) {
map["bitrate"] = codec->getNormalBitrate();
map["channels"] = codec->getChannels();
map["clockRate"] = codec->getClockRate();
map["description"] = ::Utils::coreStringToAppString(codec->getDescription());
map["description"] = Utils::coreStringToAppString(codec->getDescription());
map["enabled"] = codec->enabled();
map["encoderDescription"] = ::Utils::coreStringToAppString(codec->getEncoderDescription());
map["encoderDescription"] = Utils::coreStringToAppString(codec->getEncoderDescription());
map["isUsable"] = codec->isUsable(); // TODO: Notify in UI when unusable.
map["isVbr"] = codec->isVbr();
map["mime"] = ::Utils::coreStringToAppString(codec->getMimeType());
map["mime"] = Utils::coreStringToAppString(codec->getMimeType());
map["number"] = codec->getNumber();
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp());
map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
map["__codec"] = QVariant::fromValue(codec);
mCodecs << map;
}
void AbstractCodecsModel::addDownloadableCodec (
const QString &mime,
const QString &downloadUrl,
const QString &encoderDescription
) {
QVariantMap map;
map["mime"] = mime;
map["downloadUrl"] = downloadUrl;
map["encoderDescription"] = encoderDescription;
mCodecs << map;
}
QVariantMap AbstractCodecsModel::getCodecInfo (const QString &mime) const {
for (const auto &codec : mCodecs)
if (codec.value("mime") == mime)
return codec;
return QVariantMap();
};
QString AbstractCodecsModel::getCodecsFolder () const {
return Utils::coreStringToAppString(Paths::getCodecsDirPath());
}
......@@ -36,6 +36,8 @@ namespace linphone {
class AbstractCodecsModel : public QAbstractListModel {
Q_OBJECT;
Q_PROPERTY(QString codecsFolder READ getCodecsFolder CONSTANT);
public:
AbstractCodecsModel (QObject *parent = Q_NULLPTR);
virtual ~AbstractCodecsModel () = default;
......@@ -51,6 +53,10 @@ public:
Q_INVOKABLE void setBitrate (int id, int bitrate);
Q_INVOKABLE void setRecvFmtp (int id, const QString &recvFmtp);
Q_INVOKABLE virtual void reload () {};
Q_INVOKABLE QVariantMap getCodecInfo (const QString &mime) const;
protected:
bool moveRows (
const QModelIndex &sourceParent,
......@@ -61,10 +67,12 @@ protected:
) override;
void addCodec (std::shared_ptr<linphone::PayloadType> &codec);
void addDownloadableCodec (const QString &mime, const QString &downloadUrl, const QString &encoderDescription);
QString getCodecsFolder () const;
virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) = 0;
virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) = 0;
private:
QList<QVariantMap> mCodecs;
};
......
......@@ -24,15 +24,15 @@
#include "AudioCodecsModel.hpp"
using namespace std;
// =============================================================================
using namespace std;
AudioCodecsModel::AudioCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
for (auto &codec : CoreManager::getInstance()->getCore()->getAudioPayloadTypes())
addCodec(codec);
}
void AudioCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType> > &codecs) {
void AudioCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType>> &codecs) {
CoreManager::getInstance()->getCore()->setAudioPayloadTypes(codecs);
}
......@@ -34,8 +34,8 @@ public:
AudioCodecsModel (QObject *parent = Q_NULLPTR);
~AudioCodecsModel () = default;
protected:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override;
private:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
};
#endif // AUDIO_CODECS_MODEL_H_
......@@ -20,19 +20,79 @@
* Author: Ronan Abhamon
*/
#include <QDirIterator>
#include <QLibrary>
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp"
#include "VideoCodecsModel.hpp"
// =============================================================================
using namespace std;
// =============================================================================
namespace {
constexpr char cH264Description[] = "Provided by CISCO SYSTEM,INC";
#ifdef Q_OS_LINUX
constexpr char cLibraryExtension[] = "so";
#ifdef Q_PROCESSOR_X86_64
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-1.7.0-linux64.4.so.bz2";
#else
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-1.7.0-linux32.4.so.bz2";
#endif // ifdef Q_PROCESSOR_X86_64
#elif defined(Q_OS_WIN)
constexpr char cLibraryExtension[] = "dll";
#ifdef Q_OS_WIN64
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-1.7.0-win64.dll.bz2";
#elif defined(Q_OS_WIN32)
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-1.7.0-win32.dll.bz2";
#endif // ifdef Q_OS_WIN64
#endif // ifdef Q_OS_LINUX
}
VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
for (auto &codec : CoreManager::getInstance()->getCore()->getVideoPayloadTypes())
addCodec(codec);
load();
}
void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType> > &codecs) {
void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType>> &codecs) {
CoreManager::getInstance()->getCore()->setVideoPayloadTypes(codecs);
}
void VideoCodecsModel::load () {
mCodecs.clear();
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
// Load downloaded codecs like OpenH264.
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
QDirIterator it(Utils::coreStringToAppString(Paths::getCodecsDirPath()));
while (it.hasNext()) {
QFileInfo info(it.next());
if (info.suffix() == cLibraryExtension)
QLibrary(info.filePath()).load();
}
core->reloadMsPlugins("");
#endif
// Add codecs.
auto codecs = core->getVideoPayloadTypes();
for (auto &codec : codecs)
addCodec(codec);
// Add downloadable codecs.
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
if (find_if(codecs.begin(), codecs.end(), [](const shared_ptr<linphone::PayloadType> &codec) {
return codec->getMimeType() == "H264";
}) == codecs.end())
addDownloadableCodec("H264", cPluginUrlH264, cH264Description);
#endif
}
void VideoCodecsModel::reload () {
beginResetModel();
load();
endResetModel();
}
......@@ -34,8 +34,11 @@ public:
VideoCodecsModel (QObject *parent = Q_NULLPTR);
~VideoCodecsModel () = default;
protected:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override;
private:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
void load ();
void reload () override;
};
#endif // VIDEO_CODECS_MODEL_H_
......@@ -30,5 +30,5 @@ MessagesCountNotifier::MessagesCountNotifier (QObject *parent) : AbstractMessage
void MessagesCountNotifier::notifyUnreadMessagesCount (int n) {
// TODO.
(void)n;
Q_UNUSED(n);
}
/*
* FileDownloader.cpp
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: February 6, 2018
* Author: Danmei Chen
*/
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp"
#include "FileDownloader.hpp"
// =============================================================================
namespace {
constexpr char cDefaultFileName[] = "download";
}
static QString getDownloadFilePath (const QString &folder, const QUrl &url) {
QFileInfo fileInfo(url.path());
QString fileName = fileInfo.fileName();
if (fileName.isEmpty())
fileName = cDefaultFileName;
fileName.prepend(folder);
if (!QFile::exists(fileName))
return fileName;
// Already exists, don't overwrite.
QString baseName = fileInfo.completeBaseName();
if (baseName.isEmpty())
baseName = cDefaultFileName;
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);
#if QT_CONFIG(ssl)
QObject::connect(mNetworkReply, &QNetworkReply::sslErrors, this, &FileDownloader::handleSslErrors);
#endif
QObject::connect(mNetworkReply, &QNetworkReply::downloadProgress, this, &FileDownloader::handleDownloadProgress);
QObject::connect(mNetworkReply, &QNetworkReply::readyRead, this, &FileDownloader::handleReadyData);
QObject::connect(mNetworkReply, &QNetworkReply::finished, this, &FileDownloader::handleDownloadFinished);
if (mDownloadFolder.isEmpty()) {