Merge lp:~ahayzen/music-app/queue-refactor into lp:music-app/trusty

Proposed by Andrew Hayzen
Status: Merged
Approved by: Daniel Holm
Approved revision: 80
Merged at revision: 78
Proposed branch: lp:~ahayzen/music-app/queue-refactor
Merge into: lp:music-app/trusty
Diff against target: 568 lines (+146/-157)
9 files modified
LibraryListModel.qml (+26/-0)
LoginLastFM.qml (+0/-1)
MusicAlbums.qml (+1/-2)
MusicArtists.qml (+1/-1)
MusicPlaylists.qml (+2/-9)
MusicSettings.qml (+0/-1)
MusicTracks.qml (+4/-12)
music-app.qml (+112/-70)
playing-list.js (+0/-61)
To merge this branch: bzr merge lp:~ahayzen/music-app/queue-refactor
Reviewer Review Type Date Requested Status
Daniel Holm Approve
Victor Thompson Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+177491@code.launchpad.net

Commit message

- trackQueue is now used for the queue, with currentIndex as the current
position in the trackQueue
- LibraryListModel can now saves the current query
- addQueueFromModel has been added to build the trackQueue from a
LibraryListModel
- trackClicked has been refactored
- updateMeta has been added to update the metadata of the current track and is trigged onSourceChange from the player
- currentCover is now set from Library.hasCover so it works with all
pages/models
- playing-list.js has been removed as it is not used anymore
- Various code cleanups

Description of the change

Major changes to the queuing system, playingList has been removed and the whole queue works through the model trackQueue.
The current query of the LibraryListModel is saved and can then be queried to load the trackQueue, this fixes bug 1204293.
The updateMeta is triggered onSourceChange of the player and is retrieved from the trackQueue and database, hopefully resolving bug 1200393.

Please test this merge thoroughly to ensure no regressions.

To post a comment you must log in.
Revision history for this message
John Kim (kotux) wrote :

Hello,

How can I test this feature on my Nexus 7 grouper? I installed today's build, but the music app doesn't function properly. For instance, when I open the music app, I scroll through the songs, but the moment I tap a song or a button (play or fastforward), the app freezes. It does this regardless of how many times I force quit the app.

Andrew Hayzen <email address hidden>이 씀:
>Andrew Hayzen has proposed merging
>lp:~andrew-hayzen/music-app/queue-refactor into lp:music-app.
>
>Commit message:
>- trackQueue is now used for the queue, with currentIndex as the
>current
>position in the trackQueue
>- LibraryListModel can now saves the current query
>- addQueueFromModel has been added to build the trackQueue from a
>LibraryListModel
>- trackClicked has been refactored
>- updateMeta has been added to update the metadata of the current track
>and is trigged onSourceChange from the player
>- currentCover is now set from Library.hasCover so it works with all
>pages/models
>- playing-list.js has been removed as it is not used anymore
>- Various code cleanups
>
>Requested reviews:
> Music App Developers (music-app-dev)
>Related bugs:
>Bug #1200393 in Ubuntu Music App: "Shuffling doesn't display the
>correct song"
> https://bugs.launchpad.net/music-app/+bug/1200393
>Bug #1204293 in Ubuntu Music App: "PlayingList sometimes isn't fully
>populated causing Shuffle not to work correctly"
> https://bugs.launchpad.net/music-app/+bug/1204293
>
>For more details, see:
>https://code.launchpad.net/~andrew-hayzen/music-app/queue-refactor/+merge/177491
>
>Major changes to the queuing system, playingList has been removed and
>the whole queue works through the model trackQueue.
>The current query of the LibraryListModel is saved and can then be
>queried to load the trackQueue, this fixes bug 1204293.
>The updateMeta is triggered onSourceChange of the player and is
>retrieved from the trackQueue and database, hopefully resolving bug
>1200393.
>
>Please test this merge thoroughly to ensure no regressions.
>
>--
>https://code.launchpad.net/~andrew-hayzen/music-app/queue-refactor/+merge/177491
>You are subscribed to branch lp:music-app.

