git.iko.soy/maciek134/podphoenix
on main help sign in
Implemented restoring queue, resuming of episode where stopped and fixed some playback issues
401dff9Daniel Kutka
app/podbird.qml+32−26
@@ -42,13 +42,6 @@ MainView {
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"
44
44
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− }
52
45
53 // RefreshModel function to call refreshModel() function of the tab currently
46 // RefreshModel function to call refreshModel() function of the tab currently
54 // visible on application start.
47 // visible on application start.
@@ -62,9 +55,6 @@ MainView {
62
55
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− })
68
58
69 var today = new Date()
59 var today = new Date()
70 // Only automatically check for podcasts on launch once every 12 hours
60 // Only automatically check for podcasts on launch once every 12 hours
@@ -126,6 +116,8 @@ MainView {
126 property bool showListView: true
116 property bool showListView: true
127 property int skipForward: 30
117 property int skipForward: 30
128 property int skipBack: 10
118 property int skipBack: 10
119+ property bool continueWhereStopped: true
120+ property int playlistIndex: -1
129 property bool downloadOverWifiOnly: true
121 property bool downloadOverWifiOnly: true
130 }
122 }
131
123
@@ -232,14 +224,15 @@ MainView {
232 // but media-hub doesn't report it correctly
224 // 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 }
238
229
239 MediaPlayer {
230 MediaPlayer {
240 id: player
231 id: player
241
232
242− onPositionChanged: console.log("DEBUG: player position changed to: ", position)
233+ onPositionChanged: {
234+ restorePosition()
235+ }
243
236
244 // Wrapper function around decodeURIComponent() to prevent exceptions
237 // Wrapper function around decodeURIComponent() to prevent exceptions
245 // from bubbling up to the app.
238 // from bubbling up to the app.
@@ -277,26 +270,42 @@ MainView {
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 }
284
275
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 }
294
288
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+ console.log(episode.name)
296+ player.playlist.addItem(episode.url)
297+ }
298+ })
299+ if(playlist.itemCount > podbird.settings.playlistIndex)
300+ playlist.currentIndex = podbird.settings.playlistIndex
301+ }
302+
295 function restorePosition() {
303 function restorePosition() {
296− if (playbackState === MediaPlayer.PlayingState && pendingSeek !== 0) {
304+ if(playbackState === MediaPlayer.PlayingState && status === MediaPlayer.Loaded && pendingSeek){
297− player.seek(pendingSeek)
305+ //ugly hack because seek function does not seems to work async
298− player.clearPosition()
306+ var p = pendingSeek
299− pendingSeek = 0;
307+ pendingSeek = 0
308+ player.seek(p)
300 }
309 }
301 }
310 }
302
311
@@ -345,7 +354,6 @@ MainView {
345 }
354 }
346
355
347 property bool endOfMedia: false
356 property bool endOfMedia: false
348− property double progress: 0
349 property int pendingSeek: 0
357 property int pendingSeek: 0
350
358
351 playlist: Playlist {
359 playlist: Playlist {
@@ -361,16 +369,10 @@ MainView {
361 currentArtist = meta.artist
369 currentArtist = meta.artist
362 currentImage = meta.image
370 currentImage = meta.image
363 currentGuid = meta.guid
371 currentGuid = meta.guid
364− player.pendingSeek = meta.position
372+ player.pendingSeek = podbird.settings.continueWhereStopped && meta.position > 5000 ? meta.position : 0
365− console.log("DEBUG: player.pendingSeek: ", player.pendingSeek, " meta.position ", meta.position)
366 }
373 }
367 }
374 }
368
375
369− onPlaybackStateChanged: {
370− console.log("DEBUG: Restoring Position to: ", player.position)
371− restorePosition()
372− }
373−
374 onStatusChanged: {
376 onStatusChanged: {
375 if (status === MediaPlayer.EndOfMedia) {
377 if (status === MediaPlayer.EndOfMedia) {
376 console.log("[LOG]: End of Media. Stopping.")
378 console.log("[LOG]: End of Media. Stopping.")
@@ -399,6 +401,10 @@ MainView {
399 Component.onDestruction: {
401 Component.onDestruction: {
400 savePosition()
402 savePosition()
401 }
403 }
404+
405+ Component.onCompleted: {
406+ restoreFromQueue()
407+ }
402 }
408 }
403
409
404 PageStack {
410 PageStack {
app/podcasts.js+4
@@ -104,6 +104,10 @@ function clearQueue() {
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}
108
112
109function lookup(source) {
113function lookup(source) {
app/ui/EpisodesPage.qml+2−2
@@ -574,8 +574,8 @@ Page {
574
574
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 ? "📎 " + 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+ : Podcasts.formatEpisodeTime(model.position/1000) + "/" + Podcasts.formatEpisodeTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
579 subtitle.color: podbird.appTheme.baseSubText
579 subtitle.color: podbird.appTheme.baseSubText
580
580
581 padding.top: units.gu(1)
581 padding.top: units.gu(1)
app/ui/EpisodesTab.qml+5−5
@@ -544,8 +544,8 @@ Tab {
544
544
545 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist
545 subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist
546 : model.artist
546 : model.artist
547− : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
547+ : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.position/1000) + "/" + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
548− : Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
548+ : Podcasts.formatEpisodeTime(model.position/1000) + "/" + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
549 subtitle.color: podbird.appTheme.baseSubText
549 subtitle.color: podbird.appTheme.baseSubText
550
550
551 Image {
551 Image {
@@ -747,9 +747,9 @@ Tab {
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 }
755
755
app/ui/FullPlayingView.qml+28−5
@@ -133,10 +133,24 @@ Item {
133 maximumValue: player.duration
133 maximumValue: player.duration
134 value: player.position
134 value: player.position
135 height: units.gu(2)
135 height: units.gu(2)
136−
136+
137− onValueChanged: {
137+ property bool seeking: false
138+ property bool seeked: false
139+
140+ onSeekingChanged: {
141+ if (seeking === false) {
142+ startTime.text = Podcasts.formatTime(player.position / 1000)
143+ }
144+ }
145+
146+ onPressedChanged: {
147+ seeking = pressed
148+
138− if (pressed) {
149+ if (!pressed) {
150+ seeked = true
139− player.seek(value);
151+ player.seek(value)
152+
153+ startTime.text = Podcasts.formatTime(value / 1000)
140 }
154 }
141 }
155 }
142
156
@@ -146,7 +160,16 @@ Item {
146
160
147 Connections {
161 Connections {
148 target: player
162 target: player
163+ 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+
149− onPositionChanged: scrubber.value = player.position
169+ scrubber.value = player.position
170+ }
171+ scrubber.seeked = false;
172+ }
150 }
173 }
151
174
152 Label {
175 Label {
app/ui/SettingsPage.qml+14
@@ -180,6 +180,20 @@ Page {
180 onClicked: PopupUtils.open(skipBackDialog, settingsPage);
180 onClicked: PopupUtils.open(skipBackDialog, settingsPage);
181 }
181 }
182
182
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 }