Merge lp:~dano6/podbird/podbird into lp:podbird

Proposed by Daniel Kutka
Status: Merged
Approved by: Michael Sheldon
Approved revision: no longer in the source branch.
Merged at revision: 210
Proposed branch: lp:~dano6/podbird/podbird
Merge into: lp:podbird
Diff against target: 273 lines (+84/-38)
6 files modified
app/podbird.qml (+31/-26)
app/podcasts.js (+4/-0)
app/ui/EpisodesPage.qml (+2/-2)
app/ui/EpisodesTab.qml (+5/-5)
app/ui/FullPlayingView.qml (+28/-5)
app/ui/SettingsPage.qml (+14/-0)
To merge this branch: bzr merge lp:~dano6/podbird/podbird
Reviewer Review Type Date Requested Status
Michael Sheldon Approve
Daniel Kutka (community) Needs Resubmitting
Review via email: mp+387072@code.launchpad.net

Commit message

Implemented restoring queue, resuming of episode where stopped and fixed some playback issues

Description of the change

This MR brings restoring queue after restart and restoring playback position (optional). Seeking slider behavior was ported from music-app, that fixes some playback issues - sluggish seeking, getting weird loop after seeking, brings overall better responsiveness of controlling audio.

To post a comment you must log in.
Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Overall this has some excellent improvements, thanks very much! There's a couple of minor things to address that I've added as diff comments

review: Needs Fixing
Revision history for this message
Daniel Kutka (dano6) :
review: Needs Resubmitting
Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Excellent, thanks very much!

review: Approve
lp:~dano6/podbird/podbird updated
210. By Michael Sheldon

Merge lp:~dano6/podbird/podbird:

* Restore queue after restart
* Fix resuming playback
* Fix some playback/seeking issues

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'app/podbird.qml'
--- app/podbird.qml 2020-06-30 11:10:05 +0000
+++ app/podbird.qml 2020-07-09 11:20:38 +0000
@@ -42,13 +42,6 @@
42 theme.name: settings.themeName == "Dark.qml" ? "Ubuntu.Components.Themes.SuruDark"42 theme.name: settings.themeName == "Dark.qml" ? "Ubuntu.Components.Themes.SuruDark"
43 : "Ubuntu.Components.Themes.Ambiance"43 : "Ubuntu.Components.Themes.Ambiance"
4444
45 Component.onDestruction: {
46 var db = Podcasts.init()
47 db.transaction(function (tx) {
48 tx.executeSql('UPDATE Episode SET queued=0 WHERE queued=1');
49 })
50 Podcasts.clearQueue()
51 }
5245
53 // RefreshModel function to call refreshModel() function of the tab currently46 // RefreshModel function to call refreshModel() function of the tab currently
54 // visible on application start.47 // visible on application start.
@@ -62,9 +55,6 @@
6255
63 Component.onCompleted: {56 Component.onCompleted: {
64 var db = Podcasts.init()57 var db = Podcasts.init()
65 db.transaction(function (tx) {
66 tx.executeSql('UPDATE Episode SET queued=0 WHERE queued=1');
67 })
6858
69 var today = new Date()59 var today = new Date()
70 // Only automatically check for podcasts on launch once every 12 hours60 // Only automatically check for podcasts on launch once every 12 hours
@@ -126,6 +116,8 @@
126 property bool showListView: true116 property bool showListView: true
127 property int skipForward: 30117 property int skipForward: 30
128 property int skipBack: 10118 property int skipBack: 10
119 property bool continueWhereStopped: true
120 property int playlistIndex: -1
129 property bool downloadOverWifiOnly: true121 property bool downloadOverWifiOnly: true
130 }122 }
131123
@@ -232,14 +224,15 @@
232 // but media-hub doesn't report it correctly224 // but media-hub doesn't report it correctly
233 console.log("Starting playback")225 console.log("Starting playback")
234 player.play()226 player.play()
235 player.restorePosition()
236 }227 }
237 }228 }
238229
239 MediaPlayer {230 MediaPlayer {
240 id: player231 id: player
241232
242 onPositionChanged: console.log("DEBUG: player position changed to: ", position)233 onPositionChanged: {
234 restorePosition()
235 }
243236
244 // Wrapper function around decodeURIComponent() to prevent exceptions237 // Wrapper function around decodeURIComponent() to prevent exceptions
245 // from bubbling up to the app.238 // from bubbling up to the app.
@@ -277,26 +270,41 @@
277 savePosition()270 savePosition()
278 } else {271 } else {
279 play()272 play()
280 // Clear the last saved position when we start playing again
281 clearPosition()
282 }273 }
283 }274 }
284275
285 function savePosition() {276 function savePosition() {
277 podbird.settings.playlistIndex = playlist.currentIndex
286 if (currentGuid) {278 if (currentGuid) {
287 var db = Podcasts.init()279 var db = Podcasts.init()
288 db.transaction(function (tx) {280 db.transaction(function (tx) {
289 tx.executeSql("UPDATE Episode SET position=? WHERE guid=?", [player.position, currentGuid])281 tx.executeSql("UPDATE Episode SET position=? WHERE guid=?", [player.position, currentGuid])
290 tx.executeSql("UPDATE Queue SET position=? WHERE guid=?", [player.position, currentGuid])282 tx.executeSql("UPDATE Queue SET position=? WHERE guid=?", [player.position, currentGuid])
283 if(player.position / player.duration > 0.90)
284 tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [currentGuid])
291 })285 })
292 }286 }
293 }287 }
294288
289 function restoreFromQueue() {
290 var db = Podcasts.init()
291 db.transaction(function (tx) {
292 var rs = tx.executeSql("SELECT * FROM Queue")
293 for (var i=0; i<rs.rows.length;i++) {
294 var episode = rs.rows.item(i)
295 player.playlist.addItem(episode.url)
296 }
297 })
298 if(playlist.itemCount > podbird.settings.playlistIndex)
299 playlist.currentIndex = podbird.settings.playlistIndex
300 }
301
295 function restorePosition() {302 function restorePosition() {
296 if (playbackState === MediaPlayer.PlayingState && pendingSeek !== 0) {303 if(playbackState === MediaPlayer.PlayingState && status === MediaPlayer.Loaded && pendingSeek){
297 player.seek(pendingSeek)304 //ugly hack because seek function does not seems to work async
298 player.clearPosition()305 var p = pendingSeek
299 pendingSeek = 0;306 pendingSeek = 0
307 player.seek(p)
300 }308 }
301 }309 }
302310
@@ -345,7 +353,6 @@
345 }353 }
346354
347 property bool endOfMedia: false355 property bool endOfMedia: false
348 property double progress: 0
349 property int pendingSeek: 0356 property int pendingSeek: 0
350357
351 playlist: Playlist {358 playlist: Playlist {
@@ -361,16 +368,10 @@
361 currentArtist = meta.artist368 currentArtist = meta.artist
362 currentImage = meta.image369 currentImage = meta.image
363 currentGuid = meta.guid370 currentGuid = meta.guid
364 player.pendingSeek = meta.position371 player.pendingSeek = podbird.settings.continueWhereStopped && meta.position > 5000 ? meta.position : 0
365 console.log("DEBUG: player.pendingSeek: ", player.pendingSeek, " meta.position ", meta.position)
366 }372 }
367 }373 }
368374
369 onPlaybackStateChanged: {
370 console.log("DEBUG: Restoring Position to: ", player.position)
371 restorePosition()
372 }
373
374 onStatusChanged: {375 onStatusChanged: {
375 if (status === MediaPlayer.EndOfMedia) {376 if (status === MediaPlayer.EndOfMedia) {
376 console.log("[LOG]: End of Media. Stopping.")377 console.log("[LOG]: End of Media. Stopping.")
@@ -399,6 +400,10 @@
399 Component.onDestruction: {400 Component.onDestruction: {
400 savePosition()401 savePosition()
401 }402 }
403
404 Component.onCompleted: {
405 restoreFromQueue()
406 }
402 }407 }
403408
404 PageStack {409 PageStack {
405410
=== modified file 'app/podcasts.js'
--- app/podcasts.js 2017-01-03 00:02:16 +0000
+++ app/podcasts.js 2020-07-09 11:20:38 +0000
@@ -104,6 +104,10 @@
104 db.transaction(function(tx) {104 db.transaction(function(tx) {
105 tx.executeSql("DELETE FROM Queue");105 tx.executeSql("DELETE FROM Queue");
106 });106 });
107
108 db.transaction(function (tx) {
109 tx.executeSql('UPDATE Episode SET queued=0 WHERE queued=1');
110 });
107}111}
108112
109function lookup(source) {113function lookup(source) {
110114
=== modified file 'app/ui/EpisodesPage.qml'
--- app/ui/EpisodesPage.qml 2019-10-18 11:56:16 +0000
+++ app/ui/EpisodesPage.qml 2020-07-09 11:20:38 +0000
@@ -574,8 +574,8 @@
574574
575 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")575 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
576 : Qt.formatDate(new Date(model.published), "MMM d, yyyy")576 : Qt.formatDate(new Date(model.published), "MMM d, yyyy")
577 : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")577 : model.downloadedfile ? "📎 " + (model.position ? Podcasts.formatEpisodeTime(model.position/1000) + "/" : "") + Podcasts.formatEpisodeTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
578 : Podcasts.formatEpisodeTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")578 : (model.position ? Podcasts.formatEpisodeTime(model.position/1000) + "/" : "") + Podcasts.formatEpisodeTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
579 subtitle.color: podbird.appTheme.baseSubText579 subtitle.color: podbird.appTheme.baseSubText
580580
581 padding.top: units.gu(1)581 padding.top: units.gu(1)
582582
=== modified file 'app/ui/EpisodesTab.qml'
--- app/ui/EpisodesTab.qml 2019-03-26 10:39:53 +0000
+++ app/ui/EpisodesTab.qml 2020-07-09 11:20:38 +0000
@@ -544,8 +544,8 @@
544544
545 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist545 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist
546 : model.artist546 : model.artist
547 : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist547 : model.downloadedfile ? "📎 " + (model.position ? Podcasts.formatEpisodeTime(model.position/1000) + "/" : "") + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
548 : Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist548 : (model.position ? Podcasts.formatEpisodeTime(model.position/1000) + "/" : "") + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
549 subtitle.color: podbird.appTheme.baseSubText549 subtitle.color: podbird.appTheme.baseSubText
550550
551 Image {551 Image {
@@ -747,9 +747,9 @@
747 episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited, "diff": "Older"})747 episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited, "diff": "Older"})
748 }748 }
749 } else if (diff >= 7) {749 } else if (diff >= 7) {
750 if (episode.downloadedfile != null) {750 if (episode.downloadedfile != null) {
751 episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited, "diff": "Older"})751 episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited, "diff": "Older"})
752 }752 }
753 }753 }
754 }754 }
755755
756756
=== modified file 'app/ui/FullPlayingView.qml'
--- app/ui/FullPlayingView.qml 2017-01-01 14:28:51 +0000
+++ app/ui/FullPlayingView.qml 2020-07-09 11:20:38 +0000
@@ -133,10 +133,24 @@
133 maximumValue: player.duration133 maximumValue: player.duration
134 value: player.position134 value: player.position
135 height: units.gu(2)135 height: units.gu(2)
136 136
137 onValueChanged: {137 property bool seeking: false
138 if (pressed) {138 property bool seeked: false
139 player.seek(value);139
140 onSeekingChanged: {
141 if (seeking === false) {
142 startTime.text = Podcasts.formatTime(player.position / 1000)
143 }
144 }
145
146 onPressedChanged: {
147 seeking = pressed
148
149 if (!pressed) {
150 seeked = true
151 player.seek(value)
152
153 startTime.text = Podcasts.formatTime(value / 1000)
140 }154 }
141 }155 }
142 156
@@ -146,7 +160,16 @@
146 160
147 Connections {161 Connections {
148 target: player162 target: player
149 onPositionChanged: scrubber.value = player.position163 onPositionChanged: {
164 // seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0)
165 if (scrubber.seeking === false && !scrubber.seeked) {
166 startTime.text = Podcasts.formatTime(player.position / 1000)
167 endTime.text = Podcasts.formatTime(player.duration / 1000)
168
169 scrubber.value = player.position
170 }
171 scrubber.seeked = false;
172 }
150 }173 }
151 174
152 Label {175 Label {
153176
=== modified file 'app/ui/SettingsPage.qml'
--- app/ui/SettingsPage.qml 2016-12-11 21:23:21 +0000
+++ app/ui/SettingsPage.qml 2020-07-09 11:20:38 +0000
@@ -180,6 +180,20 @@
180 onClicked: PopupUtils.open(skipBackDialog, settingsPage);180 onClicked: PopupUtils.open(skipBackDialog, settingsPage);
181 }181 }
182182
183 ListItem {
184 ListItemLayout {
185 id: continueWhereStopped
186 title.text: i18n.tr("Continue where stopped")
187 Switch {
188 SlotsLayout.position: SlotsLayout.Last
189 checked: podbird.settings.continueWhereStopped
190 onClicked: podbird.settings.continueWhereStopped = checked
191 }
192 }
193 divider.visible: false
194 height: gridViewLayout.height
195 }
196
183 HeaderListItem {197 HeaderListItem {
184 title.text: i18n.tr("Podcast Episode Settings")198 title.text: i18n.tr("Podcast Episode Settings")
185 }199 }

Subscribers

People subscribed via source and target branches

to status/vote changes: