git.iko.soy/maciek134/podphoenix
on main help sign in
* Reintroduce position saving and fix switching between episodes
4e20264Michael Sheldon
app/podbird.qml+55−8
@@ -245,6 +245,7 @@ MainView {
245 artist: "",
245 artist: "",
246 image: "",
246 image: "",
247 guid: "",
247 guid: "",
248+ position: 0,
248 }
249 }
249
250
250 source = source.toString()
251 source = source.toString()
@@ -255,26 +256,50 @@ MainView {
255 function toggle() {
256 function toggle() {
256 if (playbackState === MediaPlayer.PlayingState) {
257 if (playbackState === MediaPlayer.PlayingState) {
257 pause()
258 pause()
259+ // Save the current position when we pause
260+ savePosition()
258 } else {
261 } else {
259 play()
262 play()
263+ // Clear the last saved position when we start playing again
264+ clearPosition()
260 }
265 }
261 }
266 }
262
267
268+ function savePosition() {
269+ if (currentGuid) {
270+ var db = Podcasts.init()
271+ db.transaction(function (tx) {
272+ tx.executeSql("UPDATE Episode SET position=? WHERE guid=?", [player.position, currentGuid])
273+ tx.executeSql("UPDATE Queue SET position=? WHERE guid=?", [player.position, currentGuid])
274+ })
275+ }
276+ }
277+
278+ function clearPosition() {
279+ if (currentGuid) {
280+ var db = Podcasts.init()
281+ db.transaction(function (tx) {
282+ tx.executeSql("UPDATE Episode SET position=NULL WHERE guid=?", [currentGuid])
283+ })
284+ }
285+ }
286+
263− function playEpisode(guid, image, name, artist, url) {
287+ function playEpisode(guid, image, name, artist, url, position) {
264 // Clear current queue
288 // Clear current queue
265 player.playlist.clear()
289 player.playlist.clear()
266 Podcasts.clearQueue()
290 Podcasts.clearQueue()
267
291
268 // Add episode to queue
292 // Add episode to queue
269− Podcasts.addItemToQueue(guid, image, name, artist, url)
293+ Podcasts.addItemToQueue(guid, image, name, artist, url, position)
270 player.playlist.addItem(url)
294 player.playlist.addItem(url)
271−
295+
272 // Play episode
296 // Play episode
297+ pendingSeek = position
273 player.play()
298 player.play()
274 }
299 }
275
300
276− function addEpisodeToQueue(guid, image, name, artist, url) {
301+ function addEpisodeToQueue(guid, image, name, artist, url, position) {
277− Podcasts.addItemToQueue(guid, image, name, artist, url)
302+ Podcasts.addItemToQueue(guid, image, name, artist, url, position)
278 player.playlist.addItem(url)
303 player.playlist.addItem(url)
279
304
280 // If added episode is the first one in the queue, then set the current metadata
305 // If added episode is the first one in the queue, then set the current metadata
@@ -286,11 +311,13 @@ MainView {
286 currentArtist = artist
311 currentArtist = artist
287 currentImage = image
312 currentImage = image
288 currentUrl = url
313 currentUrl = url
314+ pendingSeek = position
289 }
315 }
290 }
316 }
291
317
292 property bool endOfMedia: false
318 property bool endOfMedia: false
293 property double progress: 0
319 property double progress: 0
320+ property int pendingSeek: 0
294
321
295 playlist: Playlist {
322 playlist: Playlist {
296 playbackMode: Playlist.Sequential
323 playbackMode: Playlist.Sequential
@@ -305,11 +332,28 @@ MainView {
305 currentArtist = meta.artist
332 currentArtist = meta.artist
306 currentImage = meta.image
333 currentImage = meta.image
307 currentGuid = meta.guid
334 currentGuid = meta.guid
335+ player.pendingSeek = meta.position
336+ }
337+ }
338+
339+ onPlaybackStateChanged: {
340+ if (playbackState === MediaPlayer.PlayingState && pendingSeek !== 0) {
341+ player.seek(pendingSeek)
342+ player.clearPosition()
343+ pendingSeek = 0;
308 }
344 }
309 }
345 }
310
346
311 onStatusChanged: {
347 onStatusChanged: {
312− if (status === MediaPlayer.EndOfMedia) {
348+ if (status === MediaPlayer.Loading) {
349+ if (playbackState === MediaPlayer.PlayingState && pendingSeek !== 0) {
350+ player.seek(pendingSeek)
351+ player.clearPosition()
352+ pendingSeek = 0;
353+ } else {
354+ play()
355+ }
356+ } else if (status === MediaPlayer.EndOfMedia) {
313 console.log("[LOG]: End of Media. Stopping.")
357 console.log("[LOG]: End of Media. Stopping.")
314 endOfMedia = true
358 endOfMedia = true
315 stop()
359 stop()
@@ -325,14 +369,17 @@ MainView {
325 // Play then pause otherwise when we come from EndOfMedia
369 // Play then pause otherwise when we come from EndOfMedia
326 // it calls next() until EndOfMedia again.
370 // it calls next() until EndOfMedia again.
327 play()
371 play()
372+ pause()
328 }
373 }
329−
330− pause()
331 }
374 }
332
375
333 // Always reset endOfMedia
376 // Always reset endOfMedia
334 endOfMedia = false
377 endOfMedia = false
335 }
378 }
379+
380+ Component.onDestruction: {
381+ savePosition()
382+ }
336 }
383 }
337
384
338 PageStack {
385 PageStack {
app/podcasts.js+14−2
@@ -49,6 +49,16 @@ function init() {
49 tx.executeSql('UPDATE Episode SET favourited=?', [false]);
49 tx.executeSql('UPDATE Episode SET favourited=?', [false]);
50 });
50 });
51 }
51 }
52+
53+ /*
54+ Schema Upgrade to v1.3 which adds position information to the episode queue
55+ */
56+ if (db.version === "1.2") {
57+ console.log("Upgrading database from %1 -> v1.3".arg(db.version))
58+ db.changeVersion("1.2", "1.3", function(tx) {
59+ tx.executeSql('ALTER TABLE Queue ADD position INTEGER');
60+ });
61+ }
52 } catch(ex) {
62 } catch(ex) {
53 console.log(ex)
63 console.log(ex)
54 }
64 }
@@ -57,12 +67,12 @@ function init() {
57}
67}
58
68
59// Function to add item to queue
69// Function to add item to queue
60−function addItemToQueue(guid, image, name, artist, url) {
70+function addItemToQueue(guid, image, name, artist, url, position) {
61 var db = init()
71 var db = init()
62
72
63 db.transaction(function(tx) {
73 db.transaction(function(tx) {
64 var ind = getNextIndex(tx);
74 var ind = getNextIndex(tx);
65− var rs = tx.executeSql("INSERT OR REPLACE INTO Queue (ind, guid, image, name, artist, url) VALUES (?, ?, ?, ?, ?, ?)", [ind, guid, image, name, artist, url]);
75+ var rs = tx.executeSql("INSERT OR REPLACE INTO Queue (ind, guid, image, name, artist, url, position) VALUES (?, ?, ?, ?, ?, ?, ?)", [ind, guid, image, name, artist, url, position]);
66 if (rs.rowsAffected > 0) {
76 if (rs.rowsAffected > 0) {
67 console.log("[LOG]: QUEUE add OK")
77 console.log("[LOG]: QUEUE add OK")
68 console.log("[LOG]: URL Added to queue: " + url)
78 console.log("[LOG]: URL Added to queue: " + url)
@@ -103,6 +113,7 @@ function lookup(source) {
103 artist: "",
113 artist: "",
104 image: "",
114 image: "",
105 guid: "",
115 guid: "",
116+ position: 0,
106 }
117 }
107
118
108 db.transaction(function(tx) {
119 db.transaction(function(tx) {
@@ -114,6 +125,7 @@ function lookup(source) {
114 meta.artist = episode.artist
125 meta.artist = episode.artist
115 meta.image = episode.image
126 meta.image = episode.image
116 meta.guid = episode.guid
127 meta.guid = episode.guid
128+ meta.position = episode.position
117 break
129 break
118 }
130 }
119 }
131 }
app/ui/EpisodesPage.qml+2−2
@@ -234,7 +234,7 @@ Page {
234 var index = episodeList.ViewItems.selectedIndices[i]
234 var index = episodeList.ViewItems.selectedIndices[i]
235 if (episodeModel.get(index).audiourl) {
235 if (episodeModel.get(index).audiourl) {
236 var url = episodeModel.get(index).downloadedfile ? "file://" + episodeModel.get(index).downloadedfile : episodeModel.get(index).audiourl
236 var url = episodeModel.get(index).downloadedfile ? "file://" + episodeModel.get(index).downloadedfile : episodeModel.get(index).audiourl
237− player.addEpisodeToQueue(episodeModel.get(index).guid, episodeModel.get(index).image, episodeModel.get(index).name, episodeModel.get(index).artist, url)
237+ player.addEpisodeToQueue(episodeModel.get(index).guid, episodeModel.get(index).image, episodeModel.get(index).name, episodeModel.get(index).artist, url, episodeModel.get(index).position)
238 }
238 }
239 }
239 }
240
240
@@ -646,7 +646,7 @@ Page {
646 text: i18n.tr("Add to playlist")
646 text: i18n.tr("Add to playlist")
647 onTriggered: {
647 onTriggered: {
648 var url = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl
648 var url = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl
649− player.addEpisodeToQueue(model.guid, model.image, model.name, model.artist, url)
649+ player.addEpisodeToQueue(model.guid, model.image, model.name, model.artist, url, model.position)
650 }
650 }
651 },
651 },
652
652
app/ui/EpisodesTab.qml+25−24
@@ -275,7 +275,7 @@ Tab {
275 var index = episodeList.ViewItems.selectedIndices[i]
275 var index = episodeList.ViewItems.selectedIndices[i]
276 if (episodesModel.get(index).audiourl) {
276 if (episodesModel.get(index).audiourl) {
277 var url = episodesModel.get(index).downloadedfile ? "file://" + episodesModel.get(index).downloadedfile : episodesModel.get(index).audiourl
277 var url = episodesModel.get(index).downloadedfile ? "file://" + episodesModel.get(index).downloadedfile : episodesModel.get(index).audiourl
278− player.addEpisodeToQueue(episodesModel.get(index).guid, episodesModel.get(index).image, episodesModel.get(index).name, episodesModel.get(index).artist, url)
278+ player.addEpisodeToQueue(episodesModel.get(index).guid, episodesModel.get(index).image, episodesModel.get(index).name, episodesModel.get(index).artist, url, episodesModel.get(index).position)
279 }
279 }
280 }
280 }
281
281
@@ -578,6 +578,26 @@ Tab {
578
578
579 trailingActions: ListItemActions {
579 trailingActions: ListItemActions {
580 actions: [
580 actions: [
581+ Action {
582+ iconName: model.listened ? "view-collapse" : "select"
583+ onTriggered: {
584+ var db = Podcasts.init();
585+ db.transaction(function (tx) {
586+ if (model.listened) {
587+ tx.executeSql("UPDATE Episode SET listened=0 WHERE guid=?", [model.guid])
588+ episodesModel.setProperty(model.index, "listened", 0)
589+ }
590+ else {
591+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
592+ episodesModel.setProperty(model.index, "listened", 1)
593+ if (episodesPageHeaderSections.selectedIndex === 0) {
594+ episodesModel.remove(model.index, 1)
595+ }
596+ }
597+ });
598+ }
599+ },
600+
581 Action {
601 Action {
582 iconName: model.downloadedfile ? "delete" : (model.queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
602 iconName: model.downloadedfile ? "delete" : (model.queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
583 onTriggered: {
603 onTriggered: {
@@ -605,31 +625,11 @@ Tab {
605 }
625 }
606 },
626 },
607
627
608− Action {
609− iconName: model.listened ? "view-collapse" : "select"
610− onTriggered: {
611− var db = Podcasts.init();
612− db.transaction(function (tx) {
613− if (model.listened) {
614− tx.executeSql("UPDATE Episode SET listened=0 WHERE guid=?", [model.guid])
615− episodesModel.setProperty(model.index, "listened", 0)
616− }
617− else {
618− tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
619− episodesModel.setProperty(model.index, "listened", 1)
620− if (episodesPageHeaderSections.selectedIndex === 0) {
621− episodesModel.remove(model.index, 1)
622− }
623− }
624− });
625− }
626− },
627−
628 Action {
628 Action {
629 iconName: "add-to-playlist"
629 iconName: "add-to-playlist"
630 onTriggered: {
630 onTriggered: {
631 var url = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl
631 var url = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl
632− player.addEpisodeToQueue(model.guid, model.image, model.name, model.artist, url)
632+ player.addEpisodeToQueue(model.guid, model.image, model.name, model.artist, url, model.position)
633 }
633 }
634 },
634 },
635
635
@@ -669,8 +669,9 @@ Tab {
669 selected = !selected
669 selected = !selected
670 } else {
670 } else {
671 if (currentGuid !== model.guid) {
671 if (currentGuid !== model.guid) {
672+ player.savePosition()
672− currentUrl = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl;
673+ currentUrl = model.downloadedfile ? "file://" + model.downloadedfile : model.audiourl
673− player.playEpisode(model.guid, model.image, model.name, model.artist, currentUrl)
674+ player.playEpisode(model.guid, model.image, model.name, model.artist, currentUrl, model.position)
674 }
675 }
675 }
676 }
676 }
677 }
app/ui/FullPlayingView.qml+9−3
@@ -181,7 +181,10 @@ Item {
181 anchors.verticalCenter: parent.verticalCenter
181 anchors.verticalCenter: parent.verticalCenter
182 enabled: player.playlist.canGoPrevious
182 enabled: player.playlist.canGoPrevious
183 opacity: enabled ? 1.0 : 0.4
183 opacity: enabled ? 1.0 : 0.4
184+ onClicked: {
185+ player.savePosition()
184− onClicked: player.playlist.previous()
186+ player.playlist.previous()
187+ }
185
188
186 Icon {
189 Icon {
187 id: mediaBackwardIcon
190 id: mediaBackwardIcon
@@ -233,7 +236,7 @@ Item {
233 width: units.gu(10)
236 width: units.gu(10)
234 height: width
237 height: width
235 opacity: playButton.pressed ? 0.4 : 1.0
238 opacity: playButton.pressed ? 0.4 : 1.0
236− onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play()
239+ onClicked: player.toggle()
237
240
238 Icon {
241 Icon {
239 id: playIcon
242 id: playIcon
@@ -288,7 +291,10 @@ Item {
288 anchors.verticalCenter: parent.verticalCenter
291 anchors.verticalCenter: parent.verticalCenter
289 enabled: player.playlist.canGoNext
292 enabled: player.playlist.canGoNext
290 opacity: enabled ? 1.0 : 0.4
293 opacity: enabled ? 1.0 : 0.4
294+ onClicked: {
295+ player.savePosition()
291− onClicked: player.playlist.next()
296+ player.playlist.next()
297+ }
292
298
293 Icon {
299 Icon {
294 id: mediaForwardIcon
300 id: mediaForwardIcon
app/ui/PlayerControls.qml+1−2
@@ -97,8 +97,7 @@ Rectangle {
97 visible: playButton.pressed
97 visible: playButton.pressed
98 }
98 }
99
99
100− onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause()
100+ onClicked: player.toggle()
101− : player.play()
102
101
103 Icon {
102 Icon {
104 color: "white"
103 color: "white"
app/ui/Queue.qml+1
@@ -83,6 +83,7 @@ ListView {
83 if (player.playlist.currentIndex === index) {
83 if (player.playlist.currentIndex === index) {
84 player.toggle()
84 player.toggle()
85 } else {
85 } else {
86+ player.savePosition()
86 player.playlist.currentIndex = index
87 player.playlist.currentIndex = index
87 }
88 }
88 }
89 }
backend/modules/Podbird/filemanager.cpp+1−6
@@ -27,12 +27,7 @@ FileManager::FileManager(QObject *parent):
27 QObject(parent),
27 QObject(parent),
28 m_podcastDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QDir::separator() + "podcasts")
28 m_podcastDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QDir::separator() + "podcasts")
29{
29{
30− // Remove any downloads that haven't been collected previously (because we were closed before they finished)
30+
31− QString downloadManagerPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "ubuntu-download-manager" + QDir::separator() + "com.mikeasoft.podbird";
32− QDir downloadDir(downloadManagerPath);
33− if (downloadDir.exists()) {
34− downloadDir.removeRecursively();
35− }
36}
31}
37
32
38FileManager::~FileManager() {
33FileManager::~FileManager() {