Implement interval-based automatic podcast updates

This also includes a restructuring of some of the settings.

BUG: 466789
This commit is contained in:
Bart De Vries 2024-04-16 16:08:37 +02:00
parent 98603c383e
commit 6822304c56
4 changed files with 161 additions and 69 deletions

View File

@ -44,6 +44,10 @@ Fetcher::Fetcher()
manager->enableStrictTransportSecurityStore(true);
QNetworkProxyFactory::setUseSystemConfiguration(true);
// setup update timer if required
initializeUpdateTimer();
connect(SettingsManager::self(), &SettingsManager::autoFeedUpdateIntervalChanged, this, &Fetcher::initializeUpdateTimer);
}
void Fetcher::fetch(const QString &url)
@ -242,3 +246,43 @@ void Fetcher::setHeader(QNetworkRequest &request) const
{
request.setRawHeader(QByteArray("User-Agent"), QByteArray("Kasts/") + QByteArray(KASTS_VERSION_STRING) + QByteArray(" Syndication"));
}
void Fetcher::initializeUpdateTimer()
{
qCDebug(kastsFetcher) << "Fetcher::setUpdateTimer";
qCDebug(kastsFetcher) << "new auto update interval =" << SettingsManager::self()->autoFeedUpdateInterval();
if (m_updateTimer) {
m_updateTimer->stop();
disconnect(m_updateTimer, &QTimer::timeout, this, &Fetcher::checkUpdateTimer);
delete m_updateTimer;
}
if (SettingsManager::self()->autoFeedUpdateInterval() > 0) {
m_updateTriggerTime =
QDateTime::currentDateTimeUtc().addSecs(3600 * SettingsManager::self()->autoFeedUpdateInterval()); // update interval specified in hours
m_updateTimer = new QTimer(this);
m_updateTimer->setTimerType(Qt::VeryCoarseTimer);
connect(m_updateTimer, &QTimer::timeout, this, &Fetcher::checkUpdateTimer);
m_updateTimer->start(m_checkInterval); // trigger every ten minutes
}
}
void Fetcher::checkUpdateTimer()
{
qCDebug(kastsFetcher) << "Fetcher::checkUpdateTimer; next automatic feed update in" << m_updateTriggerTime - QDateTime::currentDateTimeUtc();
// add a few seconds as "fuzzy match" to avoid that the trigger is delayed
// by another 10 minutes due to a difference of just a few milliseconds
if (QDateTime::currentDateTimeUtc().addSecs(5) > m_updateTriggerTime) {
qCDebug(kastsFetcher) << "Trigger for feed update has been reached; updating feeds now";
QTimer::singleShot(0, this, &Fetcher::fetchAll);
// set next update time
m_updateTriggerTime =
QDateTime::currentDateTimeUtc().addSecs(3600 * SettingsManager::self()->autoFeedUpdateInterval()); // update interval specified in hours
qCDebug(kastsFetcher) << "new auto feed update trigger set to" << m_updateTriggerTime;
}
}

View File

@ -7,11 +7,13 @@
#pragma once
#include <QDateTime>
#include <QFile>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QObject>
#include <QQmlEngine>
#include <QTimer>
#include <QUrl>
#include <Syndication/Syndication>
@ -50,6 +52,9 @@ public:
QNetworkReply *get(QNetworkRequest &request) const;
QNetworkReply *post(QNetworkRequest &request, const QByteArray &data) const;
void initializeUpdateTimer();
void checkUpdateTimer();
Q_SIGNALS:
void entryAdded(const QString &feedurl, const QString &id);
void entryUpdated(const QString &feedurl, const QString &id);
@ -84,4 +89,8 @@ private:
int m_updateProgress;
int m_updateTotal;
bool m_updating;
const qint64 m_checkInterval = 10 * 60 * 1000; // trigger timer every 10 minutes
QTimer *m_updateTimer;
QDateTime m_updateTriggerTime;
};

View File

@ -59,9 +59,8 @@ FormCard.FormCardPage {
FormCard.FormCheckDelegate {
id: showTimeLeft
Kirigami.FormData.label: i18nc("@option:check Label for settings related to the play time, e.g. whether the total track time is shown or a countdown of the remaining play time", "Play time:")
checked: SettingsManager.toggleRemainingTime
text: i18n("Show time left instead of total track time")
text: i18nc("@option:check Label for setting whether the total track time is shown or a countdown of the remaining play time", "Show time left instead of total track time")
onToggled: {
SettingsManager.toggleRemainingTime = checked;
SettingsManager.save();
@ -121,32 +120,46 @@ FormCard.FormCardPage {
}
FormCard.FormHeader {
title: i18nc("@title Form header for settings related to the queue", "Queue settings")
title: i18nc("@title Form header for settings related podcast updates", "Podcast update settings")
Layout.fillWidth: true
}
FormCard.FormCard {
Layout.fillWidth: true
FormCard.FormCheckDelegate {
id: continuePlayingNextEntry
checked: SettingsManager.continuePlayingNextEntry
text: i18nc("@option:check", "Continue playing next episode after current one finishes")
onToggled: {
SettingsManager.continuePlayingNextEntry = checked;
FormCard.FormComboBoxDelegate {
id: autoFeedUpdateInterval
text: i18nc("@label:listbox", "Automatically fetch podcast feeds")
textRole: "text"
valueRole: "value"
model: [{"text": i18nc("@item:inlistbox automatic podcast update interval", "Never"), "value": 0},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every hour", "Every %1 hours", 1), "value": 1},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every hour", "Every %1 hours", 2), "value": 2},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every hour", "Every %1 hours", 4), "value": 4},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every hour", "Every %1 hours", 8), "value": 8},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every hour", "Every %1 hours", 12), "value": 12},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every day", "Every %1 days", 1), "value": 24},
{"text": i18ncp("@item:inlistbox automatic podcast update interval", "Every day", "Every %1 days", 3), "value": 72}]
Component.onCompleted: currentIndex = indexOfValue(SettingsManager.autoFeedUpdateInterval)
onActivated: {
SettingsManager.autoFeedUpdateInterval = currentValue;
SettingsManager.save();
}
}
FormCard.FormCheckDelegate {
id: refreshOnStartup
Kirigami.FormData.label: i18nc("@option:check Label for settings related to podcast updates", "Update Settings:")
checked: SettingsManager.refreshOnStartup
text: i18n("Automatically fetch podcast updates on startup")
text: i18nc("@option:check", "Fetch podcast updates on startup")
onToggled: {
SettingsManager.refreshOnStartup = checked;
SettingsManager.save();
}
}
FormCard.FormDelegateSeparator { above: refreshOnStartup; below: doFullUpdate }
FormCard.FormCheckDelegate {
id: doFullUpdate
checked: SettingsManager.doFullUpdate
@ -183,17 +196,49 @@ FormCard.FormCardPage {
SettingsManager.save();
}
}
}
FormCard.FormHeader {
title: i18nc("@title Form header for settings related to the queue", "Queue settings")
Layout.fillWidth: true
}
FormCard.FormCard {
Layout.fillWidth: true
FormCard.FormCheckDelegate {
id: continuePlayingNextEntry
checked: SettingsManager.continuePlayingNextEntry
text: i18nc("@option:check", "Continue playing next episode after current one finishes")
onToggled: {
SettingsManager.continuePlayingNextEntry = checked;
SettingsManager.save();
}
}
FormCard.FormDelegateSeparator { above: continuePlayingNextEntry; below: resetPositionOnPlayed }
FormCard.FormCheckDelegate {
id: resetPositionOnPlayed
checked: SettingsManager.resetPositionOnPlayed
text: i18nc("@option:check", "Reset play position after an episode is played")
onToggled: {
SettingsManager.resetPositionOnPlayed = checked;
SettingsManager.save();
}
}
FormCard.FormDelegateSeparator { above: resetPositionOnPlayed; below: episodeBehavior }
FormCard.FormDelegateSeparator { above: autoDownload; below: episodeBehavior }
FormCard.FormComboBoxDelegate {
id: episodeBehavior
text: i18nc("@label:listbox", "Played episode behavior")
textRole: "text"
valueRole: "value"
model: [{"text": i18n("Do not delete"), "value": 0},
{"text": i18n("Delete immediately"), "value": 1},
{"text": i18n("Delete at next startup"), "value": 2}]
model: [{"text": i18nc("@item:inlistbox What to do with played episodes", "Do not delete"), "value": 0},
{"text": i18nc("@item:inlistbox What to do with played episodes", "Delete immediately"), "value": 1},
{"text": i18nc("@item:inlistbox What to do with played episodes", "Delete at next startup"), "value": 2}]
Component.onCompleted: currentIndex = indexOfValue(SettingsManager.autoDeleteOnPlayed)
onActivated: {
SettingsManager.autoDeleteOnPlayed = currentValue;
@ -218,18 +263,6 @@ FormCard.FormCardPage {
}
}
}
FormCard.FormDelegateSeparator { above: markAsPlayedGracePeriod; below: resetPositionOnPlayed }
FormCard.FormCheckDelegate {
id: resetPositionOnPlayed
checked: SettingsManager.resetPositionOnPlayed
text: i18nc("@option:check", "Reset play position after an episode is played")
onToggled: {
SettingsManager.resetPositionOnPlayed = checked;
SettingsManager.save();
}
}
}
FormCard.FormHeader {

View File

@ -5,44 +5,6 @@
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="kastsrc" />
<group name="General">
<entry name="ColorScheme" type="String">
<label>Color scheme</label>
</entry>
<entry name="showTrayIcon" type="Bool">
<label>Show icon in system tray</label>
<default>false</default>
</entry>
<entry name="trayIconType" type="Enum">
<label>Color/type of the tray icon</label>
<choices>
<choice name="Colorful">
<label>Colorful</label>
</choice>
<choice name="Light">
<label>Light</label>
</choice>
<choice name="Dark">
<label>Dark</label>
</choice>
</choices>
<default>Dark</default>
</entry>
<entry name="minimizeToTray" type="Bool">
<label>Minimize to system tray instead of closing application</label>
<default>true</default>
</entry>
<entry name="alwaysShowFeedTitles" type="Bool">
<label>Always show the title of podcast feeds in subscription view</label>
<default>false</default>
</entry>
<entry name="showEpisodeImage" type="Bool">
<label>Show the image of the episode rather than the general podcast image</label>
<default>true</default>
</entry>
<entry name="showPodcastTitle" type="Bool">
<label>Show the podcast/feed title on the entry delegate</label>
<default>false</default>
</entry>
<entry name="prioritizeStreaming" type="Bool">
<label>Show streaming button instead of download button</label>
<default>false</default>
@ -71,6 +33,10 @@
<label>Automatically download new episodes</label>
<default>false</default>
</entry>
<entry name="autoFeedUpdateInterval" type="Int">
<label>Interval for automatically updating feeds/podcasts. 0 means never update automatically.</label>
<default>0</default>
</entry>
<entry name="autoDeleteOnPlayed" type="Enum">
<label>Setting to select if or when to delete played episode</label>
<choices>
@ -124,15 +90,55 @@
<label>Article font size</label>
<default>10</default>
</entry>
<entry name="articleFontUseSystem" type="Bool">
<label>Use default system font</label>
<default>true</default>
</entry>
<entry name="StoragePath" type="Url">
<label>Custom path to store enclosures and images</label>
<default></default>
</entry>
</group>
<group name="Appearance">
<entry name="ColorScheme" type="String">
<label>Color scheme</label>
</entry>
<entry name="alwaysShowFeedTitles" type="Bool">
<label>Always show the title of podcast feeds in subscription view</label>
<default>false</default>
</entry>
<entry name="showEpisodeImage" type="Bool">
<label>Show the image of the episode rather than the general podcast image</label>
<default>true</default>
</entry>
<entry name="showPodcastTitle" type="Bool">
<label>Show the podcast/feed title on the entry delegate</label>
<default>false</default>
</entry>
<entry name="showTrayIcon" type="Bool">
<label>Show icon in system tray</label>
<default>false</default>
</entry>
<entry name="trayIconType" type="Enum">
<label>Color/type of the tray icon</label>
<choices>
<choice name="Colorful">
<label>Colorful</label>
</choice>
<choice name="Light">
<label>Light</label>
</choice>
<choice name="Dark">
<label>Dark</label>
</choice>
</choices>
<default>Dark</default>
</entry>
<entry name="minimizeToTray" type="Bool">
<label>Minimize to system tray instead of closing application</label>
<default>true</default>
</entry>
<entry name="articleFontUseSystem" type="Bool">
<label>Use default system font</label>
<default>true</default>
</entry>
</group>
<group name="Playback Settings">
<entry name="playbackRates" type="IntList">
<label>List of user-defined playback rates</label>