diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdf56606..def5e0c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,7 +19,7 @@ set(SRCS_base audiomanager.cpp powermanagementinterface.cpp errorlogmodel.cpp - error.h + error.cpp mpris2/mpris2.cpp resources.qrc ) diff --git a/src/database.cpp b/src/database.cpp index edace8bd..798ff277 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -39,8 +39,11 @@ Database::Database() bool Database::migrate() { - if (version() < 1) + int dbversion = version(); + if (dbversion < 1) TRUE_OR_RETURN(migrateTo1()); + if (dbversion < 2) + TRUE_OR_RETURN(migrateTo2()); return true; } @@ -56,13 +59,22 @@ bool Database::migrateTo1() TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (feed TEXT, id TEXT, name TEXT, uri TEXT, email TEXT);"))); TRUE_OR_RETURN( execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Enclosures (feed TEXT, id TEXT, duration INTEGER, size INTEGER, title TEXT, type TEXT, url TEXT, " - "playposition INTEGER, downloaded BOOL);"))); //, filename TEXT);"))); + "playposition INTEGER, downloaded BOOL);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Queue (listnr INTEGER, feed TEXT, id TEXT, playing BOOL);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Errors (url TEXT, id TEXT, code INTEGER, message TEXT, date INTEGER);"))); TRUE_OR_RETURN(execute(QStringLiteral("PRAGMA user_version = 1;"))); return true; } +bool Database::migrateTo2() +{ + qDebug() << "Migrating database to version 2"; + TRUE_OR_RETURN(execute(QStringLiteral("DROP TABLE Errors;"))); + TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Errors (type INTEGER, url TEXT, id TEXT, code INTEGER, message TEXT, date INTEGER);"))); + TRUE_OR_RETURN(execute(QStringLiteral("PRAGMA user_version = 2;"))); + return true; +} + bool Database::execute(const QString &query) { QSqlQuery q; diff --git a/src/database.h b/src/database.h index 67fbc37f..2ddf4213 100644 --- a/src/database.h +++ b/src/database.h @@ -28,5 +28,6 @@ private: bool migrate(); bool migrateTo1(); + bool migrateTo2(); void cleanup(); }; diff --git a/src/datamanager.cpp b/src/datamanager.cpp index e83db2d8..a7ad9058 100644 --- a/src/datamanager.cpp +++ b/src/datamanager.cpp @@ -5,9 +5,12 @@ */ #include "datamanager.h" + #include "audiomanager.h" #include "database.h" #include "datamanagerlogging.h" +#include "entry.h" +#include "feed.h" #include "fetcher.h" #include "settingsmanager.h" #include @@ -574,4 +577,4 @@ void DataManager::updateQueueListnrs() const query.bindValue(QStringLiteral(":id"), m_queuemap[i]); Database::instance().execute(query); } -} +} \ No newline at end of file diff --git a/src/datamanager.h b/src/datamanager.h index dd464f9c..a8ff4e09 100644 --- a/src/datamanager.h +++ b/src/datamanager.h @@ -6,9 +6,10 @@ #pragma once -#include "entry.h" #include "episodemodel.h" -#include "feed.h" + +class Entry; +class Feed; class DataManager : public QObject { diff --git a/src/enclosure.cpp b/src/enclosure.cpp index 3f7e522c..ca6a685d 100644 --- a/src/enclosure.cpp +++ b/src/enclosure.cpp @@ -17,6 +17,7 @@ #include "downloadprogressmodel.h" #include "enclosuredownloadjob.h" #include "entry.h" +#include "error.h" #include "errorlogmodel.h" #include "fetcher.h" @@ -110,7 +111,7 @@ void Enclosure::download() if (downloadJob->error() != QNetworkReply::OperationCanceledError) { m_entry->feed()->setErrorId(downloadJob->error()); m_entry->feed()->setErrorString(downloadJob->errorString()); - Q_EMIT downloadError(m_entry->feed()->url(), m_entry->id(), downloadJob->error(), downloadJob->errorString()); + Q_EMIT downloadError(Error::Type::MediaDownload, m_entry->feed()->url(), m_entry->id(), downloadJob->error(), downloadJob->errorString()); } } disconnect(this, &Enclosure::cancelDownload, this, nullptr); diff --git a/src/enclosure.h b/src/enclosure.h index 1a1bccad..ae6e2489 100644 --- a/src/enclosure.h +++ b/src/enclosure.h @@ -13,6 +13,8 @@ #include +#include "error.h" + class Entry; class Enclosure : public QObject @@ -66,7 +68,7 @@ Q_SIGNALS: void playPositionChanged(); void durationChanged(); void sizeChanged(); - void downloadError(const QString &url, const QString &id, const int errorId, const QString &errorString); + void downloadError(const Error::Type type, const QString &url, const QString &id, const int errorId, const QString &errorString); private: void processDownloadedFile(); @@ -82,4 +84,4 @@ private: double m_downloadProgress = 0; Status m_status; KFormat m_kformat; -}; +}; \ No newline at end of file diff --git a/src/entriesmodel.cpp b/src/entriesmodel.cpp index 2543011f..272ba458 100644 --- a/src/entriesmodel.cpp +++ b/src/entriesmodel.cpp @@ -10,6 +10,7 @@ #include "datamanager.h" #include "entriesmodel.h" #include "entry.h" +#include "feed.h" EntriesModel::EntriesModel(Feed *feed) : QAbstractListModel(feed) diff --git a/src/entriesmodel.h b/src/entriesmodel.h index 3610a5f0..8131582f 100644 --- a/src/entriesmodel.h +++ b/src/entriesmodel.h @@ -12,7 +12,7 @@ #include #include -#include "feed.h" +class Feed; class EntriesModel : public QAbstractListModel { diff --git a/src/entry.cpp b/src/entry.cpp index db64a45e..f44973ac 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -13,6 +13,7 @@ #include "database.h" #include "datamanager.h" +#include "feed.h" #include "fetcher.h" Entry::Entry(Feed *feed, const QString &id) diff --git a/src/entry.h b/src/entry.h index 1f4f489f..56214015 100644 --- a/src/entry.h +++ b/src/entry.h @@ -16,7 +16,8 @@ #include "author.h" #include "enclosure.h" -#include "feed.h" + +class Feed; class Entry : public QObject { diff --git a/src/episodemodel.cpp b/src/episodemodel.cpp index e7e03437..79d73885 100644 --- a/src/episodemodel.cpp +++ b/src/episodemodel.cpp @@ -6,6 +6,7 @@ #include "episodemodel.h" #include "datamanager.h" +#include "entry.h" EpisodeModel::EpisodeModel() : QAbstractListModel(nullptr) diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 00000000..93ffd719 --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,81 @@ +/** + * SPDX-FileCopyrightText: 2021 Bart De Vries + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ + +#include +#include +#include +#include + +#include "datamanager.h" +#include "entry.h" +#include "error.h" +#include "feed.h" + +Error::Error(const Type type, const QString url, const QString id, const int code, const QString message, const QDateTime date) + : QObject(nullptr) +{ + this->type = type; + this->url = url; + this->id = id; + this->code = code; + this->message = message; + this->date = date; +}; + +QString Error::title() const +{ + QString title; + if (!id.isEmpty()) { + if (DataManager::instance().getEntry(id)) + title = DataManager::instance().getEntry(id)->title(); + } else if (!url.isEmpty()) { + if (DataManager::instance().getFeed(url)) + title = DataManager::instance().getFeed(url)->name(); + } + return title; +} + +QString Error::description() const +{ + switch (type) { + case Error::Type::FeedUpdate: + return i18n("Podcast Update Error"); + case Error::Type::MediaDownload: + return i18n("Media Download Error"); + case Error::Type::MeteredNotAllowed: + return i18n("Update Not Allowed on Metered Connection"); + default: + return QString(); + } +} + +int Error::typeToDb(Error::Type type) +{ + switch (type) { + case Error::Type::FeedUpdate: + return 0; + case Error::Type::MediaDownload: + return 1; + case Error::Type::MeteredNotAllowed: + return 2; + default: + return -1; + } +} + +Error::Type Error::dbToType(int value) +{ + switch (value) { + case 0: + return Error::Type::FeedUpdate; + case 1: + return Error::Type::MediaDownload; + case 2: + return Error::Type::MeteredNotAllowed; + default: + return Error::Type::Unknown; + } +} diff --git a/src/error.h b/src/error.h index ef1fd801..cbbb968c 100644 --- a/src/error.h +++ b/src/error.h @@ -6,7 +6,6 @@ #pragma once -#include "datamanager.h" #include #include #include @@ -15,40 +14,35 @@ class Error : public QObject { Q_OBJECT +public: + enum Type { + Unknown, + FeedUpdate, + MediaDownload, + MeteredNotAllowed, + }; + Q_ENUM(Type) + + static int typeToDb(Type type); // needed to translate Error::Type values to int for sqlite + static Type dbToType(int value); // needed to translate from int to Error::Type values for sqlite + Q_PROPERTY(QString url MEMBER url CONSTANT) Q_PROPERTY(QString id MEMBER id CONSTANT) Q_PROPERTY(int code MEMBER code CONSTANT) Q_PROPERTY(QString message MEMBER message CONSTANT) Q_PROPERTY(QDateTime date MEMBER date CONSTANT) Q_PROPERTY(QString title READ title CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) -public: - Error(const QString url, const QString id, const int code, const QString message, const QDateTime date) - : QObject(nullptr) - { - this->url = url; - this->id = id; - this->code = code; - this->message = message; - this->date = date; - }; + Error(Type type, const QString url, const QString id, const int code, const QString message, const QDateTime date); + QString title() const; + QString description() const; + + Type type; QString url; QString id; int code; QString message; QDateTime date; - - QString title() const - { - QString title; - if (!id.isEmpty()) { - if (DataManager::instance().getEntry(id)) - title = DataManager::instance().getEntry(id)->title(); - } else if (!url.isEmpty()) { - if (DataManager::instance().getFeed(url)) - title = DataManager::instance().getFeed(url)->name(); - } - return title; - } }; diff --git a/src/errorlogmodel.cpp b/src/errorlogmodel.cpp index 3e844bab..7714a410 100644 --- a/src/errorlogmodel.cpp +++ b/src/errorlogmodel.cpp @@ -21,11 +21,10 @@ ErrorLogModel::ErrorLogModel() query.prepare(QStringLiteral("SELECT * FROM Errors ORDER BY date DESC;")); Database::instance().execute(query); while (query.next()) { - QString id = query.value(QStringLiteral("id")).toString(); - QString url = query.value(QStringLiteral("url")).toString(); + Error *error = new Error(Error::dbToType(query.value(QStringLiteral("type")).toInt()), + query.value(QStringLiteral("url")).toString(), + query.value(QStringLiteral("id")).toString(), - Error *error = new Error(url, - id, query.value(QStringLiteral("code")).toInt(), query.value(QStringLiteral("message")).toString(), QDateTime::fromSecsSinceEpoch(query.value(QStringLiteral("date")).toInt())); @@ -54,19 +53,20 @@ int ErrorLogModel::rowCount(const QModelIndex &parent) const return m_errors.count(); } -void ErrorLogModel::monitorErrorMessages(const QString &url, const QString &id, const int errorCode, const QString &errorString) +void ErrorLogModel::monitorErrorMessages(const Error::Type type, const QString &url, const QString &id, const int errorCode, const QString &errorString) { - qDebug() << "Error happened:" << url << id << errorCode << errorString; + qDebug() << "Error happened:" << type << url << id << errorCode << errorString; QString title; - Error *error = new Error(url, id, errorCode, errorString, QDateTime::currentDateTime()); + Error *error = new Error(type, url, id, errorCode, errorString, QDateTime::currentDateTime()); beginInsertRows(QModelIndex(), 0, 0); m_errors.prepend(error); endInsertRows(); // Also add error to database QSqlQuery query; - query.prepare(QStringLiteral("INSERT INTO Errors VALUES (:url, :id, :code, :message, :date);")); + query.prepare(QStringLiteral("INSERT INTO Errors VALUES (:type, :url, :id, :code, :message, :date);")); + query.bindValue(QStringLiteral(":type"), Error::typeToDb(error->type)); query.bindValue(QStringLiteral(":url"), error->url); query.bindValue(QStringLiteral(":id"), error->id); query.bindValue(QStringLiteral(":code"), error->code); @@ -91,4 +91,4 @@ void ErrorLogModel::clearAll() QSqlQuery query; query.prepare(QStringLiteral("DELETE FROM Errors;")); Database::instance().execute(query); -} +} \ No newline at end of file diff --git a/src/errorlogmodel.h b/src/errorlogmodel.h index 7b774921..5d6bf6fc 100644 --- a/src/errorlogmodel.h +++ b/src/errorlogmodel.h @@ -32,7 +32,7 @@ public: Q_INVOKABLE void clearAll(); public: - void monitorErrorMessages(const QString &url, const QString &id, const int errorCode, const QString &errorString); + void monitorErrorMessages(const Error::Type type, const QString &url, const QString &id, const int errorCode, const QString &errorString); Q_SIGNALS: void newErrorLogged(Error *error); diff --git a/src/feed.cpp b/src/feed.cpp index 2bba3b4d..b4e45bf7 100644 --- a/src/feed.cpp +++ b/src/feed.cpp @@ -7,9 +7,11 @@ #include +#include "author.h" #include "database.h" #include "datamanager.h" #include "entriesmodel.h" +#include "error.h" #include "feed.h" #include "feedlogging.h" #include "fetcher.h" @@ -57,14 +59,18 @@ Feed::Feed(const QString &feedurl) setErrorString(QLatin1String("")); } }); - connect(&Fetcher::instance(), &Fetcher::error, this, [this](const QString &url, const QString &id, int errorId, const QString &errorString) { - Q_UNUSED(id) - if (url == m_url) { - setErrorId(errorId); - setErrorString(errorString); - setRefreshing(false); - } - }); + connect(&Fetcher::instance(), + &Fetcher::error, + this, + [this](const Error::Type type, const QString &url, const QString &id, int errorId, const QString &errorString) { + Q_UNUSED(type) + Q_UNUSED(id) + if (url == m_url) { + setErrorId(errorId); + setErrorString(errorString); + setRefreshing(false); + } + }); connect(&Fetcher::instance(), &Fetcher::feedUpdateFinished, this, [this](const QString &url) { if (url == m_url) { setRefreshing(false); diff --git a/src/feed.h b/src/feed.h index 26f73448..4dcb1541 100644 --- a/src/feed.h +++ b/src/feed.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include "author.h" diff --git a/src/fetcher.cpp b/src/fetcher.cpp index 72bf0669..5dc24a07 100644 --- a/src/fetcher.cpp +++ b/src/fetcher.cpp @@ -90,7 +90,7 @@ void Fetcher::retrieveFeed(const QString &url) if (reply->error()) { qWarning() << "Error fetching feed"; qWarning() << reply->errorString(); - Q_EMIT error(url, QString(), reply->error(), reply->errorString()); + Q_EMIT error(Error::Type::FeedUpdate, url, QString(), reply->error(), reply->errorString()); } else { QByteArray data = reply->readAll(); Syndication::DocumentSource *document = new Syndication::DocumentSource(data, url); @@ -452,4 +452,4 @@ QNetworkReply *Fetcher::head(QNetworkRequest &request) const void Fetcher::setHeader(QNetworkRequest &request) const { request.setRawHeader("User-Agent", "Kasts/0.1; Syndication"); -} +} \ No newline at end of file diff --git a/src/fetcher.h b/src/fetcher.h index 03dce0d3..a4c1832e 100644 --- a/src/fetcher.h +++ b/src/fetcher.h @@ -14,6 +14,8 @@ #include #include +#include "error.h" + class Fetcher : public QObject { Q_OBJECT @@ -35,6 +37,7 @@ public: Q_INVOKABLE QString image(const QString &url) const; void removeImage(const QString &url); Q_INVOKABLE QNetworkReply *download(const QString &url, const QString &fileName) const; + QString imagePath(const QString &url) const; QString enclosurePath(const QString &url) const; @@ -48,7 +51,7 @@ Q_SIGNALS: const QString &description, const QDateTime &lastUpdated); void feedUpdateFinished(const QString &url); - void error(const QString &url, const QString &id, const int errorId, const QString &errorString); + void error(Error::Type type, const QString &url, const QString &id, const int errorId, const QString &errorString); void entryAdded(const QString &feedurl, const QString &id); void downloadFinished(QString url) const; void downloadFileSizeUpdated(QString url, int fileSize) const; @@ -77,4 +80,4 @@ private: int m_updateProgress; int m_updateTotal; bool m_updating; -}; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3f3e938c..30285674 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include + #include #include #include @@ -35,8 +36,10 @@ #include "datamanager.h" #include "downloadprogressmodel.h" #include "entriesmodel.h" +#include "entry.h" #include "episodemodel.h" #include "errorlogmodel.h" +#include "feed.h" #include "feedsmodel.h" #include "fetcher.h" #include "kasts-version.h" @@ -129,6 +132,7 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("org.kde.kasts", 1, 0, "AudioManager", &AudioManager::instance()); qRegisterMetaType("const Entry*"); // "hack" to make qml understand Entry* + qRegisterMetaType("const Feed*"); // "hack" to make qml understand Feed* // Make sure that settings are saved before the application exits QObject::connect(&app, &QCoreApplication::aboutToQuit, SettingsManager::self(), &SettingsManager::save); diff --git a/src/qml/ErrorListPage.qml b/src/qml/ErrorListPage.qml index 5c21d721..268db1c6 100644 --- a/src/qml/ErrorListPage.qml +++ b/src/qml/ErrorListPage.qml @@ -8,6 +8,7 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 as Controls import QtQuick.Layouts 1.14 import QtGraphicalEffects 1.15 + import org.kde.kirigami 2.15 as Kirigami import org.kde.kasts 1.0 @@ -40,7 +41,7 @@ Kirigami.ScrollablePage { Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter Controls.Label { - text: ( (error.id) ? i18n("Media Download Error") : i18n("Podcast Update Error") ) + " · " + error.date.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + " · " + error.date.toLocaleTimeString(Qt.locale(), Locale.NarrowFormat) + text: error.description + " · " + error.date.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + " · " + error.date.toLocaleTimeString(Qt.locale(), Locale.NarrowFormat) Layout.fillWidth: true elide: Text.ElideRight font: Kirigami.Theme.smallFont @@ -54,7 +55,7 @@ Kirigami.ScrollablePage { opacity: 1 } Controls.Label { - text: i18n("Error Code: ") + error.code + (error.message ? " · " + error.message : "") + text: i18n("Error Code: ") + error.code + (error.message ? " · " + error.message : "") Layout.fillWidth: true elide: Text.ElideRight font: Kirigami.Theme.smallFont @@ -82,4 +83,4 @@ Kirigami.ScrollablePage { visible: errorList.count > 0 onTriggered: ErrorLogModel.clearAll() } -} +} \ No newline at end of file diff --git a/src/qml/main.qml b/src/qml/main.qml index 452914f7..b5731ed8 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -203,7 +203,7 @@ Kirigami.ApplicationWindow { anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom - bottomMargin: bottomMessageSpacing + bottomMargin: bottomMessageSpacing + ( inlineMessage.visible ? inlineMessage.height + Kirigami.Units.largeSpacing : 0 ) } } @@ -214,7 +214,7 @@ Kirigami.ApplicationWindow { right: parent.right left: parent.left margins: Kirigami.Units.gridUnit - bottomMargin: bottomMessageSpacing + ( updateNotification.visible ? updateNotification.height + Kirigami.Units.largeSpacing : 0 ) + bottomMargin: bottomMessageSpacing } type: Kirigami.MessageType.Error showCloseButton: true @@ -222,7 +222,7 @@ Kirigami.ApplicationWindow { Connections { target: ErrorLogModel function onNewErrorLogged(error) { - inlineMessage.text = error.id ? i18n("Media Download Error") : i18n("Podcast Update Error") + "\n" + i18n("Check Error Log Tab (under Downloads) for more details."); + inlineMessage.text = error.description + "\n" + i18n("Check Error Log Tab (under Downloads) for more details"); inlineMessage.visible = true; } }