[multimedia/kasts] src: Show an indicator on the delegate when episode is no longer in RSS feed
Bart De Vries <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 3b3710d12b428ef5cfa3e21313c3622a41a112bd by Bart De Vries.
Committed on 30/07/2026 at 17:52.
Pushed by bdevries into branch 'master'.
Show an indicator on the delegate when episode is no longer in RSS feed
M +1 -0 src/CMakeLists.txt
M +2 -0 src/datatypes.h
M +9 -0 src/entry.cpp
M +4 -0 src/entry.h
M +21 -2 src/qml/GenericEntryDelegate.qml
M +17 -0 src/utils/updatefeedjob.cpp
https://invent.kde.org/multimedia/kasts/-/commit/3b3710d12b428ef5cfa3e21313c3622a41a112bd
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 66f1f06e..c2f3bf20 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -400,6 +400,7 @@ if(ANDROID)
view-sort-descending-name
view-sort-ascending-name
kde-symbolic
+ emblem-unmounted
)
ecm_add_android_apk(kasts ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
else()
diff --git a/src/datatypes.h b/src/datatypes.h
index 3e24e35e..58776a66 100644
--- a/src/datatypes.h
+++ b/src/datatypes.h
@@ -78,6 +78,7 @@ struct EntryDetails {
bool read;
bool isNew;
bool favorite;
+ bool removed;
bool hasEnclosure;
QString image;
QHash<QString, AuthorDetails> authors; // key = author name
@@ -91,6 +92,7 @@ struct EntryDetails {
int oldCreated;
int oldUpdated;
QString oldLink;
+ bool oldRemoved;
bool oldHasEnclosure; // TODO: probably don't need this since there is the enclosure QHash anyway
QString oldImage;
};
diff --git a/src/entry.cpp b/src/entry.cpp
index b885561f..91d9784e 100644
--- a/src/entry.cpp
+++ b/src/entry.cpp
@@ -106,6 +106,10 @@ void Entry::updateFromDb(bool emitSignals)
m_favorite = entryQuery.value(QStringLiteral("favorite")).toBool();
Q_EMIT favoriteChanged(m_favorite);
}
+ if (m_removed != entryQuery.value(QStringLiteral("removed")).toBool()) {
+ m_removed = entryQuery.value(QStringLiteral("removed")).toBool();
+ Q_EMIT removedChanged(m_removed);
+ }
setHasEnclosure(entryQuery.value(QStringLiteral("hasEnclosure")).toBool(), emitSignals);
setImage(entryQuery.value(QStringLiteral("image")).toString(), emitSignals);
@@ -201,6 +205,11 @@ bool Entry::favorite() const
return m_favorite;
}
+bool Entry::removed() const
+{
+ return m_removed;
+}
+
QString Entry::baseUrl() const
{
return QUrl(m_link).adjusted(QUrl::RemovePath).toString();
diff --git a/src/entry.h b/src/entry.h
index d286bdf5..84337001 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -36,6 +36,7 @@ class Entry : public QObject
Q_PROPERTY(bool read READ read WRITE setRead NOTIFY readChanged)
Q_PROPERTY(bool new READ getNew WRITE setNew NOTIFY newChanged)
Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
+ Q_PROPERTY(bool removed READ removed NOTIFY removedChanged)
Q_PROPERTY(Enclosure *enclosure READ enclosure CONSTANT)
Q_PROPERTY(bool hasEnclosure READ hasEnclosure NOTIFY hasEnclosureChanged)
Q_PROPERTY(QString image READ image NOTIFY imageChanged)
@@ -58,6 +59,7 @@ public:
bool read() const;
bool getNew() const;
bool favorite() const;
+ bool removed() const;
Enclosure *enclosure() const;
bool hasEnclosure() const;
QString image() const;
@@ -85,6 +87,7 @@ Q_SIGNALS:
void readChanged(bool read);
void newChanged(bool state);
void favoriteChanged(bool favorite);
+ void removedChanged(bool removed);
void hasEnclosureChanged(bool hasEnclosure);
void imageChanged(const QString &url);
void cachedImageChanged(const QString &imagePath);
@@ -114,6 +117,7 @@ private:
bool m_read = false;
bool m_new = false;
bool m_favorite = false;
+ bool m_removed = false;
Enclosure *m_enclosure = nullptr;
QString m_image;
bool m_hasenclosure = false;
diff --git a/src/qml/GenericEntryDelegate.qml b/src/qml/GenericEntryDelegate.qml
index 56c6376c..01dd18de 100644
--- a/src/qml/GenericEntryDelegate.qml
+++ b/src/qml/GenericEntryDelegate.qml
@@ -249,12 +249,31 @@ AddonDelegates.RoundedItemDelegate {
}
Controls.Label {
id: supertitle
- text: ((!root.isQueue && root.entry.queueStatus) || root.entry.favorite ? "· " : "") + root.entry.updated.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + (root.entry.enclosure ? (root.entry.enclosure.size !== 0 ? " · " + root.entry.enclosure.formattedSize : "") : "") + ((root.entry.feed && root.showFeedTitle) ? " · " + root.entry.feed.name : "")
- Layout.fillWidth: true
+ text: (((!root.isQueue && root.entry.queueStatus) || root.entry.favorite) ? "· " : "") + root.entry.updated.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + (root.entry.enclosure ? (root.entry.enclosure.size !== 0 ? " · " + root.entry.enclosure.formattedSize : "") : "") + ((root.entry.feed && root.showFeedTitle) ? " · " + root.entry.feed.name : "") + (root.entry.removed ? " ·" : "")
elide: Text.ElideRight
font: Kirigami.Theme.smallFont
opacity: 0.7
}
+ Kirigami.Icon {
+ Layout.maximumHeight: 0.8 * supertitle.implicitHeight
+ Layout.maximumWidth: 0.8 * supertitle.implicitHeight
+ source: "emblem-unmounted"
+ visible: root.entry.removed
+ opacity: 0.7
+
+ HoverHandler {
+ id: removedHover
+ }
+
+ Controls.ToolTip.text: KI18n.i18nc("@info:tooltip Tooltip for an icon that can be displayed on an episode list item", "This episode has been removed from the podcast RSS feed by the podcast author(s). Downloading and/or streaming this episode might be broken.")
+ Controls.ToolTip.visible: removedHover.hovered
+ Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
+ }
+ Controls.Label {
+ text: ""
+ Layout.fillWidth: true
+ font: Kirigami.Theme.smallFont
+ }
}
Controls.Label {
text: root.entry.title
diff --git a/src/utils/updatefeedjob.cpp b/src/utils/updatefeedjob.cpp
index 10ad546c..00eb466d 100644
--- a/src/utils/updatefeedjob.cpp
+++ b/src/utils/updatefeedjob.cpp
@@ -216,6 +216,7 @@ void UpdateFeedJob::processFeed(const Syndication::FeedPtr feed, DataTypes::Feed
entryDetails.read = query.value(QStringLiteral("read")).toBool();
entryDetails.isNew = query.value(QStringLiteral("new")).toBool();
entryDetails.link = query.value(QStringLiteral("link")).toString();
+ entryDetails.removed = query.value(QStringLiteral("removed")).toBool();
entryDetails.hasEnclosure = query.value(QStringLiteral("hasEnclosure")).toBool();
entryDetails.image = query.value(QStringLiteral("image")).toString();
entryDetails.state = RecordState::Deleted; // will be set to appropriate value if the entry is found in the updated rss feed
@@ -226,6 +227,7 @@ void UpdateFeedJob::processFeed(const Syndication::FeedPtr feed, DataTypes::Feed
entryDetails.oldCreated = entryDetails.created;
entryDetails.oldUpdated = entryDetails.updated;
entryDetails.oldLink = entryDetails.link;
+ entryDetails.oldRemoved = entryDetails.removed;
entryDetails.oldHasEnclosure = entryDetails.hasEnclosure;
entryDetails.oldImage = entryDetails.image;
@@ -529,6 +531,7 @@ bool UpdateFeedJob::processEntry(const Syndication::ItemPtr &entry, DataTypes::F
updatedFeed.entries[id].created = created;
updatedFeed.entries[id].updated = updated;
updatedFeed.entries[id].link = link;
+ updatedFeed.entries[id].removed = false;
updatedFeed.entries[id].hasEnclosure = hasEnclosure;
updatedFeed.entries[id].image = image;
updatedFeed.entries[id].state = RecordState::Modified;
@@ -845,6 +848,20 @@ void UpdateFeedJob::writeToDatabase(DataTypes::FeedDetails &updatedFeed)
}
writeQuery.clear();
+ // removed entries
+ // rather than actually remove the episodes, we mark them as such through
+ // the column "removed"
+ writeQuery.prepare(QStringLiteral("UPDATE Entries SET removed=:removed WHERE entryuid=:entryuid;"));
+ for (const EntryDetails &entryDetails : std::as_const(updatedFeed.entries)) {
+ if (entryDetails.state == RecordState::Deleted && !entryDetails.removed) {
+ updatedEntryuids.insert(entryDetails.entryuid);
+ writeQuery.bindValue(QStringLiteral(":entryuid"), entryDetails.entryuid);
+ writeQuery.bindValue(QStringLiteral(":removed"), true);
+ dbExecute(writeQuery);
+ }
+ }
+ writeQuery.clear();
+
// new authors
writeQuery.prepare(QStringLiteral("INSERT INTO EntryAuthors (entryuid, name, email) VALUES (:entryuid, :name, :email);"));
for (const EntryDetails &entryDetails : std::as_const(updatedFeed.entries)) {