Implement interval-based automatic podcast updates
This also includes a restructuring of some of the settings. BUG: 466789
This commit is contained in:
parent
98603c383e
commit
6822304c56
@ -44,6 +44,10 @@ Fetcher::Fetcher()
|
|||||||
manager->enableStrictTransportSecurityStore(true);
|
manager->enableStrictTransportSecurityStore(true);
|
||||||
|
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
|
// setup update timer if required
|
||||||
|
initializeUpdateTimer();
|
||||||
|
connect(SettingsManager::self(), &SettingsManager::autoFeedUpdateIntervalChanged, this, &Fetcher::initializeUpdateTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fetcher::fetch(const QString &url)
|
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"));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,11 +7,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
#include <QTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <Syndication/Syndication>
|
#include <Syndication/Syndication>
|
||||||
|
|
||||||
@ -50,6 +52,9 @@ public:
|
|||||||
QNetworkReply *get(QNetworkRequest &request) const;
|
QNetworkReply *get(QNetworkRequest &request) const;
|
||||||
QNetworkReply *post(QNetworkRequest &request, const QByteArray &data) const;
|
QNetworkReply *post(QNetworkRequest &request, const QByteArray &data) const;
|
||||||
|
|
||||||
|
void initializeUpdateTimer();
|
||||||
|
void checkUpdateTimer();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void entryAdded(const QString &feedurl, const QString &id);
|
void entryAdded(const QString &feedurl, const QString &id);
|
||||||
void entryUpdated(const QString &feedurl, const QString &id);
|
void entryUpdated(const QString &feedurl, const QString &id);
|
||||||
@ -84,4 +89,8 @@ private:
|
|||||||
int m_updateProgress;
|
int m_updateProgress;
|
||||||
int m_updateTotal;
|
int m_updateTotal;
|
||||||
bool m_updating;
|
bool m_updating;
|
||||||
|
|
||||||
|
const qint64 m_checkInterval = 10 * 60 * 1000; // trigger timer every 10 minutes
|
||||||
|
QTimer *m_updateTimer;
|
||||||
|
QDateTime m_updateTriggerTime;
|
||||||
};
|
};
|
||||||
|
@ -59,9 +59,8 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: showTimeLeft
|
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
|
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: {
|
onToggled: {
|
||||||
SettingsManager.toggleRemainingTime = checked;
|
SettingsManager.toggleRemainingTime = checked;
|
||||||
SettingsManager.save();
|
SettingsManager.save();
|
||||||
@ -121,32 +120,46 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormHeader {
|
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
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormComboBoxDelegate {
|
||||||
id: continuePlayingNextEntry
|
id: autoFeedUpdateInterval
|
||||||
checked: SettingsManager.continuePlayingNextEntry
|
text: i18nc("@label:listbox", "Automatically fetch podcast feeds")
|
||||||
text: i18nc("@option:check", "Continue playing next episode after current one finishes")
|
textRole: "text"
|
||||||
onToggled: {
|
valueRole: "value"
|
||||||
SettingsManager.continuePlayingNextEntry = checked;
|
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();
|
SettingsManager.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: refreshOnStartup
|
id: refreshOnStartup
|
||||||
Kirigami.FormData.label: i18nc("@option:check Label for settings related to podcast updates", "Update Settings:")
|
|
||||||
checked: SettingsManager.refreshOnStartup
|
checked: SettingsManager.refreshOnStartup
|
||||||
text: i18n("Automatically fetch podcast updates on startup")
|
text: i18nc("@option:check", "Fetch podcast updates on startup")
|
||||||
onToggled: {
|
onToggled: {
|
||||||
SettingsManager.refreshOnStartup = checked;
|
SettingsManager.refreshOnStartup = checked;
|
||||||
SettingsManager.save();
|
SettingsManager.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FormCard.FormDelegateSeparator { above: refreshOnStartup; below: doFullUpdate }
|
||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: doFullUpdate
|
id: doFullUpdate
|
||||||
checked: SettingsManager.doFullUpdate
|
checked: SettingsManager.doFullUpdate
|
||||||
@ -183,17 +196,49 @@ FormCard.FormCardPage {
|
|||||||
SettingsManager.save();
|
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 {
|
FormCard.FormComboBoxDelegate {
|
||||||
id: episodeBehavior
|
id: episodeBehavior
|
||||||
text: i18nc("@label:listbox", "Played episode behavior")
|
text: i18nc("@label:listbox", "Played episode behavior")
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
valueRole: "value"
|
valueRole: "value"
|
||||||
model: [{"text": i18n("Do not delete"), "value": 0},
|
model: [{"text": i18nc("@item:inlistbox What to do with played episodes", "Do not delete"), "value": 0},
|
||||||
{"text": i18n("Delete immediately"), "value": 1},
|
{"text": i18nc("@item:inlistbox What to do with played episodes", "Delete immediately"), "value": 1},
|
||||||
{"text": i18n("Delete at next startup"), "value": 2}]
|
{"text": i18nc("@item:inlistbox What to do with played episodes", "Delete at next startup"), "value": 2}]
|
||||||
Component.onCompleted: currentIndex = indexOfValue(SettingsManager.autoDeleteOnPlayed)
|
Component.onCompleted: currentIndex = indexOfValue(SettingsManager.autoDeleteOnPlayed)
|
||||||
onActivated: {
|
onActivated: {
|
||||||
SettingsManager.autoDeleteOnPlayed = currentValue;
|
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 {
|
FormCard.FormHeader {
|
||||||
|
@ -5,44 +5,6 @@
|
|||||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||||
<kcfgfile name="kastsrc" />
|
<kcfgfile name="kastsrc" />
|
||||||
<group name="General">
|
<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">
|
<entry name="prioritizeStreaming" type="Bool">
|
||||||
<label>Show streaming button instead of download button</label>
|
<label>Show streaming button instead of download button</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
@ -71,6 +33,10 @@
|
|||||||
<label>Automatically download new episodes</label>
|
<label>Automatically download new episodes</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</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">
|
<entry name="autoDeleteOnPlayed" type="Enum">
|
||||||
<label>Setting to select if or when to delete played episode</label>
|
<label>Setting to select if or when to delete played episode</label>
|
||||||
<choices>
|
<choices>
|
||||||
@ -124,15 +90,55 @@
|
|||||||
<label>Article font size</label>
|
<label>Article font size</label>
|
||||||
<default>10</default>
|
<default>10</default>
|
||||||
</entry>
|
</entry>
|
||||||
<entry name="articleFontUseSystem" type="Bool">
|
|
||||||
<label>Use default system font</label>
|
|
||||||
<default>true</default>
|
|
||||||
</entry>
|
|
||||||
<entry name="StoragePath" type="Url">
|
<entry name="StoragePath" type="Url">
|
||||||
<label>Custom path to store enclosures and images</label>
|
<label>Custom path to store enclosures and images</label>
|
||||||
<default></default>
|
<default></default>
|
||||||
</entry>
|
</entry>
|
||||||
</group>
|
</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">
|
<group name="Playback Settings">
|
||||||
<entry name="playbackRates" type="IntList">
|
<entry name="playbackRates" type="IntList">
|
||||||
<label>List of user-defined playback rates</label>
|
<label>List of user-defined playback rates</label>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user