This used to be covered by the bottom action buttons, but those have
been removed some time ago. That only left a pull-down as trigger to
update the feeds, which is non-ideal since it's not explained anywhere.
addFeeds would stop if it encountered a url that we already had in the
database instead of continuing with the next one. This bug was
introduced due to refactoring for sync.
This implements the gpodder API from scratch. It turned out that
libmygpo-qt has several critical bugs, and there's no response to pull
requests upstream. So using that library was not an option.
The implementation into kasts consists of the following:
- Can sync with gpodder.net or with a nextcloud server that has the
nextcloud-gpodder app installed. (This app is mostly API compatible
with gpodder.)
- Passwords are stored using qtkeychain. If the keychain is
unavailable it will fallback to file.
- It syncs podcast subscriptions and episode play positions, including
marking episodes as played. Episodes that have a non-zero play
position will be added to the queue automatically.
- It will check for a metered connection before syncing. This is
coupled to the allowMeteredFeedUpdates setting.
- Full synchronization can be performed either manually (from the
settings page) or through automatic triggers: on startup and/or on
feed refresh.
- There is an additional possibility to trigger quick upload-only syncs
to make sure that the local changes are immediately uploaded to the
server (if the connection allows). This will trigger when
subscriptions are added or removed, when the pause/play button is
toggled or an episode is marked as played.
- This implements a few safeguards to avoid having multiple feed URLS
pointing to the same underlying feed (e.g. http vs https). This
solves part of #17
Solves #13
FolderDialog has issues retrieving the selected folder if that folder
has been selected through xdg-desktop-portal (i.e. flatpaks). Reverting
to FileDialog from QtQuick.Dialogs.
BUG: 443956
The fetch-on-startup would only happen if the metered state was
explicitly set to "No". Hence, when the state is "Unknown" (which will
happen in most cases) no fetching would be done on startup.
The new logic will check for the metered state not being equal to "Yes".
The feed update routine which is now spread over several methods
in Fetcher, is now put into a self-contained KJob. This will allow
to re-use this job later on in e.g. gpodder sync, where it's
required to update feeds before syncing episode statuses.
This also makes the feed update abortable.
Lastly, but most importantly, the feed update procedure has been
optimized to minimize database transactions, resulting in a dramatic
speed-up. This is especially true for importing new feeds, which
will now be at least 5x faster on slow hardware.
gettext complained that there's another string with the same text but
not plural and that is bad, so add a dummy context to make gettext happy
xgettext: warning: msgid 'Remove Podcast' is used without plural and with plural.
./qml/FeedListDelegate.qml:279: Here is the occurrence without plural.
./qml/FeedListPage.qml:249: Here is the occurrence with plural.
Workaround: If the msgid is a sentence, change the wording of the sentence; otherwise, use contexts for disambiguation.
xgettext: warning: msgid 'Delete Download' is used without plural and with plural.
./qml/GenericEntryDelegate.qml:294: Here is the occurrence without plural.
./qml/GenericEntryListView.qml:207: Here is the occurrence with plural.
Workaround: If the msgid is a sentence, change the wording of the sentence; otherwise, use contexts for disambiguation.
This adds "select all", "deselect all" to the page contextual actions
and adds "show podcast info" to the OverlayPage opened by clicking on
the overflow menu button on the cards.
This also adds correct use of plurals for the actions if more than one
item has been selected.
Context properties always takes in a QVariant, which means that
whenever you access the property it is re-evaluated because in
between each access the property may be changed as
setContextProperty() can be used at any moment in time.
The feeds will be sorted by (1) descending number of unread/unplayed
entries and (2) alphabetically by name. The current item and current
selection are maintained after re-sorting when the number of unread
entries has changed.
This commit adds keyboard navigation to entry lists.
Selection of items can be done through keyboard (shift+up/down), mouse
(left, left+shift, left+ctrl) or touch (long press).
When items are selected, contextual actions will show up on
the page (useful for touch screens), or, alternatively, a context menu
with the same actions can be opened through right mouse click (useful
for desktop).
If a single entry is selected, then only the relevant actions will be
shown (e.g. only "Mark as Played" if the entry has not been played yet).
Additionally, (database) transactions for the actions have been
optimized. This was necessary to make sure that actions on large
selections of entries finish within an acceptable time. E.g. actions on
a list of 1000 items should finish within a few seconds (on all but
underpowered hardware).
BUG: 441764
The "New Episodes" tab is to be replaced by a filter view button and
dialog to be able to filter several episode statuses. I.e. new,
played / not played, etc.
This reverts commit 5c8a6ea6a4.
It turns out that the issue is caused by qqc2-desktop-style and
qqc2-breeze-style. The problem should be solved upstream. And, anyway,
this workaround didn't completely solve the issue either.
Kasts can currently only handle one enclosure per entry. In case an
entry/item has multiple enclosures, it's probably safe to assume that the
first one is the one that's preferred by the author.
CCBUG: 440389
This adds a new setting to the Settings page.
Existing enclosures and images will be moved to the new location
(first copied, then deleted in the original location). If any of
the copy actions fail, the operation is aborted and the original
path is restored.
The StorageMoveJob is set up in such a way that it's easy to add other
files or subfolders in the future.
Solves #15
For now this only works with NetworkManager. The related settings are
greyed out on systems not using NetworkManager.
Some details of the implementation:
- Implement settings in the settings menu to enable/disable feed
updates, episode downloads and/ or image downloads on metered
connections. If the option(s) is disabled, an overlay dialog is shown
with options to "not allow", "allow once", or "allow always".
- If the network is down, no attempt is made to download images and the
fallback image will be used until the network is up again.
This also solves an issue where the application hangs when the network
is down and feed images have not been cached yet.
- Next to this, part of the cachedImage implementation in Entry and Feed
has been refactored to re-use code as part of the image() method in
Fetcher.
- In case something unexpected happens, an error will be logged.
RENAME COLUMN was only introduced in sqlite 3.25. So it's probably
better to avoid it for the time being to increase compatibility with
older versions.
Still to be done:
- Update Download Page to show partial downloads.
- Connect signals to Download Page to update whenever an enclosure
changes status. This is broken by this commit because
downloadCountChanged has been removed.
The main bits of this implementation are:
- Start a new track in paused state. We don't care about the actual
media state or player state that QMediaPlayer is reporting. We will
deal with that when the audio actually starts playing.
- If a player position needs to be restored, we set d->m_pendingSeek to
the position that needs to be seeked. We don't actually seek because
we have no idea what state the player is in yet.
- On the positionChanged signal of QMP, and if the media is buffered, we
check if there is pendingSeek value set which is set to a different
value than the current player position. If so, we call
d->m_player.setPosition(). If we have arrived at the correct
position, then we reset d->m_pendingSeek to -1.
- In the position(), duration() and seek() methods, we return sensible
values, even QMP is not. So, we report the duration from the
enclosure, the position from d->m_pendingSeek, and let seek() change
the value of d->m_PendingSeek (if it's not -1) to the new seek
position.
- When there's a pending seek, we set the notifyInterval to shorter
interval to reduce the startup audio glitch as much as possible. We
then reset it to the default of 1000 msec.
This was tested on linux and android.
- This refactoring also includes a cleanup of a lot of header includes to
avoid circular dependencies.
- The error message will now be shown below the info message.
- Add database migration (for Errors)
The inhibit dbus call to gnome sessionmanager expects flags as input.
Currently the flags are set to 4, which will "Inhibit the session being
marked as idle". This is insufficient to prevent the device from
suspending while playing.
We should also add 8 = "Inhibit suspending the session or computer".
Hence, the updated flags to 12.
This change was tested on phosh/phoc and it properly prevents the
system from suspending. The flags = 12 settings is also what gnome
music players like Lollypop are using (as checked through the gnome
sessionmanager dbus interface).
This enables the download method in Fetcher to resume in case a partial
download is already saved to disk.
For full implementation of download resumes, more changes are required,
because the current application will automatically clean up files that
don't match the expected size at startup.
When using a dark theme, the RSS icon will appear in (nearly) white on a
white background. Let's fix the icon colour to black to always get the
same fallback image.
The binding loop occured whenever the vertical scrollbar appears or
disappears. The GUI then gets stuck between solutions: one with a
certain amount of columns and another with one column less or more. It
then keeps alternating between these two until the app freezes the
machine and eventually crashes.
The fix is to calculate the number of columns based on the total width
of the Page itself, rather than the width of the content. By
subtracting enough space for the scrollbar, the cards on the page will
now rescale smoothly and properly.
The refresh buttons currently show up as main action buttons at the
bottom on desktop since they are placed inside SwipeViews/tabs. Move
these actions to the hierarchical parent ScrollablePages so they end up
in the header bar.
Also reworked some of the spacings and alignments of items on
PlayerControls to better make use of the available space and look
consistent across themes/styles (e.g. Material).
AudioManager is currently owned by qml, so it's highly confusing to have
this routine, since it would give a pointer to another instance than the
one owned by qml.
Resetting the flag immediately can lead to situations where all entries
of a new feed are marked as new if the initial feed import gets
interrupted somehow. E.g. when the user quits the application while
doing an initial import.
This leverages re-use of code and simplifies having to define fallbacks
everywhere images are displayed. It also gets rid of the dependency of
logo.png (which became problematic after the alligator logo was removed
from this repo).
Tobias was right; marking one (or more) episodes as "new" on a new feed
doesn't make a lot of sense. The latest episode from some podcast feeds
can be very old. Besides, it's quite annoying having a lot of episodes
queued automatically...
Instead of showing "add to queue" as main action and left action, the main
action will show "delete download", leaving "add to queue" as left
action.
This does mean that "delete download" can be main action or right
action, but this is nevertheless an improvement for now...
Making it toggle based on the playerState did not work properly because
the stream is toggling between pause and play several times in the
prepareAudioStream method.
This property returns the path to the cached image if it's been
downloaded, or return "" if it's not been downloaded yet, or "no-image"
in case the image property does not contain a URL to an image.
When adding a feed and simultaneously starting a feed update, a race
condition could happen where the feed update would catch up with the
feed adding, and start adding and marking old episodes as new before
the original addFeed method would reach them.
GStreamer is not used as QMediaPlayer backend on other platforms, so
audio will not start with a custom gst-pipeline on Android and Windows.
Still needs a solution to get rid of the horrible pitch change on
Android and Windows, though.
This allows the hack to be re-used in the play() method. Apparently on
some systems the stream becomes unresponsive again after pausing. So
probably it's required to ensure that the stream is fully seekable and
buffered before restarting playback.
Kirigami.Page is always adding padding around the content items. This
gives visual artifacts with the swipepage on the player controls. Part
of the previous page was still visible because the width of the
swipepage entries is less than the width of the full Kirigami.Page that
they are part of.