--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) :
review: Approve
Revision history for this message
Daniel Holm (danielholm) wrote :

Impressive work, Andrew!

John (thinkndev) I created a bleeding edge PPA that we could use to submit the absolutely latest changes to and that can be used on the device: https://code.launchpad.net/~danielholm/+archive/music-app-bleeding

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'LibraryListModel.qml'
2--- LibraryListModel.qml 2013-07-27 15:16:00 +0000
3+++ LibraryListModel.qml 2013-07-29 22:57:27 +0000
4@@ -23,6 +23,8 @@
5 Item {
6 property ListModel model : ListModel { id: libraryModel }
7 property alias count: libraryModel.count
8+ property var query: null
9+ property var param: null
10
11 WorkerScript {
12 id: worker
13@@ -51,6 +53,10 @@
14 libraryModel.clear();
15 console.log("called LibraryListModel::populate()")
16
17+ // Save query for queue
18+ query = Library.getAll
19+ param = null
20+
21 var library = Library.getAll()
22
23 for ( var key in library ) {
24@@ -64,6 +70,10 @@
25 libraryModel.clear();
26 console.log("called LibraryListModel::filterArtists()")
27
28+ // Save query for queue
29+ query = Library.getArtists
30+ param = null
31+
32 var library = Library.getArtists()
33
34 for ( var key in library ) {
35@@ -80,6 +90,10 @@
36 // libraryModel.clear();
37 console.log("called LibraryListModel::filterArtistTracks()")
38
39+ // Save query for queue
40+ query = Library.getArtistTracks
41+ param = artist
42+
43 var library = Library.getArtistTracks(artist)
44
45 for ( var key in library ) {
46@@ -93,6 +107,10 @@
47 libraryModel.clear();
48 console.log("called LibraryListModel::filterAlbums()")
49
50+ // Save query for queue
51+ query = Library.getAlbums
52+ param = null
53+
54 var library = Library.getAlbums()
55
56 for ( var key in library ) {
57@@ -109,6 +127,10 @@
58 // libraryModel.clear();
59 console.log("called LibraryListModel::filterAlbumTracks()")
60
61+ // Save query for queue
62+ query = Library.getAlbumTracks
63+ param = album
64+
65 var library = Library.getAlbumTracks(album)
66
67 for ( var key in library ) {
68@@ -122,6 +144,10 @@
69 // libraryModel.clear(); TODO: see filterAlbumTracks(album)
70 console.log("called LibraryListModel::filterPlaylistTracks()")
71
72+ // Save query for queue
73+ query = Playlists.getPlaylistTracks
74+ param = playlist
75+
76 var tracks = Playlists.getPlaylistTracks(playlist)
77
78 for ( var key in tracks ) {
79
80=== modified file 'LoginLastFM.qml'
81--- LoginLastFM.qml 2013-07-09 19:35:57 +0000
82+++ LoginLastFM.qml 2013-07-29 22:57:27 +0000
83@@ -26,7 +26,6 @@
84 import QtQuick.XmlListModel 2.0
85 import "settings.js" as Settings
86 import "meta-database.js" as Library
87-import "playing-list.js" as PlayingList
88 import "scrobble.js" as Scrobble
89
90 // LastFM login dialog
91
92=== modified file 'MusicAlbums.qml'
93--- MusicAlbums.qml 2013-07-25 22:45:57 +0000
94+++ MusicAlbums.qml 2013-07-29 22:57:27 +0000
95@@ -25,7 +25,6 @@
96 import QtQuick.LocalStorage 2.0
97 import "settings.js" as Settings
98 import "meta-database.js" as Library
99-import "playing-list.js" as PlayingList
100 import "playlists.js" as Playlists
101
102 PageStack {
103@@ -225,7 +224,7 @@
104 focus = true
105 }
106
107- trackClicked(file, index, albumTracksModel.model, albumtrackslist)
108+ trackClicked(albumTracksModel, index) // play track
109 }
110 }
111 Component.onCompleted: {
112
113=== modified file 'MusicArtists.qml'
114--- MusicArtists.qml 2013-07-25 22:45:57 +0000
115+++ MusicArtists.qml 2013-07-29 22:57:27 +0000
116@@ -201,7 +201,7 @@
117 focus = true
118 }
119
120- trackClicked(file, index, artistTracksModel.model, artisttrackslist)
121+ trackClicked(artistTracksModel, index) // play track
122 }
123 }
124 Component.onCompleted: {
125
126=== modified file 'MusicPlaylists.qml'
127--- MusicPlaylists.qml 2013-07-27 15:16:00 +0000
128+++ MusicPlaylists.qml 2013-07-29 22:57:27 +0000
129@@ -27,7 +27,6 @@
130 import "settings.js" as Settings
131 import "meta-database.js" as Library
132 import "scrobble.js" as Scrobble
133-import "playing-list.js" as PlayingList
134 import "playlists.js" as Playlists
135
136 PageStack {
137@@ -234,9 +233,6 @@
138 onCurrentIndexChanged: {
139 customdebug("tracklist.currentIndex = " + playlistslist.currentIndex)
140 }
141- onModelChanged: {
142- customdebug("PlayingList cleared")
143- }
144
145 Component {
146 id: playlistDelegate
147@@ -377,9 +373,6 @@
148 onCurrentIndexChanged: {
149 console.log("Tracks in playlist tracklist.currentIndex = " + playlistlist.currentIndex)
150 }
151- onModelChanged: {
152- console.log("PlayingList cleared")
153- }
154
155 Component {
156 id: playlisttrackDelegate
157@@ -408,7 +401,7 @@
158 }
159 onClicked: {
160 customdebug("File: " + file) // debugger
161- trackClicked(file, index, playlisttracksModel.model, playlistlist) // play track
162+ trackClicked(playlisttracksModel, index) // play track
163 }
164 }
165 }
166@@ -485,7 +478,7 @@
167 }
168 onClicked: {
169 customdebug("File: " + file) // debugger
170- trackClicked(file, index, trackQueue.model, queuelist) // play track
171+ trackClicked(trackQueue, index) // play track
172 }
173
174 /*onItemRemoved: {
175
176=== modified file 'MusicSettings.qml'
177--- MusicSettings.qml 2013-07-09 19:35:57 +0000
178+++ MusicSettings.qml 2013-07-29 22:57:27 +0000
179@@ -24,7 +24,6 @@
180 import "scrobble.js" as Scrobble
181 import "playlists.js" as Playlists
182 import "meta-database.js" as Library
183-import "playing-list.js" as PlayingList
184
185 Dialog {
186 id: root
187
188=== modified file 'MusicTracks.qml'
189--- MusicTracks.qml 2013-07-25 22:45:57 +0000
190+++ MusicTracks.qml 2013-07-29 22:57:27 +0000
191@@ -25,7 +25,6 @@
192 import QtQuick.LocalStorage 2.0
193 import "settings.js" as Settings
194 import "meta-database.js" as Library
195-import "playing-list.js" as PlayingList
196 import "playlists.js" as Playlists
197
198
199@@ -160,22 +159,15 @@
200 focus = true
201 }
202
203- trackClicked(file, index, libraryModel.model, tracklist)
204+ trackClicked(libraryModel, index) // play track
205 }
206 }
207 Component.onCompleted: {
208- if (PlayingList.size() === 0) {
209- player.source = file
210- currentModel = libraryModel.model
211- currentListView = tracklist
212- currentIndex = 0
213+ // Set first track as current track
214+ if (trackQueue.model.count === 0) {
215+ trackClicked(libraryModel, index, false)
216 }
217
218- if (!PlayingList.contains(file)) {
219- console.log("Adding file:" + file)
220- PlayingList.addItem(file, itemnum)
221- console.log(itemnum)
222- }
223 console.log("Title:" + title + " Artist: " + artist)
224 }
225 }
226
227=== modified file 'music-app.qml'
228--- music-app.qml 2013-07-27 15:16:00 +0000
229+++ music-app.qml 2013-07-29 22:57:27 +0000
230@@ -26,7 +26,6 @@
231 import QtQuick.XmlListModel 2.0
232 import "settings.js" as Settings
233 import "meta-database.js" as Library
234-import "playing-list.js" as PlayingList
235 import "scrobble.js" as Scrobble
236 import "playlists.js" as Playlists
237
238@@ -79,9 +78,7 @@
239 property string musicName: i18n.tr("Music")
240 property string musicDir: ""
241 property string appVersion: '0.4.7'
242- property int playing: 0
243 property bool isPlaying: false
244- property int itemnum: 0
245 property bool random: false
246 property bool scrobble: false
247 property string lastfmusername
248@@ -98,40 +95,19 @@
249 property string currentTracktitle: ""
250 property string currentFile: ""
251 property int currentIndex: -1
252- property ListView currentListView: null
253- property ListModel currentModel: null // Current model being used
254- property int currentModelCount: -1
255+ property LibraryListModel currentModel: null // Current model being used
256+ property var currentQuery: null
257+ property var currentParam: null
258 property string currentCover: ""
259 property string currentCoverSmall: currentCover === "" ?
260 "images/cover_default_icon.png" :
261- "image://cover-art/" + currentFile
262+ "image://cover-art/" + currentCover
263 property string currentCoverFull: currentCover !== "" ?
264- "image://cover-art-full/" + currentFile :
265+ "image://cover-art-full/" + currentCover :
266 "images/cover_default.png"
267
268 signal onPlayingTrackChange(string source)
269
270- onCurrentIndexChanged: {
271- // Index change most likely from next/pre button press or end of song
272-
273- currentListView.currentIndex = currentIndex // update highlight in listview
274-
275- // Load metadata for the track
276- currentArtist = currentModel.get(currentIndex).artist
277- currentAlbum = currentModel.get(currentIndex).album
278- currentFile = currentModel.get(currentIndex).file
279- currentCover = currentModel.get(currentIndex).cover
280- currentTracktitle = currentModel.get(currentIndex).title
281- }
282-
283- onCurrentModelChanged: {
284- PlayingList.rebuild(currentModel)
285- }
286-
287- onCurrentModelCountChanged: {
288- PlayingList.rebuild(currentModel)
289- }
290-
291 // FUNCTIONS
292
293 // Custom debug funtion that's easier to shut off
294@@ -155,45 +131,36 @@
295 if (random) {
296 var now = new Date();
297 var seed = now.getSeconds();
298+
299+ // trackQueue must be above 1 otherwise an infinite loop will occur
300 do {
301- var num = (Math.floor((PlayingList.size()) * Math.random(seed)));
302+ var num = (Math.floor((trackQueue.model.count) * Math.random(seed)));
303 console.log(num)
304- console.log(playing)
305- } while (num == playing && PlayingList.size() > 0)
306- player.source = Qt.resolvedUrl(PlayingList.getList()[num])
307- currentIndex = PlayingList.at(num)
308- playing = num
309+ } while (num == currentIndex && trackQueue.model.count > 1)
310+ currentIndex = num
311+ player.source = Qt.resolvedUrl(trackQueue.model.get(num).file)
312 console.log("MediaPlayer statusChanged, currentIndex: " + currentIndex)
313 } else {
314- if ((playing < PlayingList.size() - 1 && direction === 1 )
315- || (playing > 0 && direction === -1)) {
316- console.log("playing: " + playing)
317- console.log("filelistCount: " + currentModelCount)
318- console.log("PlayingList.size(): " + PlayingList.size())
319- playing += direction
320- if (playing === 0) {
321- currentIndex = playing + (itemnum - PlayingList.size())
322- } else {
323- currentIndex += direction
324- }
325- player.source = Qt.resolvedUrl(PlayingList.getList()[playing])
326+ if ((currentIndex < trackQueue.model.count - 1 && direction === 1 )
327+ || (currentIndex > 0 && direction === -1)) {
328+ console.log("currentIndex: " + currentIndex)
329+ console.log("trackQueue.count: " + trackQueue.model.count)
330+ currentIndex += direction
331+ player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
332 } else if(direction === 1) {
333- console.log("playing: " + playing)
334- console.log("filelistCount: " + currentModelCount)
335- console.log("PlayingList.size(): " + PlayingList.size())
336- playing = 0
337- currentIndex = playing + (currentModelCount - PlayingList.size())
338- player.source = Qt.resolvedUrl(PlayingList.getList()[playing])
339+ console.log("currentIndex: " + currentIndex)
340+ console.log("trackQueue.count: " + trackQueue.model.count)
341+ currentIndex = 0
342+ player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
343 } else if(direction === -1) {
344- console.log("playing: " + playing)
345- console.log("filelistCount: " + currentModelCount)
346- console.log("PlayingList.size(): " + PlayingList.size())
347- playing = PlayingList.size() - 1
348- currentIndex = playing + (currentModelCount - PlayingList.size())
349- player.source = Qt.resolvedUrl(PlayingList.getList()[playing])
350+ console.log("currentIndex: " + currentIndex)
351+ console.log("trackQueue.count: " + trackQueue.model.count)
352+ currentIndex = trackQueue.model.count - 1
353+ player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
354 }
355 console.log("MediaPlayer statusChanged, currentIndex: " + currentIndex)
356 }
357+ player.stop() // Add stop so that if same song is selected it restarts
358 console.log("Playing: "+player.source)
359 player.play()
360 timestamp = new Date().getTime(); // contains current date and time in Unix time, used to scrobble
361@@ -206,12 +173,49 @@
362 }
363 }
364
365- function trackClicked(file, index, libraryModel, listView)
366- {
367+ // Add items from a stored query in libraryModel into the queue
368+ function addQueueFromModel(libraryModel)
369+ {
370+ var items;
371+
372+ if (libraryModel.query === null)
373+ {
374+ return
375+ }
376+
377+ if (libraryModel.param === null)
378+ {
379+ items = libraryModel.query()
380+ }
381+ else
382+ {
383+ items = libraryModel.query(libraryModel.param)
384+ }
385+
386+ for (var key in items)
387+ {
388+ trackQueue.model.append(items[key])
389+ }
390+ }
391+
392+ function trackClicked(libraryModel, index, play)
393+ {
394+ if (play === undefined)
395+ {
396+ play = true
397+ }
398+
399+ var file = libraryModel.model.get(index).file
400+
401 console.debug(player.source, Qt.resolvedUrl(file))
402
403 if (player.source == Qt.resolvedUrl(file)) // same file different pages what should happen then?
404 {
405+ if (play === false)
406+ {
407+ return
408+ }
409+
410 console.log("Is current track: "+player.playbackState)
411
412 if (player.playbackState == MediaPlayer.PlayingState)
413@@ -226,25 +230,63 @@
414 return
415 }
416
417- currentListView = listView
418+ // Clear the play queue and load the new tracks - if not trackQueue
419+ if (libraryModel !== trackQueue)
420+ {
421+ // Don't reload queue if model, query and parameters are the same
422+ if (currentModel !== libraryModel ||
423+ currentQuery !== libraryModel.query ||
424+ currentParam !== libraryModel.param)
425+ {
426+ trackQueue.model.clear()
427+ addQueueFromModel(libraryModel)
428+ }
429+ }
430+
431+ // Current index must be updated before player.source
432 currentModel = libraryModel
433- currentModelCount = libraryModel.count
434- currentIndex = index // update index after model so index can get info from model
435+ currentQuery = libraryModel.query
436+ currentParam = libraryModel.param
437+ currentIndex = trackQueue.indexOf(file)
438
439 console.log("Click of fileName: " + file)
440
441- player.stop()
442+ if (play === true)
443+ {
444+ player.stop()
445+ }
446+
447 player.source = Qt.resolvedUrl(file)
448- player.play()
449
450- playing = PlayingList.indexOf(file)
451+ if (play === true)
452+ {
453+ player.play()
454+ }
455
456 console.log("Source: " + player.source.toString())
457- console.log("Index: " + index)
458+ console.log("Index: " + currentIndex)
459
460 return file
461 }
462
463+ function updateMeta()
464+ {
465+ // Load metadata for the track
466+ currentArtist = trackQueue.model.get(currentIndex).artist
467+ currentAlbum = trackQueue.model.get(currentIndex).album
468+ currentTracktitle = trackQueue.model.get(currentIndex).title
469+
470+ // hasCover and currentCover require no file://
471+ var file = currentFile
472+
473+ if (file.indexOf("file://") == 0)
474+ {
475+ file = file.slice(7, file.length)
476+ }
477+
478+ currentCover = Library.hasCover(file) ? file : ""
479+ }
480+
481 MediaPlayer {
482 id: player
483 objectName: "player"
484@@ -257,7 +299,9 @@
485 property string positionStr: "00:00"
486
487 onSourceChanged: {
488+ currentFile = source
489 onPlayingTrackChange(source)
490+ updateMeta()
491 }
492
493 onStatusChanged: {
494@@ -459,8 +503,6 @@
495 libraryModel.populate()
496 albumModel.filterAlbums()
497 artistModel.filterArtists()
498- PlayingList.clear()
499- itemnum = 0
500 timer.stop()
501 }
502 counted = filelist.count
503
504=== removed file 'playing-list.js'
505--- playing-list.js 2013-07-18 21:57:52 +0000
506+++ playing-list.js 1970-01-01 00:00:00 +0000
507@@ -1,61 +0,0 @@
508-/*
509- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
510- * Daniel Holm <d.holmen@gmail.com>
511- *
512- * This program is free software; you can redistribute it and/or modify
513- * it under the terms of the GNU General Public License as published by
514- * the Free Software Foundation; version 3.
515- *
516- * This program is distributed in the hope that it will be useful,
517- * but WITHOUT ANY WARRANTY; without even the implied warranty of
518- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
519- * GNU General Public License for more details.
520- *
521- * You should have received a copy of the GNU General Public License
522- * along with this program. If not, see <http://www.gnu.org/licenses/>.
523- */
524-
525-.pragma library
526-var myArray = new Array()
527-var myLocation = new Array()
528-
529-function getList() {
530- return myArray
531-}
532-
533-function at(index) {
534- return myLocation[index]
535-}
536-
537-function addItem(item, index) {
538- myArray.push(item)
539- myLocation.push(index)
540-}
541-
542-function contains(item) {
543- return myArray.indexOf(item) !== -1
544-}
545-
546-function indexOf(item) {
547- return myArray.indexOf(item)
548-}
549-
550-function size() {
551- return myArray.length
552-}
553-
554-function clear() {
555- myArray = []
556- myLocation = []
557-}
558-
559-function rebuild(currentModel)
560-{
561- console.debug("Clearing playing list")
562- clear()
563-
564- for (var i=0; i < currentModel.count; i++)
565- {
566- addItem(currentModel.get(i).file, i)
567- }
568-}

Subscribers

People subscribed via source and target branches

to status/vote changes: