Merge lp:~music-app-dev/music-app/use-mediascanner2.0 into lp:music-app/trusty

Proposed by Victor Thompson on 2014-04-04
Status: Merged
Approved by: Nicholas Skaggs on 2014-06-19
Approved revision: 528
Merged at revision: 496
Proposed branch: lp:~music-app-dev/music-app/use-mediascanner2.0
Merge into: lp:music-app/trusty
Diff against target: 4287 lines (+1018/-1352)
34 files modified
LibraryListModel.qml (+16/-89)
LoginLastFM.qml (+0/-1)
MusicAlbums.qml (+26/-22)
MusicArtists.qml (+57/-14)
MusicNowPlaying.qml (+12/-12)
MusicPlaylists.qml (+4/-3)
MusicSearch.qml (+22/-27)
MusicStart.qml (+76/-33)
MusicToolbar.qml (+4/-4)
MusicTracks.qml (+28/-17)
MusicaddtoPlaylist.qml (+8/-7)
Player.qml (+10/-11)
Style.qml (+4/-3)
click/apparmor.json (+1/-1)
click/manifest.json.in (+2/-3)
common/AlbumsSheet.qml (+52/-25)
common/BlurredBackground.qml (+9/-9)
common/CoverRow.qml (+7/-3)
common/Expander.qml (+4/-3)
common/SongsSheet.qml (+37/-21)
debian/control (+7/-6)
meta-database.js (+8/-380)
music-app.qml (+139/-375)
playlists.js (+6/-7)
plugins.json (+0/-5)
po/com.ubuntu.music.pot (+68/-76)
po/lv.po (+2/-1)
tests/autopilot/music_app/__init__.py (+1/-1)
tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql (+39/-0)
tests/autopilot/music_app/content/mediascanner/mediaindex (+0/-10)
tests/autopilot/music_app/emulators.py (+21/-2)
tests/autopilot/music_app/tests/__init__.py (+242/-52)
tests/autopilot/music_app/tests/test_music.py (+102/-126)
worker-library-loader.js (+4/-3)
To merge this branch: bzr merge lp:~music-app-dev/music-app/use-mediascanner2.0
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve on 2014-06-19
David Planella 2014-04-04 Needs Information on 2014-05-13
Review via email: mp+214140@code.launchpad.net

Commit message

* Refactor app to use new Media Scanner 2.0 qml bindings and database

Description of the change

I propose we review and merge into this branch as we develop and test against the mediascanner2.0[1] code.

To install this use the typical cmake and 'sudo make install' as you normally would. I had some difficulties satisfying the cmake prerequisites. I should have written down everything I had to install, but I did not. You'll need to just walk through the CMakeLists.txt as you search for possible candidate packages in apt-get. If someone else could capture what needs to be installed that'd be great.

The best description of the currently available components and types [2] should be pretty complete. This needs to be reviewed as well, so we can provide feedback to the mediascanner team. If you can not install and test with the code, please at least review the mediascanner2.0 API and code to target any deficiencies early on.

[1] https://launchpad.net/mediascanner2
[2] http://bazaar.launchpad.net/~mediascanner-team/mediascanner2/trunk/view/head:/src/qml/Ubuntu/MediaScanner/plugin.qmltypes

To post a comment you must log in.
Nicholas Skaggs (nskaggs) wrote :

Commenting here so I don't lose this. Once the plugin is gone, we can make use of the on device testing for mp's (because we'll have a proper click package that includes all the plugins)

Andrew Hayzen (ahayzen) wrote :

Note for us and others watching :)

From a app/ui point of view, other than missing the genre section on the start page [0], this is ready.
However now the autopilot tests need to be rewritten to work with mediascanner2.

As a note currently you will need to install qtdeclarative5-ubuntu-mediascanner0.1 on device.

A further enhancement which would be nice to have before landing is the ability to set a fallback album art but this requires changes to mediascanner/thumbnailer.

0 - For us to implement genres on the start screen we need GenresModel {} and filtering by a genre within the AlbumsModel and SongsModel.

Victor Thompson (vthompson) wrote :

Currently there's an issue where the Thumbnailer is not working because it is not in the app's apparmor profile. Additionally, the error this causes prevents AP from being run. Once this issue is figured out r450 can be reverted.

David Planella (dpm) wrote :

Hi Victor, thanks for figuring this one out. In terms of the error, is there a bug filed in Thumbnailer that we can track? Are the mediascanner folks aware of this issue?

review: Needs Information
Victor Thompson (vthompson) wrote :

No bug at the moment that I'm aware of. It was suggested on IRC that I ask jdstrand today to see what might be needed.

Victor Thompson (vthompson) wrote :

This is likely the bug to track that is blocking app confinement with Thumbnailer: lp:1303962

452. By Andrew Hayzen on 2014-05-20

* Merge of trunk

453. By Victor Thompson on 2014-05-20

* Initial test updates for mediascanner migration.

454. By Victor Thompson on 2014-05-20

Merge trunk

455. By Victor Thompson on 2014-05-20

Fix pyflakes error

456. By Victor Thompson on 2014-05-20

Fix bad merge conflict resolution

457. By Victor Thompson on 2014-05-20

empty

458. By Victor Thompson on 2014-05-20

Fix unused variable

459. By Victor Thompson on 2014-05-21

Merge trunk

460. By Victor Thompson on 2014-05-21

Update test_play_pause_library test

461. By Andrew Hayzen on 2014-05-22

* Fix for test_play_pause_library touching indicators

462. By Victor Thompson on 2014-05-22

Find new customBackButton

463. By Victor Thompson on 2014-05-22

Patch db

464. By Andrew Hayzen on 2014-05-22

* Fixes for Pep8

465. By Victor Thompson on 2014-05-22

Patch SQLite DB.

466. By Victor Thompson on 2014-05-22

* Fix play button on device
* Fix check for playing

467. By Victor Thompson on 2014-05-22

Test for only backButton

468. By Andrew Hayzen on 2014-05-22

* Use sortFilterModel to correctly sort Albums and Tracks
* Fix issue of blank album art in recent
* Correctly drop metadata table

469. By Andrew Hayzen on 2014-05-23

* Move all model.art/model.cover to use model.artist and model.album to construct a image:// url

470. By Andrew Hayzen on 2014-05-23

* Fix for incorrect tag for artist in image://

471. By Victor Thompson on 2014-05-23

Re-write test_play_pause_library test

472. By Victor Thompson on 2014-05-23

Use new 14.10-dev1 framework

473. By Andrew Hayzen on 2014-05-23

* Fix for cover art

474. By Andrew Hayzen on 2014-05-23

* Fix for pyflakes

475. By Andrew Hayzen on 2014-05-23

* Revert framework bump for testing

476. By Andrew Hayzen on 2014-05-23

* Fix for blur background empty at startup

477. By Andrew Hayzen on 2014-05-23

* Bump framework to ubuntu-sdk-14.10-qml-dev1

478. By Victor Thompson on 2014-05-23

* Update app armor for confinement
* Update policy version

479. By Victor Thompson on 2014-05-23

Update copyright

480. By Andrew Hayzen on 2014-05-28

* Fix for patching home_dir

481. By Nicholas Skaggs on 2014-05-29

tweak env patching apparmor setup

482. By Nicholas Skaggs on 2014-05-29

simplify logic per thomi for local_location_qml

483. By Nicholas Skaggs on 2014-05-29

revert local_location tweak

FAILED: Continuous integration, rev:480
http://91.189.93.70:8080/job/music-app-ci/826/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/50

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/826/rebuild

review: Needs Fixing (continuous-integration)
484. By Andrew Hayzen on 2014-05-30

* Readd genre support

FAILED: Continuous integration, rev:484
http://91.189.93.70:8080/job/music-app-ci/829/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/53

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/829/rebuild

review: Needs Fixing (continuous-integration)

PASSED: Continuous integration, rev:484
http://91.189.93.70:8080/job/music-app-ci/831/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/55

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/831/rebuild

review: Approve (continuous-integration)
485. By Andrew Hayzen on 2014-05-30

* Ensure that genre is set to undefined when filtering by album

PASSED: Continuous integration, rev:485
http://91.189.93.70:8080/job/music-app-ci/832/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/56

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/832/rebuild

review: Approve (continuous-integration)
Nicholas Skaggs (nskaggs) wrote :

Just and update on the apparmor info; we are dependent on the apparmor changes for mediascanner which I guess are still being worked? The policy for this to allow testing has gotten quite complex. We may be forced to go back to the old method of backing up files if this has to land before we can get apparmor playing nicely with the tests.

486. By Andrew Hayzen on 2014-05-30

* Merge of trunk

PASSED: Continuous integration, rev:486
http://91.189.93.70:8080/job/music-app-ci/833/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/57

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/833/rebuild

review: Approve (continuous-integration)
487. By Nicholas Skaggs on 2014-05-30

update ap env isolation (mediascanner still will whine)

PASSED: Continuous integration, rev:487
http://91.189.93.70:8080/job/music-app-ci/834/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/58

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/834/rebuild

review: Approve (continuous-integration)
488. By Victor Thompson on 2014-05-31

Add gstreamer dependencies needed for the app to work on Utopic desktop.

PASSED: Continuous integration, rev:488
http://91.189.93.70:8080/job/music-app-ci/835/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/59

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/835/rebuild

review: Approve (continuous-integration)
489. By Andrew Hayzen on 2014-06-03

* Fix to stop genres incorrectly appearing in recent if play all was selected

PASSED: Continuous integration, rev:489
http://91.189.93.70:8080/job/music-app-ci/837/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/61

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/837/rebuild

review: Approve (continuous-integration)
490. By Victor Thompson on 2014-06-03

Merge of trunk

PASSED: Continuous integration, rev:490
http://91.189.93.70:8080/job/music-app-ci/839/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/63

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/839/rebuild

review: Approve (continuous-integration)
491. By Victor Thompson on 2014-06-04

Fix play all of an album from the Artist albums sheet

PASSED: Continuous integration, rev:491
http://91.189.93.70:8080/job/music-app-ci/840/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/64

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/840/rebuild

review: Approve (continuous-integration)
492. By Andrew Hayzen on 2014-06-04

* Add thumbnailer-service as depends for now to resolve issues on desktop

PASSED: Continuous integration, rev:492
http://91.189.93.70:8080/job/music-app-ci/841/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/65

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/841/rebuild

review: Approve (continuous-integration)
Alan Pope 🍺🐧🐱 🦄 (popey) wrote :

Just tested this on my desktop, and when mediascanner-2.0 service isn't running it crashes..

alan@deep-thought:/tmp/use-mediascanner2.0$ qmlscene music-app.qml
unity::action::ActionManager::ActionManager(QObject*):
 Could not determine application identifier. HUD will not work properly.
 Provide your application identifier in $APP_ID environment variable.
Debug: Queue: Now has: 0 tracks
Debug: onCountChanged: 0
terminate called after throwing an instance of 'std::runtime_error'
  what(): org.freedesktop.DBus.Error.Spawn.ChildSignaled: Process /usr/lib/x86_64-linux-gnu/mediascanner-2.0/mediascanner-dbus-2.0 received signal 6
Aborted (core dumped)

When mediascanner-2.0 _is_ running, it also crashes but took a little longer and crashed slightly differently.

alan@deep-thought:/tmp/use-mediascanner2.0$ qmlscene music-app.qml
unity::action::ActionManager::ActionManager(QObject*):
 Could not determine application identifier. HUD will not work properly.
 Provide your application identifier in $APP_ID environment variable.
Debug: Queue: Now has: 0 tracks
Debug: onCountChanged: 0
terminate called after throwing an instance of 'std::runtime_error'
  what(): com.canonical.MediaScanner2.Error: database is locked
Aborted (core dumped)

493. By Nicholas Skaggs on 2014-06-05

revert to using backup and restore for database and Music folders

494. By Nicholas Skaggs on 2014-06-05

fix flake8, also backup mediascanner path

Nicholas Skaggs (nskaggs) wrote :

I left the patching/mocking/fakeenv code for the autopilot tests alone, but changed the setup path to no longer call it. Instead, the mediascanner, Music and music app database directories are now backed up at the start of the test and restored after completion. This is not ideal, but the mocking will require more apparmor policy work, so it is a stopgap for now.

Note, that should a test fail the restore might not work properly. Caveat emptor.

495. By Nicholas Skaggs on 2014-06-05

set /home to be ~/

PASSED: Continuous integration, rev:495
http://91.189.93.70:8080/job/music-app-ci/842/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/music-app-utopic-amd64-ci/66

Click here to trigger a rebuild:
http://91.189.93.70:8080/job/music-app-ci/842/rebuild

review: Approve (continuous-integration)
496. By Nicholas Skaggs on 2014-06-05

wip

review: Needs Fixing (continuous-integration)
497. By Nicholas Skaggs on 2014-06-05

undo the backup/restore. it's just not an option

498. By Andrew Hayzen on 2014-06-06

* Merge of trunk

Andrew Hayzen (ahayzen) wrote :

#blocked mediascanner2 bug 1326753

499. By Victor Thompson on 2014-06-14

Limiting SongsModels to 500 to ensure they at least load

500. By Andrew Hayzen on 2014-06-15

* Hack to limit all SongsModels to 500 tracks

501. By Victor Thompson on 2014-06-18

Merge trunk

Nicholas Skaggs (nskaggs) wrote :

The issues with music no longer mocking are baffling; but music is not alone. Calendar which has a similar implementation, is no longer mocking home properly either. AFAICT, the proper environment variables are being set, and music sees them. Since qml is unable to dump env vars, I used reminders to verify autopilot was patching properly (needed qt and reminders is compiled). It's not 100%, but seems reasonable. If you attempt to get the env var from within a test, it too reflects the proper directory. So it seems practical that the app itself is being launched with the proper env variable set.

For the moment, I'm ignoring the issues present with running and mocking on the phone; the app seemingly still looks in your actual home directory despite being told it's somewhere else.

So, has something else changed that would cause the app to look somewhere else for where HOME is?

Nicholas Skaggs (nskaggs) wrote :

Or perhaps does the app now need it's environment set yet another way, or another variable set?

Victor Thompson (vthompson) wrote :

Personally, I assumed it had to do with dbus changes in mediascanner2. Isn't the app in the store/image mocking? That appears to work. I hate making concessions, but we might want to go back to copying off the Music dir, but in addition to that maybe we could run each test as follows:

1) stop mediascanner2*
2) copy/save user's Music dir
3) move test songs to ~/Music
4) start mediascanner2
5) sleep for a predefined period
6) stop mediascanner2
7) run test
8) restore user's Music dir

*Note that subsequent tests will already have mediascanner2 stopped. This will prevent the service from needlessly rescanning the users music--which could take some time. This would mean that the user should either start mediascanner2 to rebuild their music library to use the app or reboot. Everything should then work.

502. By Victor Thompson on 2014-06-18

Fix destructive tests

review: Needs Fixing (continuous-integration)
503. By Victor Thompson on 2014-06-18

Stop and start mediascanner2 and also wait 10 seconds

review: Needs Fixing (continuous-integration)
504. By Nicholas Skaggs on 2014-06-18

all the hackiness at once

505. By Nicholas Skaggs on 2014-06-18

slightly saner version

review: Needs Fixing (continuous-integration)
506. By Nicholas Skaggs on 2014-06-18

pull out dbus launch

507. By Andrew Hayzen on 2014-06-18

* Add cleanups to autopilot

review: Needs Fixing (continuous-integration)
review: Needs Fixing (continuous-integration)
508. By Nicholas Skaggs on 2014-06-18

new fake db based on schema 6

509. By Nicholas Skaggs on 2014-06-18

rebase

510. By Nicholas Skaggs on 2014-06-18

removing debugging prints and waits; fix for dbus call

511. By Nicholas Skaggs on 2014-06-18

remove extraneous function

review: Needs Fixing (continuous-integration)
512. By Nicholas Skaggs on 2014-06-18

remove sys.exit from test

review: Needs Fixing (continuous-integration)
513. By Nicholas Skaggs on 2014-06-18

remove extra comment

514. By Nicholas Skaggs on 2014-06-18

merge back andrew's branch using backup/restore instead of isolation

515. By Nicholas Skaggs on 2014-06-18

more comments

516. By Nicholas Skaggs on 2014-06-18

merged victor's artist fix

review: Needs Fixing (continuous-integration)
517. By Nicholas Skaggs on 2014-06-18

add back backup/restore to create_music_libraray

518. By Nicholas Skaggs on 2014-06-18

flake8 goodness

519. By Victor Thompson on 2014-06-18

Cleanup

review: Needs Fixing (continuous-integration)
520. By Victor Thompson on 2014-06-18

revert last commit

521. By Nicholas Skaggs on 2014-06-19

remove extra cleanup steps

522. By Nicholas Skaggs on 2014-06-19

fix up isolation code

523. By Nicholas Skaggs on 2014-06-19

add back _patch_mediascanner_home

524. By Nicholas Skaggs on 2014-06-19

simplify backup/restore code

525. By Nicholas Skaggs on 2014-06-19

simplification, better expansion, pep8 fixes

526. By Victor Thompson on 2014-06-19

Update calls

review: Needs Fixing (continuous-integration)
527. By Victor Thompson on 2014-06-19

remove time

528. By Nicholas Skaggs on 2014-06-19

switch to subprocess

Nicholas Skaggs (nskaggs) wrote :

This is the merge that doesn't end, it goes on and on my friend. Some people tried to merge it not knowing what it was, but they'll continue trying to merge forever just because, This is the mergeThis is the merge that doesn't end that doesn't end . . .

review: Approve (continuous-integration)

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 2014-02-22 21:46:04 +0000
3+++ LibraryListModel.qml 2014-06-19 01:49:30 +0000
4@@ -1,6 +1,8 @@
5 /*
6- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
7- * Daniel Holm <d.holmen@gmail.com>
8+ * Copyright (C) 2013, 2014
9+ * Andrew Hayzen <ahayzen@gmail.com>
10+ * Daniel Holm <d.holmen@gmail.com>
11+ * Victor Thompson <victor.thompson@gmail.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15@@ -21,7 +23,11 @@
16 import "playlists.js" as Playlists
17
18 Item {
19- property ListModel model : ListModel { id: libraryModel }
20+ id: libraryListModelItem
21+ property ListModel model : ListModel {
22+ id: libraryModel
23+ property var linkLibraryListModel: libraryListModelItem
24+ }
25 property alias count: libraryModel.count
26 property var query: null
27 property var param: null
28@@ -37,6 +43,13 @@
29 }
30 }
31
32+ /* Pretent to be like a mediascanner2 listmodel */
33+ property alias rowCount: libraryModel.count
34+
35+ function get(index, role) {
36+ return model.get(index);
37+ }
38+
39 WorkerScript {
40 id: worker
41 source: "worker-library-loader.js"
42@@ -105,66 +118,6 @@
43 return -1;
44 }
45
46- function populate() {
47- console.log("called LibraryListModel::populate()")
48-
49- // Save query for queue
50- query = Library.getAll
51- param = null
52-
53- worker.list = Library.getAll();
54- }
55-
56- function filterArtists() {
57- console.log("called LibraryListModel::filterArtists()")
58-
59- // Save query for queue
60- query = Library.getArtists
61- param = null
62-
63- worker.list = Library.getArtists();
64- }
65-
66- function filterArtistTracks(artist) {
67- console.log("called LibraryListModel::filterArtistTracks()")
68-
69- // Save query for queue
70- query = Library.getArtistTracks
71- param = artist
72-
73- worker.list = Library.getArtistTracks(artist);
74- }
75-
76- function filterArtistAlbums(artist) {
77- console.log("called LibraryListModel::filterArtistAlbums()")
78-
79- // Save query for queue
80- query = Library.getArtistAlbums
81- param = artist
82-
83- worker.list = Library.getArtistAlbums(artist);
84- }
85-
86- function filterAlbums() {
87- console.log("called LibraryListModel::filterAlbums()")
88-
89- // Save query for queue
90- query = Library.getAlbums
91- param = null
92-
93- worker.list = Library.getAlbums();
94- }
95-
96- function filterAlbumTracks(album) {
97- console.log("called LibraryListModel::filterAlbumTracks()")
98-
99- // Save query for queue
100- query = Library.getAlbumTracks
101- param = album
102-
103- worker.list = Library.getAlbumTracks(album);
104- }
105-
106 function filterPlaylists() {
107 console.log("called LibraryListModel::filterPlaylistTracks()")
108
109@@ -195,36 +148,10 @@
110 worker.list = Library.getRecent();
111 }
112
113- function filterGenres() {
114- console.log("called LibraryListModel::filterGenres()")
115-
116- // Save query for queue
117- query = Library.getGenres
118- param = null
119-
120- worker.list = Library.getGenres();
121- }
122-
123- function filterGenreTracks(genre) {
124- console.log("called LibraryListModel::filterGenreTracks()")
125-
126- // Save query for queue
127- query = Library.getGenreTracks
128- param = genre
129-
130- worker.list = Library.getGenreTracks(genre);
131- }
132-
133 function clear() {
134 if (worker.list !== null)
135 {
136 worker.sendMessage({'clear': true, 'model': libraryModel})
137 }
138 }
139-
140- function filterSearch(searchQuery) {
141- query = Library.search
142- param = searchQuery
143- worker.list = Library.search(searchQuery)
144- }
145 }
146
147=== modified file 'LoginLastFM.qml'
148--- LoginLastFM.qml 2014-01-11 17:41:20 +0000
149+++ LoginLastFM.qml 2014-06-19 01:49:30 +0000
150@@ -24,7 +24,6 @@
151 import QtQuick.LocalStorage 2.0
152 import QtQuick.XmlListModel 2.0
153 import "settings.js" as Settings
154-import "meta-database.js" as Library
155 import "scrobble.js" as Scrobble
156
157 // Last.fm login dialog
158
159=== modified file 'MusicAlbums.qml'
160--- MusicAlbums.qml 2014-04-17 14:42:51 +0000
161+++ MusicAlbums.qml 2014-06-19 01:49:30 +0000
162@@ -1,6 +1,8 @@
163 /*
164- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
165- * Daniel Holm <d.holmen@gmail.com>
166+ * Copyright (C) 2013, 2014
167+ * Andrew Hayzen <ahayzen@gmail.com>
168+ * Daniel Holm <d.holmen@gmail.com>
169+ * Victor Thompson <victor.thompson@gmail.com>
170 *
171 * This program is free software; you can redistribute it and/or modify
172 * it under the terms of the GNU General Public License as published by
173@@ -17,14 +19,16 @@
174
175 import QtQuick 2.0
176 import Ubuntu.Components 0.1
177+import Ubuntu.Components 1.1 as Toolkit
178 import Ubuntu.Components.ListItems 0.1
179 import Ubuntu.Components.Popups 0.1
180 import Ubuntu.Components.ListItems 0.1 as ListItem
181+import Ubuntu.MediaScanner 0.1
182+import Ubuntu.Thumbnailer 0.1
183 import QtMultimedia 5.0
184 import QtQuick.LocalStorage 2.0
185 import QtGraphicalEffects 1.0
186 import "settings.js" as Settings
187-import "meta-database.js" as Library
188 import "playlists.js" as Playlists
189 import "common"
190
191@@ -59,21 +63,23 @@
192 anchors.bottomMargin: units.gu(1)
193 cellHeight: height/3
194 cellWidth: height/3
195- model: albumModel.model
196+ model: Toolkit.SortFilterModel {
197+ id: albumsModelFilter
198+ property alias rowCount: albumsModel.rowCount
199+ model: AlbumsModel {
200+ id: albumsModel
201+ store: musicStore
202+ }
203+ sort.property: "title"
204+ sort.order: Qt.AscendingOrder
205+ }
206+
207 delegate: albumDelegate
208 flow: GridView.TopToBottom
209
210 Component {
211 id: albumDelegate
212 Item {
213- property string artist: model.artist
214- property string album: model.album
215- property string title: model.title
216- property string cover: model.cover !== "" ? model.cover : Qt.resolvedUrl("images/music-app-cover@30.png")
217- property string length: model.length
218- property string file: model.file
219- property string year: model.year
220-
221 id: albumItem
222 height: albumlist.cellHeight - units.gu(1)
223 width: albumlist.cellHeight - units.gu(1)
224@@ -85,7 +91,7 @@
225 image: Image {
226 id: icon
227 fillMode: Image.Stretch
228- source: cover
229+ source: "image://albumart/artist=" + model.artist + "&album=" + model.title
230 onStatusChanged: {
231 if (status === Image.Error) {
232 source = Qt.resolvedUrl("images/music-app-cover@30.png")
233@@ -118,7 +124,7 @@
234 anchors.rightMargin: units.gu(1)
235 color: styleMusic.nowPlaying.labelSecondaryColor
236 elide: Text.ElideRight
237- text: artist
238+ text: model.artist
239 fontSize: "x-small"
240 }
241 Label {
242@@ -131,7 +137,7 @@
243 anchors.rightMargin: units.gu(1)
244 color: styleMusic.common.white
245 elide: Text.ElideRight
246- text: album
247+ text: model.title
248 fontSize: "small"
249 }
250 }
251@@ -143,14 +149,12 @@
252 onPressAndHold: {
253 }
254 onClicked: {
255- albumTracksModel.filterAlbumTracks(album)
256-
257- songsSheet.line1 = artist
258- songsSheet.line2 = album
259+ songsSheet.album = model.title;
260+ songsSheet.genre = undefined
261+ songsSheet.line1 = model.artist
262+ songsSheet.line2 = model.title
263 songsSheet.isAlbum = true
264- songsSheet.file = file
265- songsSheet.year = year
266- songsSheet.covers = [cover]
267+ songsSheet.covers = [{author: model.artist, album: model.title}]
268 PopupUtils.open(songsSheet.sheet)
269 }
270 }
271
272=== modified file 'MusicArtists.qml'
273--- MusicArtists.qml 2014-05-19 23:13:39 +0000
274+++ MusicArtists.qml 2014-06-19 01:49:30 +0000
275@@ -1,6 +1,8 @@
276 /*
277- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
278- * Daniel Holm <d.holmen@gmail.com>
279+ * Copyright (C) 2013, 2014
280+ * Andrew Hayzen <ahayzen@gmail.com>
281+ * Daniel Holm <d.holmen@gmail.com>
282+ * Victor Thompson <victor.thompson@gmail.com>
283 *
284 * This program is free software; you can redistribute it and/or modify
285 * it under the terms of the GNU General Public License as published by
286@@ -20,6 +22,8 @@
287 import Ubuntu.Components.ListItems 0.1
288 import Ubuntu.Components.Popups 0.1
289 import Ubuntu.Components.ListItems 0.1 as ListItem
290+import Ubuntu.MediaScanner 0.1
291+import Ubuntu.Thumbnailer 0.1
292 import QtMultimedia 5.0
293 import QtQuick.LocalStorage 2.0
294 import "settings.js" as Settings
295@@ -47,7 +51,12 @@
296 id: artistlist
297 anchors.fill: parent
298 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
299- model: artistModel.model
300+ model: ArtistsModel {
301+ id: artistsModel
302+ albumArtists: true
303+ store: musicStore
304+ }
305+
306 delegate: artistDelegate
307
308 Component {
309@@ -55,9 +64,46 @@
310
311 ListItem.Standard {
312 id: track
313- property string artist: model.artist
314 height: styleMusic.common.itemHeight
315
316+ AlbumsModel {
317+ id: albumArtistModel
318+ albumArtist: model.artist
319+ store: musicStore
320+ }
321+
322+ Repeater {
323+ id: albumArtistModelRepeater
324+ model: albumArtistModel
325+ delegate: Item {
326+ property string author: model.artist
327+ property string album: model.title
328+ }
329+ property var covers: []
330+ signal finished()
331+
332+ onFinished: {
333+ coverRow.count = count
334+ coverRow.covers = covers
335+ }
336+ onItemAdded: {
337+ covers.push({author: item.author, album: item.album});
338+
339+ if (index === count - 1) {
340+ finished();
341+ }
342+ }
343+ }
344+
345+ SongsModel {
346+ id: songArtistModel
347+ albumArtist: model.artist
348+ // HACK: Temporarily setting limit to 500 to ensure model
349+ // is populated. See lp:1326753
350+ limit: 500
351+ store: musicStore
352+ }
353+
354 CoverRow {
355 id: coverRow
356 anchors {
357@@ -65,9 +111,10 @@
358 left: parent.left
359 margins: units.gu(1)
360 }
361- count: parseInt(Library.getArtistCovers(artist).length)
362+
363+ count: 0
364 size: styleMusic.common.albumSize
365- covers: Library.getArtistCovers(artist)
366+ covers: []
367 }
368
369 Label {
370@@ -86,7 +133,7 @@
371 rightMargin: units.gu(1.5)
372 }
373 elide: Text.ElideRight
374- text: artist
375+ text: model.artist
376 }
377
378 Label {
379@@ -103,8 +150,7 @@
380 rightMargin: units.gu(1.5)
381 }
382 elide: Text.ElideRight
383- // model for number of albums?
384- text: i18n.tr("%1 album", "%1 albums", Library.getArtistAlbumCount(artist)).arg(Library.getArtistAlbumCount(artist))
385+ text: i18n.tr("%1 album", "%1 albums", albumArtistModel.rowCount).arg(albumArtistModel.rowCount)
386 }
387
388 Label {
389@@ -121,7 +167,7 @@
390 rightMargin: units.gu(1.5)
391 }
392 elide: Text.ElideRight
393- text: i18n.tr("%1 song", "%1 songs", Library.getArtistTracks(artist).length).arg(Library.getArtistTracks(artist).length)
394+ text: i18n.tr("%1 song", "%1 songs", songArtistModel.rowCount).arg(songArtistModel.rowCount)
395 }
396 onFocusChanged: {
397 }
398@@ -132,14 +178,11 @@
399 onPressAndHold: {
400 }
401 onClicked: {
402- artistAlbumsModel.filterArtistAlbums(artist)
403- artistSheet.artist = artist
404+ artistSheet.artist = model.artist
405 artistSheet.covers = coverRow.covers
406 PopupUtils.open(artistSheet.sheet)
407 }
408 }
409- Component.onCompleted: {
410- }
411 }
412 }
413 }
414
415=== modified file 'MusicNowPlaying.qml'
416--- MusicNowPlaying.qml 2014-05-08 22:58:35 +0000
417+++ MusicNowPlaying.qml 2014-06-19 01:49:30 +0000
418@@ -1,7 +1,8 @@
419 /*
420- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
421- * Daniel Holm <d.holmen@gmail.com>
422- * Victor Thompson <victor.thompson@gmail.com>
423+ * Copyright (C) 2013, 2014
424+ * Andrew Hayzen <ahayzen@gmail.com>
425+ * Daniel Holm <d.holmen@gmail.com>
426+ * Victor Thompson <victor.thompson@gmail.com>
427 *
428 * This program is free software; you can redistribute it and/or modify
429 * it under the terms of the GNU General Public License as published by
430@@ -22,9 +23,9 @@
431 import QtQuick.LocalStorage 2.0
432 import Ubuntu.Components 0.1
433 import Ubuntu.Components.ListItems 0.1 as ListItem
434+import Ubuntu.Thumbnailer 0.1
435 import "common"
436 import "common/ExpanderItems"
437-import "meta-database.js" as Library
438 import "settings.js" as Settings
439
440 Page {
441@@ -254,8 +255,8 @@
442
443 onClicked: {
444 collapseSwipeDelete(-1); // collapse all expands
445- customdebug("File: " + model.file) // debugger
446- trackClicked(trackQueue, index) // play track
447+ customdebug("File: " + model.filename) // debugger
448+ trackQueueClick(index); // toggle track state
449 }
450
451 onMouseXChanged: {
452@@ -309,7 +310,7 @@
453 {
454 collapseSwipeDelete(-1); // collapse all swipedeletes
455 collapseExpand(); // collapse all
456- customdebug("Pressed and held queued track "+model.file)
457+ customdebug("Pressed and held queued track "+model.filename)
458 queuelist.state = "reorder"; // enable reordering state
459 trackContainerReorderAnimation.start();
460 }
461@@ -490,7 +491,6 @@
462
463 // Remove item from queue and clear caches
464 trackQueue.model.remove(index);
465- queueChanged = true;
466 }
467 }
468 }
469@@ -537,7 +537,7 @@
470 height: (queueListItem.state === "current" ? queuelist.currentHeight - units.gu(8) : queuelist.normalHeight) - units.gu(2)
471 width: height
472 image: Image {
473- source: cover !== "" ? cover : "images/music-app-cover@30.png"
474+ source: "image://albumart/artist=" + model.author + "&album=" + model.album
475 onStatusChanged: {
476 if (status === Image.Error) {
477 source = Qt.resolvedUrl("images/music-app-cover@30.png")
478@@ -579,7 +579,7 @@
479 color: styleMusic.nowPlaying.labelSecondaryColor
480 elide: Text.ElideRight
481 height: units.gu(1)
482- text: artist
483+ text: model.author
484 fontSize: 'small'
485 width: parent.width - trackImage.width - units.gu(3.5)
486 x: trackImage.x + trackImage.width + units.gu(1)
487@@ -591,7 +591,7 @@
488 color: styleMusic.common.white
489 elide: Text.ElideRight
490 height: units.gu(1)
491- text: title
492+ text: model.title
493 fontSize: 'medium'
494 width: parent.width - trackImage.width - units.gu(3.5)
495 x: trackImage.x + trackImage.width + units.gu(1)
496@@ -603,7 +603,7 @@
497 color: styleMusic.nowPlaying.labelSecondaryColor
498 elide: Text.ElideRight
499 height: units.gu(1)
500- text: album
501+ text: model.album
502 fontSize: 'x-small'
503 width: parent.width - trackImage.width - units.gu(3.5)
504 x: trackImage.x + trackImage.width + units.gu(1)
505
506=== modified file 'MusicPlaylists.qml'
507--- MusicPlaylists.qml 2014-04-26 17:13:02 +0000
508+++ MusicPlaylists.qml 2014-06-19 01:49:30 +0000
509@@ -1,6 +1,8 @@
510 /*
511- * Copyright (C) 2013 Daniel Holm <d.holmen@gmail.com>
512- Victor Thompson <victor.thompson@gmail.com>
513+ * Copyright (C) 2013, 2014
514+ * Andrew Hayzen <ahayzen@gmail.com>
515+ * Daniel Holm <d.holmen@gmail.com>
516+ * Victor Thompson <victor.thompson@gmail.com>
517 *
518 *
519 * This program is free software; you can redistribute it and/or modify
520@@ -24,7 +26,6 @@
521 import QtMultimedia 5.0
522 import QtQuick.LocalStorage 2.0
523 import "settings.js" as Settings
524-import "meta-database.js" as Library
525 import "scrobble.js" as Scrobble
526 import "playlists.js" as Playlists
527 import "common"
528
529=== modified file 'MusicSearch.qml'
530--- MusicSearch.qml 2014-05-14 23:39:19 +0000
531+++ MusicSearch.qml 2014-06-19 01:49:30 +0000
532@@ -1,7 +1,8 @@
533 /*
534- * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com>
535- * Daniel Holm <d.holmen@gmail.com>
536- * Victor Thompson <victor.thompson@gmail.com>
537+ * Copyright (C) 2013, 2014
538+ * Andrew Hayzen <ahayzen@gmail.com>
539+ * Daniel Holm <d.holmen@gmail.com>
540+ * Victor Thompson <victor.thompson@gmail.com>
541 *
542 * This program is free software; you can redistribute it and/or modify
543 * it under the terms of the GNU General Public License as published by
544@@ -21,9 +22,10 @@
545 import Ubuntu.Components 0.1
546 import Ubuntu.Components.ListItems 0.1 as ListItem
547 import Ubuntu.Components.Popups 0.1
548+import Ubuntu.MediaScanner 0.1
549+import Ubuntu.Thumbnailer 0.1
550 import QtQuick.LocalStorage 2.0
551 import "playlists.js" as Playlists
552-import "meta-database.js" as Library
553 import "common"
554 import "common/ExpanderItems"
555
556@@ -93,18 +95,13 @@
557 // Provide a small pause before search
558 Timer {
559 id: searchTimer
560- interval: 1500
561+ interval: 500
562 repeat: false
563 onTriggered: {
564- if(searchField.text) {
565- searchModel.filterSearch(searchField.text) // query the databse
566- searchActivity.running = true // start the activity indicator
567- }
568- else {
569- customdebug("No search terms.")
570- searchModel.filterSearch("empty somehow?")
571- }
572- indicatorTimer.start()
573+ songsSearchModel.query = searchField.text;
574+ searchActivity.running = true // start the activity indicator
575+
576+ indicatorTimer.start()
577 }
578 }
579 // and onother one for the indicator
580@@ -133,6 +130,7 @@
581 width: parent.width
582 height: parent.height
583 color: "transparent"
584+ visible: searchField.text
585 clip: true
586 anchors {
587 top: searchField.bottom
588@@ -147,7 +145,10 @@
589 objectName: "searchtrackview"
590 width: parent.width
591 height: parent.width
592- model: searchModel.model
593+ model: SongsSearchModel {
594+ id: songsSearchModel
595+ store: musicStore
596+ }
597
598 onMovementStarted: {
599 searchTrackView.forceActiveFocus()
600@@ -158,18 +159,12 @@
601 objectName: "playlist"
602 width: parent.width
603 height: styleMusic.common.itemHeight
604- property string title: model.title
605- property string artist: model.artist
606- property string file: model.file
607- property string album: model.album
608- property string cover: model.cover
609- property string genre: model.genre
610
611 onClicked: {
612 console.debug("Debug: "+title+" added to queue")
613 // now play this track, but keep current queue
614 trackQueue.append(model)
615- trackClicked(trackQueue, trackQueue.model.count - 1, true)
616+ trackQueueClick(trackQueue.model.count - 1);
617 onDoneClicked: PopupUtils.close(searchTrack)
618 }
619
620@@ -189,7 +184,7 @@
621 width: styleMusic.common.albumSize
622 height: styleMusic.common.albumSize
623 image: Image {
624- source: cover !== "" ? cover : Qt.resolvedUrl("images/music-app-cover@30.png")
625+ source: "image://albumart/artist=" + model.author + "&album=" + model.title
626 onStatusChanged: {
627 if (status === Image.Error) {
628 source = Qt.resolvedUrl("images/music-app-cover@30.png")
629@@ -211,7 +206,7 @@
630 anchors.right: parent.right
631 anchors.rightMargin: units.gu(1.5)
632 elide: Text.ElideRight
633- text: artist
634+ text: model.author
635 }
636 Label {
637 id: trackTitle
638@@ -227,7 +222,7 @@
639 anchors.right: parent.right
640 anchors.rightMargin: units.gu(1.5)
641 elide: Text.ElideRight
642- text: title
643+ text: model.title
644 }
645 Label {
646 id: trackAlbum
647@@ -242,7 +237,7 @@
648 anchors.right: parent.right
649 anchors.rightMargin: units.gu(1.5)
650 elide: Text.ElideRight
651- text: album
652+ text: model.album
653 }
654 Label {
655 id: trackDuration
656@@ -267,7 +262,7 @@
657 fill: parent
658 }
659 listItem: search
660- model: searchModel.model.get(index)
661+ model: songsSearchModel.get(index, songsSearchModel.RoleModelData)
662 row: Row {
663 AddToPlaylist {
664
665
666=== modified file 'MusicStart.qml'
667--- MusicStart.qml 2014-06-02 15:51:48 +0000
668+++ MusicStart.qml 2014-06-19 01:49:30 +0000
669@@ -1,7 +1,8 @@
670 /*
671- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
672- * Daniel Holm <d.holmen@gmail.com>
673- * Victor Thompson <victor.thompson@gmail.com>
674+ * Copyright (C) 2013, 2014
675+ * Andrew Hayzen <ahayzen@gmail.com>
676+ * Daniel Holm <d.holmen@gmail.com>
677+ * Victor Thompson <victor.thompson@gmail.com>
678 *
679 * This program is free software; you can redistribute it and/or modify
680 * it under the terms of the GNU General Public License as published by
681@@ -18,9 +19,12 @@
682
683 import QtQuick 2.0
684 import Ubuntu.Components 0.1
685+import Ubuntu.Components 1.1 as Toolkit
686 import Ubuntu.Components.ListItems 0.1
687 import Ubuntu.Components.Popups 0.1
688 import Ubuntu.Components.ListItems 0.1 as ListItem
689+import Ubuntu.MediaScanner 0.1
690+import Ubuntu.Thumbnailer 0.1
691 import QtMultimedia 5.0
692 import QtQuick.LocalStorage 2.0
693 import "settings.js" as Settings
694@@ -94,7 +98,7 @@
695 model: recentModel.model
696 delegate: recentDelegate
697 header: Item {
698- id: spacer
699+ id: recentSpacer
700 width: units.gu(1)
701 }
702 footer: Item {
703@@ -121,7 +125,7 @@
704 Item {
705 property string title: model.title
706 property string title2: model.title2
707- property var covers: type === "playlist" ? Playlists.getPlaylistCovers(title) : [Library.getAlbumCover(title)]
708+ property var covers: type === "playlist" ? Playlists.getPlaylistCovers(title) : [{author: model.title2, album: model.title}]
709 property string type: model.type
710 property string time: model.time
711 property string key: model.key
712@@ -187,8 +191,9 @@
713 if (type === "playlist") {
714 albumTracksModel.filterPlaylistTracks(key)
715 } else {
716- albumTracksModel.filterAlbumTracks(title)
717+ songsSheet.album = title;
718 }
719+ songsSheet.genre = undefined;
720
721 songsSheet.line1 = title2
722 songsSheet.line2 = title
723@@ -226,10 +231,13 @@
724 anchors.topMargin: units.gu(1)
725 spacing: units.gu(1)
726 height: units.gu(18)
727- model: genreModel.model
728+ model: GenresModel {
729+ store: musicStore
730+ }
731+
732 delegate: genreDelegate
733 header: Item {
734- id: spacer
735+ id: genreSpacer
736 width: units.gu(1)
737 }
738 orientation: ListView.Horizontal
739@@ -237,19 +245,47 @@
740 Component {
741 id: genreDelegate
742 Item {
743- property string artist: model.artist
744- property string album: model.album
745- property string title: model.title
746- property var covers: Library.getGenreCovers(model.genre)
747- property string length: model.length
748- property string file: model.file
749- property string year: model.year
750- property string genre: model.genre
751-
752 id: genreItem
753 objectName: "genreItemObject"
754 height: genrelist.height - units.gu(1)
755 width: height
756+
757+ Repeater {
758+ id: albumGenreModelRepeater
759+ model: AlbumsModel {
760+ genre: model.genre
761+ store: musicStore
762+ }
763+
764+ delegate: Item {
765+ property string author: model.artist
766+ property string album: model.title
767+ }
768+ property var covers: []
769+ signal finished()
770+
771+ onFinished: {
772+ genreShape.count = count
773+ genreShape.covers = covers
774+ }
775+ onItemAdded: {
776+ covers.push({author: item.author, album: item.album});
777+
778+ if (index === count - 1) {
779+ finished();
780+ }
781+ }
782+ }
783+
784+ SongsModel {
785+ id: songGenreModel
786+ genre: model.genre
787+ // HACK: Temporarily setting limit to 500 to ensure model
788+ // is populated. See lp:1326753
789+ limit: 500
790+ store: musicStore
791+ }
792+
793 CoverRow {
794 id: genreShape
795 anchors {
796@@ -257,19 +293,20 @@
797 left: parent.left
798 verticalCenter: parent.verticalCenter
799 }
800- count: genreItem.covers.length
801+ count: 0
802 size: genreItem.width
803- covers: genreItem.covers
804+ covers: []
805 spacing: units.gu(2)
806 }
807 MouseArea {
808 anchors.fill: parent
809 onClicked: {
810- albumTracksModel.filterGenreTracks(genre)
811+ songsSheet.album = undefined
812+ songsSheet.genre = model.genre
813 songsSheet.line1 = "Genre"
814- songsSheet.line2 = genre
815- songsSheet.isAlbum = false
816- songsSheet.covers = covers
817+ songsSheet.line2 = model.genre
818+ songsSheet.isAlbum = true
819+ songsSheet.covers = genreShape.covers
820 PopupUtils.open(songsSheet.sheet)
821 }
822 }
823@@ -298,7 +335,7 @@
824 anchors.rightMargin: units.gu(1)
825 color: styleMusic.common.white
826 elide: Text.ElideRight
827- text: genre
828+ text: model.genre
829 fontSize: "small"
830 }
831 Label {
832@@ -311,7 +348,7 @@
833 anchors.rightMargin: units.gu(1)
834 color: styleMusic.nowPlaying.labelSecondaryColor
835 elide: Text.ElideRight
836- text: i18n.tr("%1 song", "%1 songs", model.total).arg(model.total)
837+ text: i18n.tr("%1 song", "%1 songs", songGenreModel.rowCount).arg(songGenreModel.rowCount)
838 fontSize: "x-small"
839 }
840 }
841@@ -343,7 +380,16 @@
842 anchors.topMargin: units.gu(1)
843 spacing: units.gu(1)
844 height: units.gu(18)
845- model: albumModel.model
846+ model: Toolkit.SortFilterModel {
847+ id: albumsModelFilter
848+ property alias rowCount: albumsModel.rowCount
849+ model: AlbumsModel {
850+ id: albumsModel
851+ store: musicStore
852+ }
853+ sort.property: "title"
854+ sort.order: Qt.AscendingOrder
855+ }
856 delegate: albumDelegate
857 header: Item {
858 id: albumSpacer
859@@ -355,12 +401,8 @@
860 id: albumDelegate
861 Item {
862 property string artist: model.artist
863- property string album: model.album
864- property var covers: [Library.getAlbumCover(album)]
865- property string length: model.length
866- property string file: model.file
867- property string year: model.year
868- property string genre: model.genre
869+ property string album: model.title
870+ property var covers: [{author: model.artist, album: model.title}]
871
872 id: albumItem
873 objectName: "albumItemObject"
874@@ -381,7 +423,8 @@
875 MouseArea {
876 anchors.fill: parent
877 onClicked: {
878- albumTracksModel.filterAlbumTracks(album)
879+ songsSheet.album = album;
880+ songsSheet.genre = undefined
881 songsSheet.line1 = artist
882 songsSheet.line2 = album
883 songsSheet.isAlbum = true
884
885=== modified file 'MusicToolbar.qml'
886--- MusicToolbar.qml 2014-05-04 05:30:54 +0000
887+++ MusicToolbar.qml 2014-06-19 01:49:30 +0000
888@@ -1,7 +1,8 @@
889 /*
890- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
891- * Daniel Holm <d.holmen@gmail.com>
892- * Victor Thompson <victor.thompson@gmail.com>
893+ * Copyright (C) 2013, 2014
894+ * Andrew Hayzen <ahayzen@gmail.com>
895+ * Daniel Holm <d.holmen@gmail.com>
896+ * Victor Thompson <victor.thompson@gmail.com>
897 *
898 * This program is free software; you can redistribute it and/or modify
899 * it under the terms of the GNU General Public License as published by
900@@ -21,7 +22,6 @@
901 import QtMultimedia 5.0
902 import Ubuntu.Components 0.1
903 import Ubuntu.Components.Popups 0.1
904-import "meta-database.js" as Library
905 import "settings.js" as Settings
906
907 Item {
908
909=== modified file 'MusicTracks.qml'
910--- MusicTracks.qml 2014-04-26 17:13:02 +0000
911+++ MusicTracks.qml 2014-06-19 01:49:30 +0000
912@@ -1,6 +1,8 @@
913 /*
914- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
915- * Daniel Holm <d.holmen@gmail.com>
916+ * Copyright (C) 2013, 2014
917+ * Andrew Hayzen <ahayzen@gmail.com>
918+ * Daniel Holm <d.holmen@gmail.com>
919+ * Victor Thompson <victor.thompson@gmail.com>
920 *
921 * This program is free software; you can redistribute it and/or modify
922 * it under the terms of the GNU General Public License as published by
923@@ -17,12 +19,14 @@
924
925 import QtQuick 2.0
926 import Ubuntu.Components 0.1
927+import Ubuntu.Components 1.1 as Toolkit
928 import Ubuntu.Components.ListItems 0.1
929 import Ubuntu.Components.ListItems 0.1 as ListItem
930+import Ubuntu.MediaScanner 0.1
931+import Ubuntu.Thumbnailer 0.1
932 import QtMultimedia 5.0
933 import QtQuick.LocalStorage 2.0
934 import "settings.js" as Settings
935-import "meta-database.js" as Library
936 import "playlists.js" as Playlists
937 import "common"
938 import "common/ExpanderItems"
939@@ -43,23 +47,30 @@
940 }
941 }
942
943+
944 ListView {
945 id: tracklist
946 anchors.fill: parent
947 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
948 highlightFollowsCurrentItem: false
949- model: libraryModel.model
950+ model: Toolkit.SortFilterModel {
951+ id: songsModelFilter
952+ property alias rowCount: songsModel.rowCount
953+ model: SongsModel {
954+ id: songsModel
955+ // HACK: Temporarily setting limit to 500 to ensure model
956+ // is populated. See lp:1326753
957+ limit: 500
958+ store: musicStore
959+ }
960+ sort.property: "title"
961+ sort.order: Qt.AscendingOrder
962+ }
963 delegate: trackDelegate
964 Component {
965 id: trackDelegate
966 ListItem.Standard {
967 id: track
968- property string artist: model.artist
969- property string album: model.album
970- property string title: model.title
971- property string cover: model.cover
972- property string length: model.length
973- property string file: model.file
974 width: parent.width
975 height: styleMusic.common.itemHeight
976
977@@ -70,7 +81,7 @@
978 focus = true
979 }
980
981- trackClicked(libraryModel, index) // play track
982+ trackClicked(tracklist.model, index) // play track
983 }
984 }
985
986@@ -90,7 +101,7 @@
987 width: styleMusic.common.albumSize
988 height: styleMusic.common.albumSize
989 image: Image {
990- source: cover !== "" ? cover : Qt.resolvedUrl("images/music-app-cover@30.png")
991+ source: "image://albumart/artist=" + model.author + "&album=" + model.album
992 onStatusChanged: {
993 if (status === Image.Error) {
994 source = Qt.resolvedUrl("images/music-app-cover@30.png")
995@@ -111,7 +122,7 @@
996 anchors.right: parent.right
997 anchors.rightMargin: units.gu(1.5)
998 elide: Text.ElideRight
999- text: artist
1000+ text: model.author
1001 }
1002 Label {
1003 id: trackTitle
1004@@ -127,7 +138,7 @@
1005 anchors.right: parent.right
1006 anchors.rightMargin: units.gu(1.5)
1007 elide: Text.ElideRight
1008- text: track.title
1009+ text: model.title
1010 }
1011 Label {
1012 id: trackAlbum
1013@@ -142,7 +153,7 @@
1014 anchors.right: parent.right
1015 anchors.rightMargin: units.gu(1.5)
1016 elide: Text.ElideRight
1017- text: album
1018+ text: model.album
1019 }
1020 Label {
1021 id: trackDuration
1022@@ -157,7 +168,7 @@
1023 anchors.rightMargin: units.gu(1.5)
1024 elide: Text.ElideRight
1025 visible: false
1026- text: ""
1027+ text: "" // model.duration
1028 }
1029 }
1030
1031@@ -167,7 +178,7 @@
1032 fill: parent
1033 }
1034 listItem: track
1035- model: libraryModel.model.get(index)
1036+ model: songsModelFilter.get(index, songsModelFilter.RoleModelData)
1037 row: Row {
1038 AddToPlaylist {
1039
1040
1041=== modified file 'MusicaddtoPlaylist.qml'
1042--- MusicaddtoPlaylist.qml 2014-04-26 17:13:02 +0000
1043+++ MusicaddtoPlaylist.qml 2014-06-19 01:49:30 +0000
1044@@ -1,7 +1,8 @@
1045 /*
1046- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1047- * Daniel Holm <d.holmen@gmail.com>
1048- * Victor Thompson <victor.thompson@gmail.com>
1049+ * Copyright (C) 2013, 2014
1050+ * Andrew Hayzen <ahayzen@gmail.com>
1051+ * Daniel Holm <d.holmen@gmail.com>
1052+ * Victor Thompson <victor.thompson@gmail.com>
1053 *
1054 * This program is free software; you can redistribute it and/or modify
1055 * it under the terms of the GNU General Public License as published by
1056@@ -78,13 +79,13 @@
1057 property string name: model.name
1058 property string count: model.count
1059 onClicked: {
1060- console.debug("Debug: "+chosenElement.file+" added to "+name)
1061+ console.debug("Debug: "+chosenElement.filename+" added to "+name)
1062 Playlists.addtoPlaylist(name,
1063- chosenElement.file,
1064- chosenElement.artist,
1065+ chosenElement.filename,
1066+ chosenElement.author,
1067 chosenElement.title,
1068 chosenElement.album,
1069- chosenElement.cover,
1070+ chosenElement.art,
1071 "","","","")
1072 count = Playlists.getPlaylistCount(name) // get the new count
1073 playlistModel.model.set(index, {"count": count}) // update number ot tracks in playlist
1074
1075=== modified file 'Player.qml'
1076--- Player.qml 2014-04-16 11:58:54 +0000
1077+++ Player.qml 2014-06-19 01:49:30 +0000
1078@@ -1,7 +1,8 @@
1079 /*
1080- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1081- * Daniel Holm <d.holmen@gmail.com>
1082- * Victor Thompson <victor.thompson@gmail.com>
1083+ * Copyright (C) 2013, 2014
1084+ * Andrew Hayzen <ahayzen@gmail.com>
1085+ * Daniel Holm <d.holmen@gmail.com>
1086+ * Victor Thompson <victor.thompson@gmail.com>
1087 *
1088 * This program is free software; you can redistribute it and/or modify
1089 * it under the terms of the GNU General Public License as published by
1090@@ -33,7 +34,6 @@
1091
1092 property string currentMetaAlbum: ""
1093 property string currentMetaArtist: ""
1094- property string currentMetaCover: ""
1095 property string currentMetaFile: ""
1096 property string currentMetaTitle: ""
1097 property int currentIndex: -1
1098@@ -61,9 +61,9 @@
1099 onCountChanged: {
1100 if (trackQueue.model.count === 1) {
1101 player.currentIndex = 0;
1102- player.source = Qt.resolvedUrl(trackQueue.model.get(0).file)
1103+ player.source = Qt.resolvedUrl(trackQueue.model.get(0).filename)
1104 } else if (trackQueue.model.count === 0) {
1105- player.currentMetaCover = ""
1106+ currentMetaFile = ""
1107 }
1108 }
1109 }
1110@@ -117,11 +117,11 @@
1111 }
1112
1113 if (startPlaying) { // only start the track if told
1114- playSong(trackQueue.model.get(newIndex).file, newIndex)
1115+ playSong(trackQueue.model.get(newIndex).filename, newIndex)
1116 }
1117 else {
1118 currentIndex = newIndex
1119- source = Qt.resolvedUrl(trackQueue.model.get(newIndex).file)
1120+ source = Qt.resolvedUrl(trackQueue.model.get(newIndex).filename)
1121 }
1122 }
1123
1124@@ -183,9 +183,8 @@
1125 else {
1126 var obj = trackQueue.model.get(player.currentIndex);
1127 currentMetaAlbum = obj.album;
1128- currentMetaArtist = obj.artist;
1129- currentMetaCover = obj.cover;
1130- currentMetaFile = obj.file;
1131+ currentMetaArtist = obj.author;
1132+ currentMetaFile = obj.filename;
1133 currentMetaTitle = obj.title;
1134 }
1135
1136
1137=== modified file 'Style.qml'
1138--- Style.qml 2014-04-25 17:05:03 +0000
1139+++ Style.qml 2014-06-19 01:49:30 +0000
1140@@ -1,7 +1,8 @@
1141 /*
1142- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1143- * Daniel Holm <d.holmen@gmail.com>
1144- * Victor Thompson <victor.thompson@gmail.com>
1145+ * Copyright (C) 2013, 2014
1146+ * Andrew Hayzen <ahayzen@gmail.com>
1147+ * Daniel Holm <d.holmen@gmail.com>
1148+ * Victor Thompson <victor.thompson@gmail.com>
1149 *
1150 * This program is free software; you can redistribute it and/or modify
1151 * it under the terms of the GNU General Public License as published by
1152
1153=== modified file 'click/apparmor.json'
1154--- click/apparmor.json 2014-05-30 14:23:43 +0000
1155+++ click/apparmor.json 2014-06-19 01:49:30 +0000
1156@@ -1,5 +1,5 @@
1157 {
1158- "policy_version": 1.1,
1159+ "policy_version": 1.2,
1160 "template": "unconfined",
1161 "policy_groups": []
1162 }
1163
1164=== modified file 'click/manifest.json.in'
1165--- click/manifest.json.in 2014-06-06 11:35:53 +0000
1166+++ click/manifest.json.in 2014-06-19 01:49:30 +0000
1167@@ -1,7 +1,6 @@
1168 {
1169- "description": "A music application for Ubuntu",
1170- "framework": "ubuntu-sdk-14.04-dev1",
1171- "architecture": "armhf",
1172+ "description": "A music application for ubuntu",
1173+ "framework": "ubuntu-sdk-14.10-qml-dev1",
1174 "hooks": {
1175 "@APP_NAME@": {
1176 "apparmor": "apparmor.json",
1177
1178=== modified file 'common/AlbumsSheet.qml'
1179--- common/AlbumsSheet.qml 2014-05-19 23:13:39 +0000
1180+++ common/AlbumsSheet.qml 2014-06-19 01:49:30 +0000
1181@@ -1,7 +1,8 @@
1182 /*
1183- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1184- * Daniel Holm <d.holmen@gmail.com>
1185- * Victor Thompson <victor.thompson@gmail.com>
1186+ * Copyright (C) 2013, 2014
1187+ * Andrew Hayzen <ahayzen@gmail.com>
1188+ * Daniel Holm <d.holmen@gmail.com>
1189+ * Victor Thompson <victor.thompson@gmail.com>
1190 *
1191 * This program is free software; you can redistribute it and/or modify
1192 * it under the terms of the GNU General Public License as published by
1193@@ -21,6 +22,8 @@
1194 import Ubuntu.Components.ListItems 0.1
1195 import Ubuntu.Components.Popups 0.1
1196 import Ubuntu.Components.ListItems 0.1 as ListItem
1197+import Ubuntu.MediaScanner 0.1
1198+import Ubuntu.Thumbnailer 0.1
1199 import QtQuick.LocalStorage 2.0
1200 import "../meta-database.js" as Library
1201
1202@@ -58,7 +61,12 @@
1203 width: parent.width
1204 anchors.top: parent.top
1205 anchors.bottom: parent.bottom
1206- model: artistAlbumsModel.model
1207+ model: AlbumsModel {
1208+ id: artistsModel
1209+ albumArtist: sheetItem.artist
1210+ store: musicStore
1211+ }
1212+
1213 delegate: albumTracksDelegate
1214 header: artistHeaderDelegate
1215
1216@@ -132,6 +140,15 @@
1217 fontSize: "large"
1218 }
1219
1220+ SongsModel {
1221+ id: songArtistModel
1222+ albumArtist: sheetItem.artist
1223+ // HACK: Temporarily setting limit to 500 to ensure model
1224+ // is populated. See lp:1326753
1225+ limit: 500
1226+ store: musicStore
1227+ }
1228+
1229 // Play
1230 Rectangle {
1231 id: playRow
1232@@ -167,10 +184,7 @@
1233 MouseArea {
1234 anchors.fill: parent
1235 onClicked: {
1236- albumTracksModel.filterArtistTracks(artist)
1237- trackQueue.model.clear()
1238- addQueueFromModel(albumTracksModel)
1239- trackClicked(trackQueue, 0) // play track
1240+ trackClicked(songArtistModel, 0, true)
1241
1242 // TODO: add links to recent
1243
1244@@ -218,8 +232,7 @@
1245 MouseArea {
1246 anchors.fill: parent
1247 onClicked: {
1248- albumTracksModel.filterArtistTracks(artist)
1249- addQueueFromModel(albumTracksModel)
1250+ addQueueFromModel(songArtistModel)
1251 }
1252 }
1253 }
1254@@ -236,6 +249,23 @@
1255 width: parent.width
1256 height: units.gu(20)
1257
1258+ SongsModel {
1259+ id: songAlbumArtistModel
1260+ albumArtist: model.artist
1261+ album: model.title
1262+ // HACK: Temporarily setting limit to 500 to ensure model
1263+ // is populated. See lp:1326753
1264+ limit: 500
1265+ store: musicStore
1266+ }
1267+ Repeater {
1268+ id: songAlbumArtistModelRepeater
1269+ model: songAlbumArtistModel
1270+ delegate: Text { text: new Date(model.date).toLocaleString(Qt.locale(),'yyyy'); visible: false }
1271+ property string year: ""
1272+ onItemAdded: year = item.text
1273+ }
1274+
1275 CoverRow {
1276 id: albumImage
1277 anchors {
1278@@ -246,7 +276,7 @@
1279 }
1280 count: 1
1281 size: parent.height
1282- covers: [Library.getAlbumCover(model.album)]
1283+ covers: [{author: artist, album: model.title}]
1284 objectName: "artistsheet-albumcover"
1285 spacing: units.gu(2)
1286
1287@@ -259,13 +289,12 @@
1288 focus = true
1289 }
1290
1291- albumTracksModel.filterAlbumTracks(album)
1292+ albumSheet.album = model.title;
1293+
1294 albumSheet.line1 = artist
1295- albumSheet.line2 = model.album
1296+ albumSheet.line2 = model.title
1297 albumSheet.isAlbum = true
1298- albumSheet.file = file
1299- albumSheet.year = year
1300- albumSheet.covers = [Library.getAlbumCover(model.album) || Qt.resolvedUrl("../images/music-app-cover@30.png")]
1301+ albumSheet.covers = [{author: artist, album: model.title}]
1302 PopupUtils.open(albumSheet.sheet)
1303
1304 // TODO: This closes the SDK defined sheet
1305@@ -305,7 +334,7 @@
1306 anchors.right: parent.right
1307 anchors.rightMargin: units.gu(1.5)
1308 elide: Text.ElideRight
1309- text: album
1310+ text: model.title
1311 }
1312 Label {
1313 id: albumYear
1314@@ -320,7 +349,9 @@
1315 anchors.right: parent.right
1316 anchors.rightMargin: units.gu(1.5)
1317 elide: Text.ElideRight
1318- text: i18n.tr(model.year + " | %1 song", model.year + " | %1 songs", Library.getAlbumTracks(album).length).arg(Library.getAlbumTracks(album).length)
1319+ text: i18n.tr(songAlbumArtistModelRepeater.year + " | %1 song",
1320+ songAlbumArtistModelRepeater.year + " | %1 songs",
1321+ songAlbumArtistModelRepeater.count).arg(songAlbumArtistModelRepeater.count)
1322 }
1323
1324 // Play
1325@@ -355,13 +386,10 @@
1326 MouseArea {
1327 anchors.fill: parent
1328 onClicked: {
1329- albumTracksModel.filterAlbumTracks(album)
1330- Library.addRecent(album, artist, Library.getAlbumCover(album), album, "album")
1331+ Library.addRecent(model.title, artist, "", model.title, "album")
1332 mainView.hasRecent = true
1333 recentModel.filterRecent()
1334- trackQueue.model.clear()
1335- addQueueFromModel(albumTracksModel)
1336- trackClicked(trackQueue, 0) // play track
1337+ trackClicked(songAlbumArtistModel, 0, true)
1338
1339 // TODO: This closes the SDK defined sheet
1340 // component. It should be able to close
1341@@ -403,8 +431,7 @@
1342 MouseArea {
1343 anchors.fill: parent
1344 onClicked: {
1345- albumTracksModel.filterAlbumTracks(album)
1346- addQueueFromModel(albumTracksModel)
1347+ addQueueFromModel(songAlbumArtistModel)
1348 }
1349 }
1350 }
1351
1352=== modified file 'common/BlurredBackground.qml'
1353--- common/BlurredBackground.qml 2014-04-12 01:06:28 +0000
1354+++ common/BlurredBackground.qml 2014-06-19 01:49:30 +0000
1355@@ -1,7 +1,8 @@
1356 /*
1357- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1358- * Daniel Holm <d.holmen@gmail.com>
1359- * Victor Thompson <victor.thompson@gmail.com>
1360+ * Copyright (C) 2013, 2014
1361+ * Andrew Hayzen <ahayzen@gmail.com>
1362+ * Daniel Holm <d.holmen@gmail.com>
1363+ * Victor Thompson <victor.thompson@gmail.com>
1364 *
1365 * This program is free software; you can redistribute it and/or modify
1366 * it under the terms of the GNU General Public License as published by
1367@@ -23,15 +24,14 @@
1368 // Blurred background
1369 Rectangle {
1370 anchors.fill: parent
1371- property string cover: player.currentMetaCover !== "" ?
1372- player.currentMetaCover :
1373- "../images/music-app-cover@30.png"
1374+ property string art: player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : "image://albumart/artist=" + player.currentMetaArtist + "&album=" + player.currentMetaAlbum
1375+
1376 // the album art
1377 Image {
1378 id: backgroundImage
1379 anchors.horizontalCenter: parent.horizontalCenter
1380 anchors.verticalCenter: parent.verticalCenter
1381- source: cover // this has to be fixed for the default cover art to work - cant find in this dir
1382+ source: art // this has to be fixed for the default cover art to work - cant find in this dir
1383 height: Math.max(parent.height, parent.width)
1384 width: Math.max(parent.height, parent.width)
1385 visible: false
1386@@ -54,10 +54,10 @@
1387 color: "white"
1388 opacity: 0.7
1389 }
1390- onCoverChanged: {
1391+ onArtChanged: {
1392 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,
1393 // there should be a better way of getting the blur to repaint
1394- backgroundImage.source = cover
1395+ backgroundImage.source = art
1396 backgroundBlur.source = null
1397 backgroundBlur.source = backgroundImage
1398 }
1399
1400=== modified file 'common/CoverRow.qml'
1401--- common/CoverRow.qml 2014-04-11 18:18:12 +0000
1402+++ common/CoverRow.qml 2014-06-19 01:49:30 +0000
1403@@ -1,5 +1,8 @@
1404 /*
1405- * Copyright (C) 2013 Nekhelesh Ramananthan <krnekhelesh@gmail.com>
1406+ * Copyright (C) 2013, 2014
1407+ * Andrew Hayzen <ahayzen@gmail.com>
1408+ * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
1409+ * Victor Thompson <victor.thompson@gmail.com>
1410 *
1411 * This program is free software; you can redistribute it and/or modify
1412 * it under the terms of the GNU General Public License as published by
1413@@ -52,8 +55,9 @@
1414 delegate: Image {
1415 width: coverRow.size
1416 height: width
1417- source: coverRow.count === 0 || coverRow.covers[index] === ""
1418- ? Qt.resolvedUrl("../images/music-app-cover@30.png") : coverRow.covers[index]
1419+ source: coverRow.count !== 0 && coverRow.covers[index] !== "" && coverRow.covers[index] !== undefined
1420+ ? "image://albumart/artist=" + coverRow.covers[index].author + "&album=" + coverRow.covers[index].album
1421+ : Qt.resolvedUrl("../images/music-app-cover@30.png")
1422 onStatusChanged: {
1423 if (status === Image.Error) {
1424 source = Qt.resolvedUrl("../images/music-app-cover@30.png")
1425
1426=== modified file 'common/Expander.qml'
1427--- common/Expander.qml 2014-04-30 01:59:41 +0000
1428+++ common/Expander.qml 2014-06-19 01:49:30 +0000
1429@@ -1,7 +1,8 @@
1430 /*
1431- * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com>
1432- * Daniel Holm <d.holmen@gmail.com>
1433- * Victor Thompson <victor.thompson@gmail.com>
1434+ * Copyright (C) 2013, 2014
1435+ * Andrew Hayzen <ahayzen@gmail.com>
1436+ * Daniel Holm <d.holmen@gmail.com>
1437+ * Victor Thompson <victor.thompson@gmail.com>
1438 *
1439 * This program is free software; you can redistribute it and/or modify
1440 * it under the terms of the GNU General Public License as published by
1441
1442=== modified file 'common/SongsSheet.qml'
1443--- common/SongsSheet.qml 2014-04-26 17:13:02 +0000
1444+++ common/SongsSheet.qml 2014-06-19 01:49:30 +0000
1445@@ -1,7 +1,8 @@
1446 /*
1447- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
1448- * Daniel Holm <d.holmen@gmail.com>
1449- * Victor Thompson <victor.thompson@gmail.com>
1450+ * Copyright (C) 2013, 2014
1451+ * Andrew Hayzen <ahayzen@gmail.com>
1452+ * Daniel Holm <d.holmen@gmail.com>
1453+ * Victor Thompson <victor.thompson@gmail.com>
1454 *
1455 * This program is free software; you can redistribute it and/or modify
1456 * it under the terms of the GNU General Public License as published by
1457@@ -21,6 +22,8 @@
1458 import Ubuntu.Components.ListItems 0.1
1459 import Ubuntu.Components.Popups 0.1
1460 import Ubuntu.Components.ListItems 0.1 as ListItem
1461+import Ubuntu.MediaScanner 0.1
1462+import Ubuntu.Thumbnailer 0.1
1463 import QtQuick.LocalStorage 2.0
1464 import "../meta-database.js" as Library
1465 import "ExpanderItems"
1466@@ -38,6 +41,17 @@
1467 property bool isAlbum: false
1468 property alias sheet: sheetComponent
1469
1470+ property alias album: songsModel.album
1471+ property alias genre: songsModel.genre
1472+
1473+ SongsModel {
1474+ id: songsModel
1475+ // HACK: Temporarily setting limit to 500 to ensure model
1476+ // is populated. See lp:1326753
1477+ limit: 500
1478+ store: musicStore
1479+ }
1480+
1481 Component {
1482 id: sheetComponent
1483 DefaultSheet {
1484@@ -61,7 +75,8 @@
1485 width: parent.width
1486 anchors.top: parent.top
1487 anchors.bottom: parent.bottom
1488- model: albumTracksModel.model
1489+ model: isAlbum ? songsModel : albumTracksModel.model
1490+
1491 delegate: albumTracksDelegate
1492 header: ListItem.Standard {
1493 id: albumInfo
1494@@ -125,8 +140,8 @@
1495 anchors.right: parent.right
1496 anchors.rightMargin: units.gu(1.5)
1497 elide: Text.ElideRight
1498- text: isAlbum ? i18n.tr(year + " | %1 song", year + " | %1 songs", albumTracksModel.model.count).arg(albumTracksModel.model.count)
1499- : i18n.tr("%1 song", "%1 songs", albumTracksModel.model.count).arg(albumTracksModel.model.count)
1500+ text: isAlbum ? i18n.tr(year + " | %1 song", year + " | %1 songs", albumtrackslist.count).arg(albumtrackslist.count)
1501+ : i18n.tr("%1 song", "%1 songs", albumtrackslist.count).arg(albumtrackslist.count)
1502
1503 }
1504
1505@@ -162,13 +177,14 @@
1506 MouseArea {
1507 anchors.fill: parent
1508 onClicked: {
1509- trackClicked(albumTracksModel, 0) // play track
1510- if (isAlbum) {
1511- Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.cover, sheetItem.line2, "album")
1512+ trackClicked(albumtrackslist.model, 0) // play track
1513+
1514+ if (isAlbum && sheetItem.line1 != "Genre") {
1515+ Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.covers[0], sheetItem.line2, "album")
1516 mainView.hasRecent = true
1517 recentModel.filterRecent()
1518 } else if (sheetItem.line1 == "Playlist") {
1519- Library.addRecent(sheetItem.line2, "Playlist", sheetItem.cover, sheetItem.line2, "playlist")
1520+ Library.addRecent(sheetItem.line2, "Playlist", sheetItem.covers[0], sheetItem.line2, "playlist")
1521 mainView.hasRecent = true
1522 recentModel.filterRecent()
1523 }
1524@@ -213,7 +229,7 @@
1525 MouseArea {
1526 anchors.fill: parent
1527 onClicked: {
1528- addQueueFromModel(albumTracksModel)
1529+ addQueueFromModel(albumtrackslist.model)
1530 }
1531 }
1532 }
1533@@ -237,13 +253,13 @@
1534 if (focus == false) {
1535 focus = true
1536 }
1537- trackClicked(albumTracksModel, index) // play track
1538- if (isAlbum) {
1539- Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.cover, sheetItem.line2, "album")
1540+ trackClicked(albumtrackslist.model, index) // play track
1541+ if (isAlbum && sheetItem.line1 != "Genre") {
1542+ Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.covers[0], sheetItem.line2, "album")
1543 mainView.hasRecent = true
1544 recentModel.filterRecent()
1545 } else if (sheetItem.line1 == "Playlist") {
1546- Library.addRecent(sheetItem.line2, "Playlist", sheetItem.cover, sheetItem.line2, "playlist")
1547+ Library.addRecent(sheetItem.line2, "Playlist", sheetItem.covers[0], sheetItem.line2, "playlist")
1548 mainView.hasRecent = true
1549 recentModel.filterRecent()
1550 }
1551@@ -275,7 +291,7 @@
1552 height: styleMusic.common.albumSize
1553 visible: !isAlbum
1554 image: Image {
1555- source: model.cover !== "" ? model.cover : Qt.resolvedUrl("../images/music-app-cover@30.png")
1556+ source: "image://albumart/artist=" + model.author + "&album=" + model.album
1557 onStatusChanged: {
1558 if (status === Image.Error) {
1559 source = Qt.resolvedUrl("../images/music-app-cover@30.png")
1560@@ -300,7 +316,7 @@
1561 rightMargin: units.gu(1.5)
1562 }
1563 elide: Text.ElideRight
1564- text: model.artist
1565+ text: model.author
1566 }
1567
1568 Label {
1569@@ -348,7 +364,7 @@
1570 fill: parent
1571 }
1572 listItem: track
1573- model: albumTracksModel.model.get(index)
1574+ model: isAlbum ? albumtrackslist.model.get(index, albumTracksModel.model.RoleModelData) : albumtrackslist.model.get(index)
1575 row: Row {
1576 AddToPlaylist {
1577
1578@@ -360,10 +376,10 @@
1579 }
1580
1581 Component.onCompleted: {
1582- if (index === 0)
1583+ if (model.date !== undefined && sheetItem.year === "")
1584 {
1585- sheetItem.file = model.file;
1586- sheetItem.year = model.year;
1587+ sheetItem.file = model.filename;
1588+ sheetItem.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
1589 }
1590 }
1591 }
1592
1593=== modified file 'debian/control'
1594--- debian/control 2014-06-06 11:35:53 +0000
1595+++ debian/control 2014-06-19 01:49:30 +0000
1596@@ -13,18 +13,21 @@
1597
1598 Package: music-app
1599 Architecture: all
1600-Depends: grilo-plugins-0.2-mediascanner,
1601- mediascanner,
1602+Depends: mediascanner2.0,
1603+ gstreamer0.10-fluendo-mp3,
1604+ gstreamer1.0-fluendo-mp3,
1605 qmlscene,
1606 qtdeclarative5-localstorage-plugin,
1607 qtdeclarative5-particles-plugin,
1608- qtdeclarative5-qtgrilo0.1,
1609 qtdeclarative5-qtmultimedia-plugin,
1610 qtdeclarative5-qtquick2-plugin,
1611+ qtdeclarative5-ubuntu-mediascanner0.1,
1612+ qtdeclarative5-ubuntu-thumbnailer0.1,
1613 qtdeclarative5-ubuntu-ui-toolkit-plugin,
1614 qtdeclarative5-usermetrics0.1,
1615 qtdeclarative5-window-plugin,
1616 qtdeclarative5-xmllistmodel-plugin,
1617+ thumbnailer-service,
1618 suru-icon-theme | ubuntu-mobile-icons,
1619 ${misc:Depends},
1620 Description: Music player for Ubuntu Touch
1621@@ -32,9 +35,7 @@
1622
1623 Package: music-app-autopilot
1624 Architecture: all
1625-Depends: gstreamer0.10-fluendo-mp3,
1626- gstreamer1.0-fluendo-mp3,
1627- libautopilot-qt (>= 1.4),
1628+Depends: libautopilot-qt (>= 1.4),
1629 libqt5test5,
1630 music-app (= ${source:Version}),
1631 python-mock,
1632
1633=== modified file 'meta-database.js'
1634--- meta-database.js 2014-05-02 04:03:50 +0000
1635+++ meta-database.js 2014-06-19 01:49:30 +0000
1636@@ -1,6 +1,8 @@
1637 /*
1638- * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>
1639- * Daniel Holm <d.holmen@gmail.com>
1640+ * Copyright (C) 2013, 2014
1641+ * Andrew Hayzen <ahayzen@gmail.com>
1642+ * Daniel Holm <d.holmen@gmail.com>
1643+ * Victor Thompson <victor.thompson@gmail.com>
1644 *
1645 * This program is free software; you can redistribute it and/or modify
1646 * it under the terms of the GNU General Public License as published by
1647@@ -15,9 +17,6 @@
1648 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1649 */
1650
1651-var buffer = []; // Buffer of metadata to write to the db
1652-var maxBufferLength = 8000; // Maximum size of buffer before auto write to db
1653-
1654 // First, let's create a short helper function to get the database connection
1655 function getDatabase() {
1656 return LocalStorage.openDatabaseSync("music-app-metadata", "1.0", "StorageDatabase", 1000000);
1657@@ -30,19 +29,8 @@
1658 function(tx) {
1659 // Create the table if it doesn't already exist
1660 // If the table exists, this is skipped
1661- //tx.executeSql('DROP TABLE metadata');
1662- tx.executeSql('CREATE TABLE IF NOT EXISTS metadata(file TEXT UNIQUE, title TEXT, artist TEXT, album TEXT, cover TEXT, year TEXT, number TEXT, length TEXT, genre TEXT)');
1663- createRecent();
1664- });
1665-}
1666-function reset() {
1667- var db = getDatabase();
1668- db.transaction(
1669- function(tx) {
1670- // Create the table if it doesn't already exist
1671- // If the table exists, this is skipped
1672- tx.executeSql('DROP TABLE IF EXISTS metadata');
1673- tx.executeSql('CREATE TABLE IF NOT EXISTS metadata(file TEXT UNIQUE, title TEXT, artist TEXT, album TEXT, cover TEXT, year TEXT, number TEXT, length TEXT, genre TEXT)');
1674+ tx.executeSql('DROP TABLE IF EXISTS metadata'); // TODO: drop recent as well to reset data
1675+
1676 createRecent();
1677 });
1678 }
1679@@ -67,348 +55,6 @@
1680 });
1681 }
1682
1683-// This function is used to flush the buffer of metadata to the db
1684-function writeDb()
1685-{
1686- var db = getDatabase();
1687- var res = "";
1688- var i;
1689-
1690- console.debug("Writing DB");
1691- console.debug(buffer.length);
1692-
1693- // Keep within one transaction for performance win
1694- db.transaction(function(tx) {
1695- // Loop through all the metadata in the buffer
1696- for (i=0; i < buffer.length; i++)
1697- {
1698- var res = tx.executeSql('INSERT OR REPLACE INTO metadata VALUES (?,?,?,?,?,?,?,?,?);', buffer[i]);
1699-
1700- if (res.rowsAffected <= 0)
1701- {
1702- // Nothing was added error occured?
1703- console.debug("Error occured writing to db for ", buffer[i]);
1704- }
1705- }
1706- });
1707-
1708- buffer = []; // Clear buffer
1709-}
1710-
1711-// This function is used to write meta data into the database
1712-function setMetadata(record) {
1713- buffer.push([record.file,record.title,record.artist,record.album,record.cover,record.year,record.number,record.length,record.genre]); // Add metadata to buffer
1714-
1715- if (buffer.length >= maxBufferLength)
1716- {
1717- console.debug("Buffer full, flushing buffer to disk");
1718- writeDb();
1719- }
1720-}
1721-
1722-
1723-function removeFiles(files)
1724-{
1725- var db = getDatabase();
1726-
1727- db.transaction(function(tx) {
1728- for (var i=0; i < files.length; i++)
1729- {
1730- for (var k in files[i])
1731- {
1732- tx.executeSql('DELETE FROM metadata WHERE file=?;', files[i]["file"]);
1733- }
1734- }
1735- });
1736-}
1737-
1738-// This function is used to retrieve meta data from the database
1739-function getMetadata(file) {
1740- var db = getDatabase();
1741- var res="";
1742-
1743- try {
1744- db.transaction(function(tx) {
1745- var rs = tx.executeSql('SELECT * FROM metadata WHERE file=?;', [file]); // tries to get the title of track
1746-
1747- if (rs.rows.length > 0) {
1748- res = rs.rows.item(0);
1749- } else {
1750- res = "Unknown";
1751- }
1752- })
1753- } catch(e) {
1754- return "";
1755- }
1756-
1757- // The function returns “Unknown” if the setting was not found in the database
1758- // For more advanced projects, this should probably be handled through error codes
1759- return res
1760-}
1761-
1762-// This function is used to retrieve meta data from the database
1763-function hasCover(file) {
1764- var db = getDatabase();
1765- var res = false;
1766-
1767- try {
1768- db.transaction(function(tx) {
1769- var rs = tx.executeSql('SELECT cover FROM metadata WHERE file = ?;', [file]); // tries to get the cover art of track
1770-
1771- if (rs.rows.length > 0) {
1772- res = rs.rows.item(0).cover !== ""
1773- }
1774- })
1775- } catch(e) {
1776- return false;
1777- }
1778-
1779- // The function returns false if cover art was not found in the database
1780- return res
1781-}
1782-
1783-
1784-function printValues() {
1785- var db = getDatabase();
1786- db.transaction( function(tx) {
1787- var rs = tx.executeSql("SELECT * FROM metadata");
1788- for(var i = 0; i < rs.rows.length; i++) {
1789- var dbItem = rs.rows.item(i);
1790- console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover);
1791- }
1792- });
1793-}
1794-
1795-
1796-function getAll() {
1797- var res = [];
1798- var db = getDatabase();
1799- db.transaction( function(tx) {
1800- var rs = tx.executeSql("SELECT * FROM metadata ORDER BY title COLLATE NOCASE ASC, artist COLLATE NOCASE ASC, album COLLATE NOCASE ASC, CAST(number AS int) ASC");
1801- for(var i = 0; i < rs.rows.length; i++) {
1802- var dbItem = rs.rows.item(i);
1803- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1804- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1805- }
1806- });
1807- return res;
1808-}
1809-
1810-function getAllFileOrder() {
1811- var res = [];
1812- var db = getDatabase();
1813- db.transaction( function(tx) {
1814- var rs = tx.executeSql("SELECT * FROM metadata ORDER BY file COLLATE NOCASE ASC");
1815- for(var i = 0; i < rs.rows.length; i++) {
1816- var dbItem = rs.rows.item(i);
1817- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1818- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, number:dbItem.number, year:dbItem.year, genre:dbItem.genre});
1819- }
1820- });
1821- return res;
1822-}
1823-
1824-function getArtists() {
1825- var res = [];
1826- var db = getDatabase();
1827- db.transaction( function(tx) {
1828- var rs = tx.executeSql("SELECT * FROM metadata GROUP BY artist ORDER BY artist COLLATE NOCASE ASC");
1829- for(var i = 0; i < rs.rows.length; i++) {
1830- var dbItem = rs.rows.item(i);
1831- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1832- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1833- }
1834- });
1835- return res;
1836-}
1837-
1838-function getArtistTracks(artist) {
1839- var res = [];
1840- var db = getDatabase();
1841- db.transaction( function(tx) {
1842- var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? ORDER BY artist COLLATE NOCASE ASC, year ASC, CAST(number AS int) ASC", [artist]);
1843- for(var i = 0; i < rs.rows.length; i++) {
1844- var dbItem = rs.rows.item(i);
1845- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1846- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1847- }
1848- });
1849- return res;
1850-}
1851-
1852-function getArtistAlbums(artist) {
1853- var res = [];
1854- var db = getDatabase();
1855- db.transaction( function(tx) {
1856- var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? GROUP BY album ORDER BY year ASC, CAST(number AS int) ASC", [artist]);
1857- for(var i = 0; i < rs.rows.length; i++) {
1858- var dbItem = rs.rows.item(i);
1859- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1860- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1861- }
1862- });
1863- return res;
1864-}
1865-
1866-function getArtistCovers(artist) {
1867- var res = [];
1868- var db = getDatabase();
1869- try {
1870- db.transaction( function(tx) {
1871- var rs = tx.executeSql("SELECT cover FROM metadata WHERE artist=? AND cover <> '' ORDER BY album COLLATE NOCASE ASC", [artist]);
1872- for(var i = 0; i < rs.rows.length; i++) {
1873- var dbItem = rs.rows.item(i);
1874- //console.log("Cover:"+ dbItem.cover+" Size:"+res.length);
1875- if (res.indexOf(dbItem.cover) == -1) res.push(dbItem.cover);
1876- }
1877- });
1878- } catch(e) {
1879- return [];
1880- }
1881-
1882- return res;
1883-}
1884-
1885-function getAlbumCover(album) {
1886- var res = "";
1887- var db = getDatabase();
1888- try {
1889- db.transaction( function(tx) {
1890- var rs = tx.executeSql("SELECT cover FROM metadata WHERE album=? ORDER BY cover DESC", [album]);
1891- var dbItem = rs.rows.item(0);
1892- //console.log("Cover:"+ dbItem.cover+" Size:"+res.length);
1893- if (rs.rows.length > 0) res = rs.rows.item(0).cover;
1894- });
1895- } catch(e) {
1896- return "";
1897- }
1898-
1899- return res;
1900-}
1901-
1902-function getArtistAlbumCount(artist) {
1903- var res = 0;
1904- var db = getDatabase();
1905- db.transaction( function(tx) {
1906- var rs = tx.executeSql("SELECT count(DISTINCT album) AS value FROM metadata WHERE artist=?", [artist]);
1907- if (rs.rows.item(0).value > 0) {
1908- res = rs.rows.item(0).value;
1909- } else {
1910- res = 0;
1911- }
1912- });
1913- return res;
1914-}
1915-
1916-function getAlbums() {
1917- var res = [];
1918- var db = getDatabase();
1919- try {
1920- db.transaction( function(tx) {
1921- var rs = tx.executeSql("SELECT * FROM metadata GROUP BY album ORDER BY album COLLATE NOCASE ASC");
1922- for(var i = 0; i < rs.rows.length; i++) {
1923- var dbItem = rs.rows.item(i);
1924- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1925- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1926- }
1927- });
1928- } catch(e) {
1929- return [];
1930- }
1931-
1932- return res;
1933-}
1934-
1935-function getAlbumTracks(album) {
1936- var res = [];
1937- var db = getDatabase();
1938- //console.log("Album: " + album);
1939- db.transaction( function(tx) {
1940- var rs = tx.executeSql("SELECT * FROM metadata WHERE album=? ORDER BY artist COLLATE NOCASE ASC, album COLLATE NOCASE ASC, CAST(number AS int) ASC", [album]);
1941- for(var i = 0; i < rs.rows.length; i++) {
1942- var dbItem = rs.rows.item(i);
1943- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1944- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1945- }
1946- });
1947- return res;
1948-}
1949-
1950-function getArtistAlbumTracks(artist, album) {
1951- var res = [];
1952- var db = getDatabase();
1953- //console.log("Album: " + album);
1954- db.transaction( function(tx) {
1955- var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? AND album=? ORDER BY artist COLLATE NOCASE ASC, album COLLATE NOCASE ASC, CAST(number AS int) ASC", [artist, album]);
1956- for(var i = 0; i < rs.rows.length; i++) {
1957- var dbItem = rs.rows.item(i);
1958- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1959- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1960- }
1961- });
1962- return res;
1963-}
1964-
1965-function getGenres() {
1966- var res = [];
1967- var db = getDatabase();
1968- db.transaction( function(tx) {
1969- var rs = tx.executeSql("SELECT *, count(genre) AS total FROM metadata GROUP BY genre ORDER BY genre COLLATE NOCASE ASC");
1970- for(var i = 0; i < rs.rows.length; i++) {
1971- var dbItem = rs.rows.item(i);
1972- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1973- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre, total: dbItem.total});
1974- }
1975- });
1976- return res;
1977-}
1978-
1979-function getGenreTracks(genre) {
1980- var res = [];
1981- var db = getDatabase();
1982- //console.log("Genre: " + genre);
1983- db.transaction( function(tx) {
1984- var rs = tx.executeSql("SELECT * FROM metadata WHERE genre=? ORDER BY artist COLLATE NOCASE ASC, album COLLATE NOCASE ASC, CAST(number AS int) ASC", [genre]);
1985- for(var i = 0; i < rs.rows.length; i++) {
1986- var dbItem = rs.rows.item(i);
1987- //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
1988- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
1989- }
1990- });
1991- return res;
1992-}
1993-
1994-function getGenreCovers(genre) {
1995- var res = [];
1996- var db = getDatabase();
1997- try {
1998- db.transaction( function(tx) {
1999- var rs = tx.executeSql("SELECT cover FROM metadata WHERE genre=? AND cover <> '' ORDER BY artist COLLATE NOCASE ASC", [genre]);
2000- for(var i = 0; i < rs.rows.length; i++) {
2001- if (res.indexOf(rs.rows.item(i).cover) === -1) {
2002- res.push(rs.rows.item(i).cover);
2003- }
2004- }
2005- });
2006- } catch(e) {
2007- return [];
2008- }
2009-
2010- return res;
2011-}
2012-
2013-
2014-function size() {
2015- var db = getDatabase();
2016- var res="";
2017-
2018- db.transaction( function(tx) {
2019- var rs = tx.executeSql("SELECT count(*) FROM metadata");
2020- res = rs.rows.item(0).value;
2021- });
2022- return res;
2023-}
2024-
2025 // This function is used to insert a recent item into the database
2026 function addRecent(title, title2, cover, key, type) {
2027 var db = getDatabase();
2028@@ -436,21 +82,20 @@
2029 var rs = tx.executeSql("SELECT * FROM recent ORDER BY time DESC LIMIT 15");
2030 for(var i = 0; i < rs.rows.length; i++) {
2031 var dbItem = rs.rows.item(i);
2032- console.log("Time:"+ dbItem.time + ", Key:"+dbItem.key + ", Title:"+dbItem.title + ", Title2:"+dbItem.title2 + ", Cover:"+dbItem.cover + ", Type:"+dbItem.type);
2033+ console.log("Time:"+ dbItem.time + ", Key:"+dbItem.key + ", Title:"+dbItem.title + ", Title2:"+dbItem.title2 + ", Type:"+dbItem.type);
2034
2035 if (dbItem.type === "album")
2036 {
2037 res.push({time:dbItem.time,
2038 title:dbItem.title || i18n.tr("Unknown Album"),
2039 title2:dbItem.title2 || i18n.tr("Unknown Artist"),
2040- cover:dbItem.cover,
2041 key:dbItem.key || i18n.tr("Unknown Album"),
2042 type:dbItem.type
2043 });
2044 }
2045 else
2046 {
2047- res.push({time:dbItem.time, title:dbItem.title, title2:dbItem.title2, cover:dbItem.cover, key:dbItem.key, type:dbItem.type});
2048+ res.push({time:dbItem.time, title:dbItem.title, title2:dbItem.title2, key:dbItem.key, type:dbItem.type});
2049 }
2050 }
2051 });
2052@@ -474,20 +119,3 @@
2053 );
2054 return res === 0;
2055 }
2056-
2057-// Search track LIKE
2058-function search(input) {
2059- console.debug("Got a new search: "+input)
2060- input = "%" + input + "%" // workaround
2061- var res = [];
2062- var db = getDatabase();
2063- db.transaction( function(tx) {
2064- var rs = tx.executeSql("SELECT * FROM metadata WHERE title LIKE ? OR artist LIKE ? OR album LIKE ? OR genre LIKE ?;", [input,input,input,input]); // WRONG! WHy?
2065- for(var i = 0; i < rs.rows.length; i++) {
2066- var dbItem = rs.rows.item(i);
2067- console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
2068- res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
2069- }
2070- });
2071- return res;
2072-}
2073
2074=== modified file 'music-app.qml'
2075--- music-app.qml 2014-05-08 22:58:35 +0000
2076+++ music-app.qml 2014-06-19 01:49:30 +0000
2077@@ -1,7 +1,8 @@
2078 /*
2079- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
2080- * Daniel Holm <d.holmen@gmail.com>
2081- * Victor Thompson <victor.thompson@gmail.com>
2082+ * Copyright (C) 2013, 2014
2083+ * Andrew Hayzen <ahayzen@gmail.com>
2084+ * Daniel Holm <d.holmen@gmail.com>
2085+ * Victor Thompson <victor.thompson@gmail.com>
2086 *
2087 * This program is free software; you can redistribute it and/or modify
2088 * it under the terms of the GNU General Public License as published by
2089@@ -21,8 +22,8 @@
2090 import Ubuntu.Components.ListItems 0.1
2091 import Ubuntu.Components.Popups 0.1
2092 import Ubuntu.Components.ListItems 0.1 as ListItem
2093+import Ubuntu.MediaScanner 0.1
2094 import Ubuntu.Unity.Action 1.0 as UnityActions
2095-import org.nemomobile.grilo 0.1
2096 import QtMultimedia 5.0
2097 import QtQuick.LocalStorage 2.0
2098 import QtQuick.XmlListModel 2.0
2099@@ -199,9 +200,6 @@
2100 actions: [searchAction, nextAction, playsAction, prevAction, stopAction, backAction]
2101
2102 // signal to open new URIs
2103- // TODO currently this only allows playing file:// URIs of known files
2104- // (already in the database), not e.g. http:// URIs or files in directories
2105- // not picked up by Grilo
2106 Connections {
2107 id: uriHandler
2108 target: UriHandler
2109@@ -214,33 +212,29 @@
2110 return;
2111 }
2112
2113- // Get tracks
2114- var tracks = Library.getArtistAlbumTracks(decodeURIComponent(split[0]), decodeURIComponent(split[1]));
2115+ // Filter by artist and album
2116+ songsAlbumArtistModel.artist = decodeURIComponent(split[0]);
2117+ songsAlbumArtistModel.album = decodeURIComponent(split[1]);
2118
2119- if (tracks.length === 0) {
2120+ // Play album it tracks exist
2121+ if (songsAlbumArtistModel.rowCount > 0) {
2122+ trackClicked(songsAlbumArtistModel, 0, true);
2123+ }
2124+ else {
2125 console.debug("Unknown artist-album " + uri + ", skipping")
2126 return;
2127 }
2128-
2129- // Enqueue
2130- for (var track in tracks) {
2131- trackQueue.append(tracks[track]);
2132- }
2133-
2134- // Play first track
2135- trackClicked(trackQueue, 0, true);
2136 }
2137
2138 function processFile(uri, play) {
2139 uri = decodeURIComponent(uri);
2140
2141- // search for path in library
2142- var library = Library.getAll();
2143 var track = false;
2144
2145- for (var item in library) {
2146- if (decodeURIComponent(library[item].file) === uri) {
2147- track = library[item];
2148+ // Search for track in songs model
2149+ for (var i=0; i < allSongsModel.rowCount; i++) {
2150+ if (decodeURIComponent(allSongsModel.get(i, allSongsModel.RoleModelData).filename) === uri) {
2151+ track = allSongsModel.get(i, allSongsModel.RoleModelData);
2152 break;
2153 }
2154 }
2155@@ -255,7 +249,7 @@
2156
2157 // play first URI
2158 if (play) {
2159- trackClicked(trackQueue, 0, true)
2160+ trackQueueClick(0);
2161 }
2162 }
2163
2164@@ -340,8 +334,7 @@
2165 Settings.setSetting("repeat", "0") // default state of repeat
2166 //Settings.setSetting("scrobble", "0") // default state of scrobble
2167 }
2168- Library.reset()
2169- //Library.initialize();
2170+ Library.initialize();
2171
2172 // initialize playlists
2173 Playlists.initializePlaylists()
2174@@ -355,13 +348,27 @@
2175 // push the page to view
2176 pageStack.push(tabs)
2177
2178+ loadedUI = true;
2179+
2180 // TODO: Switch tabs back and forth to get the background color in the
2181 // header to work properly.
2182 tabs.selectedTabIndex = 1
2183 tabs.selectedTabIndex = 0
2184+
2185+ // Run post load
2186+ tabs.ensurePopulated(tabs.selectedTab);
2187+
2188+ if (args.values.url) {
2189+ uriHandler.process(args.values.url, true);
2190+ }
2191+
2192+ // Show toolbar and start timer if there is music
2193+ if (!emptyPage.noMusic) {
2194+ musicToolbar.showToolbar();
2195+ musicToolbar.startAutohideTimer();
2196+ }
2197 }
2198
2199-
2200 // VARIABLES
2201 property string musicName: i18n.tr("Music")
2202 property string appVersion: '1.2'
2203@@ -371,16 +378,13 @@
2204 property string lastfmpassword
2205 property string timestamp // used to scrobble
2206 property var chosenElement: null
2207- property LibraryListModel currentModel: null // Current model being used
2208- property var currentQuery: null
2209- property var currentParam: null
2210- property bool queueChanged: false
2211 property bool toolbarShown: musicToolbar.shown
2212 signal collapseExpand();
2213 signal collapseSwipeDelete(int index);
2214 signal onToolbarShownChanged(bool shown, var currentPage, var currentTab)
2215
2216 property bool wideAspect: width >= units.gu(70)
2217+ property bool loadedUI: false // property to detect if the UI has finished
2218
2219 // FUNCTIONS
2220
2221@@ -393,28 +397,20 @@
2222 }
2223 }
2224
2225- // Add items from a stored query in libraryModel into the queue
2226- function addQueueFromModel(libraryModel)
2227+ function addQueueFromModel(model)
2228 {
2229- var items;
2230-
2231- if (libraryModel.query === null)
2232- {
2233- return
2234- }
2235-
2236- if (libraryModel.param === null)
2237- {
2238- items = libraryModel.query()
2239- }
2240- else
2241- {
2242- items = libraryModel.query(libraryModel.param)
2243- }
2244-
2245- for (var key in items)
2246- {
2247- trackQueue.append(items[key])
2248+ // TODO: remove once playlists uses U1DB
2249+ if (model.hasOwnProperty("linkLibraryListModel")) {
2250+ model = model.linkLibraryListModel;
2251+ }
2252+
2253+ for (var i=0; i < model.rowCount; i++) {
2254+ var item = model.get(i, model.RoleModelData);
2255+ if (item.art !== undefined && (item.art === "" || item.art === null)) {
2256+ item.art = "image://albumart/artist=" + item.author + "&album=" + item.album
2257+ }
2258+
2259+ trackQueue.model.append(makeDict(item));
2260 }
2261 }
2262
2263@@ -430,64 +426,39 @@
2264 return minutes + ":" + (seconds<10 ? "0"+seconds : seconds);
2265 }
2266
2267- function trackClicked(libraryModel, index, play)
2268- {
2269+ // Make dictionary from model item
2270+ function makeDict(model) {
2271+ return {
2272+ album: model.album,
2273+ author: model.author,
2274+ filename: model.filename,
2275+ title: model.title
2276+ };
2277+ }
2278+
2279+ function trackClicked(model, index, play) {
2280+ // TODO: remove once playlists uses U1DB
2281+ if (model.hasOwnProperty("linkLibraryListModel")) {
2282+ model = model.linkLibraryListModel;
2283+ }
2284+
2285+ var file = Qt.resolvedUrl(model.get(index, model.RoleModelData).filename);
2286+
2287 play = play === undefined ? true : play // default play to true
2288
2289- if (index > libraryModel.model.count - 1 || index < 0) {
2290- customdebug("Incorrect index given to trackClicked.")
2291+ // If same track and on now playing page then toggle
2292+ if (musicToolbar.currentPage === nowPlaying &&
2293+ Qt.resolvedUrl(trackQueue.model.get(player.currentIndex).filename) === file) {
2294+ player.toggle()
2295 return;
2296 }
2297
2298- var file = Qt.resolvedUrl(libraryModel.model.get(index).file)
2299-
2300- // Clear the play queue and load the new tracks - if not trackQueue
2301- // Don't reload queue if model, query and parameters are the same
2302- // Same file different pages is treated as a new session
2303- if (libraryModel !== trackQueue &&
2304- (currentModel !== libraryModel ||
2305- currentQuery !== libraryModel.query ||
2306- currentParam !== libraryModel.param ||
2307- queueChanged === true))
2308- {
2309- trackQueue.model.clear()
2310- addQueueFromModel(libraryModel)
2311- }
2312- else if (player.source == file &&
2313- player.currentIndex === index)
2314- {
2315- // Same track so just toggle playing state
2316- if (play === true) {
2317- console.log("Is current track: "+player.playbackState)
2318-
2319- // Show the Now Playing page and make sure the track is visible
2320- tabs.pushNowPlaying();
2321- nowPlaying.ensureVisibleIndex = index;
2322-
2323- musicToolbar.showToolbar();
2324-
2325- if (musicToolbar.currentPage == nowPlaying) {
2326- player.toggle()
2327- }
2328- }
2329-
2330- return
2331- }
2332-
2333- // Current index must be updated before player.source
2334- currentModel = libraryModel
2335- currentQuery = libraryModel.query
2336- currentParam = libraryModel.param
2337-
2338- if (Qt.resolvedUrl(trackQueue.model.get(index).file) != file) {
2339- index = trackQueue.indexOf(file) // pick given index first
2340- }
2341- queueChanged = false
2342-
2343- console.log("Click of fileName: " + file)
2344-
2345- if (play === true) {
2346- player.playSong(file, index)
2347+ trackQueue.model.clear(); // clear the old model
2348+
2349+ addQueueFromModel(model);
2350+
2351+ if (play) {
2352+ player.playSong(file, index);
2353
2354 // Show the Now Playing page and make sure the track is visible
2355 tabs.pushNowPlaying();
2356@@ -496,34 +467,59 @@
2357 musicToolbar.showToolbar();
2358 }
2359 else {
2360- player.source = file
2361+ player.source = file;
2362 }
2363
2364 collapseExpand(); // collapse all expands if track clicked
2365-
2366- return file
2367+ }
2368+
2369+ function trackQueueClick(index) {
2370+ if (player.currentIndex === index) {
2371+ player.toggle();
2372+ }
2373+ else {
2374+ player.playSong(trackQueue.model.get(index).filename, index);
2375+ }
2376+
2377+ // Show the Now Playing page and make sure the track is visible
2378+ tabs.pushNowPlaying();
2379+ nowPlaying.ensureVisibleIndex = index;
2380+
2381+ musicToolbar.showToolbar();
2382 }
2383
2384 function playRandomSong(shuffle)
2385 {
2386 trackQueue.model.clear();
2387
2388- var items = Library.getAll();
2389-
2390- for (var key in items) {
2391- trackQueue.append(items[key]);
2392- }
2393-
2394 var now = new Date();
2395 var seed = now.getSeconds();
2396- var index = Math.floor(trackQueue.model.count * Math.random(seed));
2397-
2398- console.debug("THIS", index);
2399+ var index = Math.floor(allSongsModel.rowCount * Math.random(seed));
2400
2401 player.shuffle = shuffle === undefined ? true : shuffle;
2402- trackClicked(trackQueue,
2403- index,
2404- true);
2405+
2406+ trackClicked(allSongsModel, index, true)
2407+ }
2408+
2409+ // Load mediascanner store
2410+ MediaStore {
2411+ id: musicStore
2412+ }
2413+
2414+ SongsModel {
2415+ id: allSongsModel
2416+ // HACK: Temporarily setting limit to 500 to ensure model
2417+ // is populated. See lp:1326753
2418+ limit: 500
2419+ store: musicStore
2420+ }
2421+
2422+ SongsModel {
2423+ id: songsAlbumArtistModel
2424+ // HACK: Temporarily setting limit to 500 to ensure model
2425+ // is populated. See lp:1326753
2426+ limit: 500
2427+ store: musicStore
2428 }
2429
2430 // WHERE THE MAGIC HAPPENS
2431@@ -583,211 +579,19 @@
2432 }
2433 }
2434
2435- GriloModel {
2436- id: griloModel
2437- property bool loaded: false
2438-
2439- source: GriloBrowse {
2440- id: browser
2441- source: "grl-mediascanner"
2442- registry: registry
2443- metadataKeys: [GriloBrowse.Title]
2444- typeFilter: [GriloBrowse.Audio]
2445- Component.onCompleted: {
2446- console.log(browser.supportedKeys);
2447- console.log(browser.slowKeys);
2448- refresh();
2449- console.log("Refreshing");
2450- }
2451-
2452- onAvailableChanged: {
2453- console.log("Available ? " + available);
2454- if (available === true) {
2455- console.log("griloModel.count " + griloModel.count)
2456- }
2457- }
2458- onBaseMediaChanged: refresh();
2459-
2460- /* Check if the file (needle) exists in the library (haystack)
2461- * Searches the the haystack using a binary search
2462- *
2463- * false if the file in in grilo but not in the haystack
2464- * positive if the file is the same (number is the actual index)
2465- * negative if the file has changed, actual index is -(i + 1)
2466- */
2467- function exists(haystack, needle)
2468- {
2469- var keyToFind = needle["file"];
2470-
2471- var upper = haystack.length - 1;
2472- var lower = 0;
2473- var i = Math.floor(haystack.length / 2);
2474-
2475- while (upper >= lower)
2476- {
2477- var key = haystack[i]["file"];
2478-
2479- if (keyToFind < key)
2480- {
2481- upper = i - 1;
2482- }
2483- else if (keyToFind > key)
2484- {
2485- lower = i + 1;
2486- }
2487- else
2488- {
2489- var found = false;
2490-
2491- for (var k in haystack[i])
2492- {
2493- if (haystack[i][k] === needle[k])
2494- {
2495- found = true;
2496- }
2497- else
2498- {
2499- found = false;
2500- break;
2501- }
2502- }
2503-
2504- if (found === true)
2505- {
2506- return i; // in grilo and lib - same
2507- }
2508- else
2509- {
2510- return -i - 1; // in grilo and lib - different
2511- }
2512- }
2513-
2514- i = Math.floor((upper + lower) / 2);
2515- }
2516-
2517- return false; // in grilo not in lib
2518- }
2519-
2520- onFinished: {
2521- // FIXME: remove when grilo is fixed
2522- var files = [];
2523- var duplicates = 0;
2524-
2525- for (var i = 0; i < griloModel.count; i++)
2526- {
2527- var media = griloModel.get(i)
2528- var file = media.url.toString()
2529- if (file.indexOf("file://") === 0)
2530- {
2531- file = file.slice(7, file.length)
2532- }
2533-
2534- // FIXME: grilo can supply duplicates
2535- if (files.indexOf(file) > -1)
2536- {
2537- duplicates++;
2538- continue;
2539- }
2540- files.push(file);
2541-
2542- var record = {
2543- artist: media.artist || i18n.tr("Unknown Artist"),
2544- album: media.album || i18n.tr("Unknown Album"),
2545- title: media.title || file,
2546- file: file,
2547- cover: media.thumbnail.toString() || "",
2548- length: media.duration.toString(),
2549- number: media.trackNumber,
2550- year: media.year.toString() !== "0" ? media.year.toString(): i18n.tr("Unknown Year"),
2551- genre: media.genre || i18n.tr("Unknown Genre")
2552- };
2553-
2554- //console.log("Artist:"+ media.artist + ", Album:"+media.album + ", Title:"+media.title + ", File:"+file + ", Cover:"+media.thumbnail + ", Number:"+media.trackNumber + ", Genre:"+media.genre);
2555- Library.setMetadata(record)
2556- }
2557-
2558- Library.writeDb()
2559-
2560- console.debug("Grilo duplicates:", duplicates); // FIXME: remove when grilo is fixed
2561- griloModel.loaded = true
2562-
2563- // Show toolbar and start timer if there is music
2564- if (!emptyPage.noMusic || wideAspect) {
2565- musicToolbar.showToolbar();
2566- musicToolbar.startAutohideTimer();
2567- }
2568-
2569- tabs.ensurePopulated(tabs.selectedTab);
2570-
2571- if (args.values.url) {
2572- uriHandler.process(args.values.url, true);
2573- }
2574- }
2575- }
2576- }
2577-
2578- GriloRegistry {
2579- id: registry
2580-
2581- Component.onCompleted: {
2582- console.log("Registry is ready");
2583- loadAll();
2584- }
2585- }
2586-
2587- LibraryListModel {
2588- id: libraryModel
2589- onPreLoadCompleteChanged: {
2590- if (preLoadComplete)
2591- {
2592- loading.visible = false
2593- tracksTab.loading = false
2594- tracksTab.populated = true
2595- }
2596- }
2597- }
2598-
2599- LibraryListModel {
2600- id: artistModel
2601- onPreLoadCompleteChanged: {
2602- if (preLoadComplete)
2603- {
2604- loading.visible = false
2605- artistsTab.loading = false
2606- artistsTab.populated = true
2607- }
2608- }
2609- }
2610- LibraryListModel {
2611- id: artistTracksModel
2612- }
2613- LibraryListModel {
2614- id: artistAlbumsModel
2615- }
2616-
2617- LibraryListModel {
2618- id: albumModel
2619- onPreLoadCompleteChanged: {
2620- if (preLoadComplete)
2621- {
2622- loading.visible = false
2623- albumsTab.loading = false
2624- albumsTab.populated = true
2625- }
2626- }
2627- }
2628+ // TODO: Used by playlisttracks move to U1DB
2629 LibraryListModel {
2630 id: albumTracksModel
2631 }
2632
2633+ // TODO: used by recent items move to U1DB
2634 LibraryListModel {
2635 id: recentModel
2636 property bool complete: false
2637 onPreLoadCompleteChanged: {
2638 complete = true;
2639
2640- if (preLoadComplete && (genreModel.complete ||
2641- genreModel.query().length === 0))
2642+ if (preLoadComplete)
2643 {
2644 loading.visible = false
2645 startTab.loading = false
2646@@ -795,63 +599,29 @@
2647 }
2648 }
2649 }
2650+
2651+ // TODO: used by recent albums move to U1DB
2652 LibraryListModel {
2653 id: recentAlbumTracksModel
2654 }
2655+
2656+ // TODO: used by recent playlists move to U1DB
2657 LibraryListModel {
2658 id: recentPlaylistTracksModel
2659 }
2660
2661- LibraryListModel {
2662- id: genreModel
2663- property bool complete: false
2664- onPreLoadCompleteChanged: {
2665- complete = true;
2666-
2667- if (preLoadComplete && (recentModel.complete ||
2668- recentModel.query().length === 0))
2669- {
2670- loading.visible = false
2671- startTab.loading = false
2672- startTab.populated = true
2673- }
2674- }
2675- }
2676-
2677- LibraryListModel {
2678- id: genreTracksModel
2679- }
2680-
2681 // list of tracks on startup. This is just during development
2682 LibraryListModel {
2683 id: trackQueue
2684- Connections {
2685- target: trackQueue.model
2686- onCountChanged: queueChanged = true
2687- }
2688
2689 function append(listElement)
2690 {
2691- model.append({
2692- "album": listElement.album,
2693- "artist": listElement.artist,
2694- "cover": listElement.cover,
2695- "file": listElement.file,
2696- "title": listElement.title
2697- })
2698+ model.append(makeDict(listElement))
2699+ console.debug(JSON.stringify(makeDict(listElement)));
2700 }
2701 }
2702
2703- // list of songs, which has been removed.
2704- ListModel {
2705- id: removedTrackQueue
2706- }
2707-
2708- // list of single tracks
2709- ListModel {
2710- id: singleTracksgriloMo
2711- }
2712-
2713+ // TODO: list of playlists move to U1DB
2714 // create the listmodel to use for playlists
2715 LibraryListModel {
2716 id: playlistModel
2717@@ -866,11 +636,6 @@
2718 }
2719 }
2720
2721- // search model
2722- LibraryListModel {
2723- id: searchModel
2724- }
2725-
2726 // load sheets (after model)
2727 SongsSheet {
2728 id: songsSheet
2729@@ -1002,7 +767,7 @@
2730 title: i18n.tr("Music")
2731 visible: false
2732
2733- property bool noMusic: griloModel.count === 0 && griloModel.loaded === true
2734+ property bool noMusic: allSongsModel.rowCount === 0 && loadedUI
2735
2736 onNoMusicChanged: {
2737 if (noMusic)
2738@@ -1027,7 +792,6 @@
2739 Column {
2740 anchors.centerIn: parent
2741
2742-
2743 Label {
2744 anchors.horizontalCenter: parent.horizontalCenter
2745 color: styleMusic.libraryEmpty.labelColor
2746@@ -1059,9 +823,9 @@
2747 // First tab is all music
2748 Tab {
2749 property bool populated: false
2750- property var loader: [recentModel.filterRecent, genreModel.filterGenres, albumModel.filterAlbums]
2751+ property var loader: [recentModel.filterRecent]
2752 property bool loading: false
2753- property var model: [recentModel, genreModel, albumTracksModel]
2754+ property var model: [recentModel, albumTracksModel]
2755 id: startTab
2756 objectName: "starttab"
2757 anchors.fill: parent
2758@@ -1075,10 +839,10 @@
2759
2760 // Second tab is arists
2761 Tab {
2762- property bool populated: false
2763- property var loader: [artistModel.filterArtists]
2764+ property bool populated: true
2765+ property var loader: []
2766 property bool loading: false
2767- property var model: [artistModel, artistAlbumsModel, albumTracksModel]
2768+ property var model: []
2769 id: artistsTab
2770 objectName: "artiststab"
2771 anchors.fill: parent
2772@@ -1092,10 +856,10 @@
2773
2774 // third tab is albums
2775 Tab {
2776- property bool populated: false
2777- property var loader: [albumModel.filterAlbums]
2778+ property bool populated: true
2779+ property var loader: []
2780 property bool loading: false
2781- property var model: [albumModel, albumTracksModel]
2782+ property var model: []
2783 id: albumsTab
2784 objectName: "albumstab"
2785 anchors.fill: parent
2786@@ -1109,10 +873,10 @@
2787
2788 // fourth tab is all songs
2789 Tab {
2790- property bool populated: false
2791- property var loader: [libraryModel.populate]
2792+ property bool populated: true
2793+ property var loader: []
2794 property bool loading: false
2795- property var model: [libraryModel]
2796+ property var model: []
2797 id: tracksTab
2798 objectName: "trackstab"
2799 anchors.fill: parent
2800@@ -1158,7 +922,7 @@
2801 {
2802 allowLoading(selectedTab, true); // allow loading of the models
2803
2804- if (!selectedTab.populated && !selectedTab.loading && griloModel.loaded) {
2805+ if (!selectedTab.populated && !selectedTab.loading && loadedUI) {
2806 loading.visible = true
2807 selectedTab.loading = true
2808
2809
2810=== modified file 'playlists.js'
2811--- playlists.js 2014-03-26 12:16:43 +0000
2812+++ playlists.js 2014-06-19 01:49:30 +0000
2813@@ -182,12 +182,11 @@
2814 for(var i = 0; i < rs.rows.length; i++) {
2815 var dbItem = rs.rows.item(i);
2816 //console.log("Cover: "+ dbItem.cover);
2817- res[i] = {'file': dbItem.track,
2818+ res[i] = {'filename': dbItem.track,
2819 'title': dbItem.title,
2820- 'artist': dbItem.artist,
2821+ 'author': dbItem.artist,
2822 'album': dbItem.album,
2823- 'cover': dbItem.cover,
2824- 'year': dbItem.year,
2825+ 'date': dbItem.year,
2826 'number': dbItem.number,
2827 'length': dbItem.length,
2828 'genre': dbItem.genre,
2829@@ -227,10 +226,10 @@
2830 // Get a list of unique covers for the playlist
2831 try {
2832 db.transaction(function(tx) {
2833- var rs = tx.executeSql("SELECT * FROM playlist WHERE playlist=? AND cover <> '' ;", [playlist]);
2834+ var rs = tx.executeSql("SELECT * FROM playlist WHERE playlist=?;", [playlist]);
2835 for(var i = 0; i < rs.rows.length; i++) {
2836- if (res.indexOf(rs.rows.item(i).cover) === -1) {
2837- res.push(rs.rows.item(i).cover);
2838+ if (res.indexOf({author: rs.rows.item(i).artist, album: rs.rows.item(i).album}) === -1) {
2839+ res.push({author: rs.rows.item(i).artist, album: rs.rows.item(i).album});
2840 }
2841 }
2842 })
2843
2844=== removed file 'plugins.json'
2845--- plugins.json 2014-03-14 00:03:53 +0000
2846+++ plugins.json 1970-01-01 00:00:00 +0000
2847@@ -1,5 +0,0 @@
2848-[
2849-{
2850- "package": "qtdeclarative5-qtgrilo0.1"
2851-}
2852-]
2853
2854=== modified file 'po/com.ubuntu.music.pot'
2855--- po/com.ubuntu.music.pot 2014-06-06 11:35:53 +0000
2856+++ po/com.ubuntu.music.pot 2014-06-19 01:49:30 +0000
2857@@ -8,7 +8,7 @@
2858 msgstr ""
2859 "Project-Id-Version: music-app\n"
2860 "Report-Msgid-Bugs-To: \n"
2861-"POT-Creation-Date: 2014-06-06 12:31+0100\n"
2862+"POT-Creation-Date: 2014-06-17 13:05-0400\n"
2863 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
2864 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
2865 "Language-Team: LANGUAGE <LL@li.org>\n"
2866@@ -18,115 +18,115 @@
2867 "Content-Transfer-Encoding: 8bit\n"
2868 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2869
2870-#: ../LoginLastFM.qml:50 ../MusicSettings.qml:145 ../MusicSettings.qml:153
2871+#: ../LoginLastFM.qml:49 ../MusicSettings.qml:145 ../MusicSettings.qml:153
2872 msgid "Last.fm"
2873 msgstr ""
2874
2875-#: ../LoginLastFM.qml:56
2876+#: ../LoginLastFM.qml:55
2877 msgid "Login to be able to scrobble."
2878 msgstr ""
2879
2880-#: ../LoginLastFM.qml:64
2881+#: ../LoginLastFM.qml:63
2882 msgid "Username"
2883 msgstr ""
2884
2885-#: ../LoginLastFM.qml:74
2886+#: ../LoginLastFM.qml:73
2887 msgid "Password"
2888 msgstr ""
2889
2890-#: ../LoginLastFM.qml:96
2891+#: ../LoginLastFM.qml:95
2892 msgid "Login"
2893 msgstr ""
2894
2895-#: ../LoginLastFM.qml:103
2896+#: ../LoginLastFM.qml:102
2897 msgid "Trying to login..."
2898 msgstr ""
2899
2900-#: ../LoginLastFM.qml:117
2901+#: ../LoginLastFM.qml:116
2902 msgid "Login Successful"
2903 msgstr ""
2904
2905-#: ../LoginLastFM.qml:123
2906+#: ../LoginLastFM.qml:122
2907 msgid "Login Failed"
2908 msgstr ""
2909
2910-#: ../LoginLastFM.qml:129
2911+#: ../LoginLastFM.qml:128
2912 msgid "You forgot to set your username and/or password"
2913 msgstr ""
2914
2915-#: ../MusicAlbums.qml:33 ../MusicStart.qml:333
2916+#: ../MusicAlbums.qml:37 ../MusicStart.qml:370
2917 msgid "Albums"
2918 msgstr ""
2919
2920-#: ../MusicArtists.qml:33
2921+#: ../MusicArtists.qml:37
2922 msgid "Artists"
2923 msgstr ""
2924
2925-#: ../MusicArtists.qml:107 ../common/AlbumsSheet.qml:116
2926+#: ../MusicArtists.qml:153 ../common/AlbumsSheet.qml:124
2927 #, qt-format
2928 msgid "%1 album"
2929 msgid_plural "%1 albums"
2930 msgstr[0] ""
2931 msgstr[1] ""
2932
2933-#: ../MusicArtists.qml:124 ../MusicPlaylists.qml:186 ../MusicStart.qml:314
2934-#: ../common/SongsSheet.qml:129
2935+#: ../MusicArtists.qml:170 ../MusicPlaylists.qml:187 ../MusicStart.qml:351
2936+#: ../common/SongsSheet.qml:144
2937 #, qt-format
2938 msgid "%1 song"
2939 msgid_plural "%1 songs"
2940 msgstr[0] ""
2941 msgstr[1] ""
2942
2943-#: ../MusicNowPlaying.qml:33
2944+#: ../MusicNowPlaying.qml:34
2945 msgid "Now Playing"
2946 msgstr ""
2947
2948 #. TRANSLATORS: this is the name of the playlists page shown in the tab header.
2949 #. Remember to keep the translation short to fit the screen width
2950-#: ../MusicPlaylists.qml:38
2951+#: ../MusicPlaylists.qml:39
2952 msgid "Playlists"
2953 msgstr ""
2954
2955 #. TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist
2956-#: ../MusicPlaylists.qml:59
2957+#: ../MusicPlaylists.qml:60
2958 msgid "Change name"
2959 msgstr ""
2960
2961-#: ../MusicPlaylists.qml:60
2962+#: ../MusicPlaylists.qml:61
2963 msgid "Enter the new name of the playlist."
2964 msgstr ""
2965
2966-#: ../MusicPlaylists.qml:71
2967+#: ../MusicPlaylists.qml:72
2968 msgid "Change"
2969 msgstr ""
2970
2971-#: ../MusicPlaylists.qml:84
2972+#: ../MusicPlaylists.qml:85
2973 msgid "You didn't type in a name."
2974 msgstr ""
2975
2976-#: ../MusicPlaylists.qml:89 ../MusicPlaylists.qml:115 ../music-app.qml:987
2977+#: ../MusicPlaylists.qml:90 ../MusicPlaylists.qml:116 ../music-app.qml:752
2978 msgid "Cancel"
2979 msgstr ""
2980
2981 #. TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist
2982-#: ../MusicPlaylists.qml:102
2983+#: ../MusicPlaylists.qml:103
2984 msgid "Are you sure?"
2985 msgstr ""
2986
2987-#: ../MusicPlaylists.qml:103
2988+#: ../MusicPlaylists.qml:104
2989 msgid "This will delete your playlist."
2990 msgstr ""
2991
2992-#: ../MusicPlaylists.qml:106
2993+#: ../MusicPlaylists.qml:107
2994 msgid "Remove"
2995 msgstr ""
2996
2997-#: ../MusicSearch.qml:42 ../MusicToolbar.qml:508 ../music-app.qml:93
2998-#: ../music-app.qml:141 ../music-app.qml:147
2999+#: ../MusicSearch.qml:44 ../MusicToolbar.qml:508 ../music-app.qml:94
3000+#: ../music-app.qml:142 ../music-app.qml:148
3001 msgid "Search"
3002 msgstr ""
3003
3004-#: ../MusicSettings.qml:30 ../music-app.qml:191
3005+#: ../MusicSettings.qml:30 ../music-app.qml:192
3006 msgid "Settings"
3007 msgstr ""
3008
3009@@ -212,20 +212,20 @@
3010 msgid "Clean everything!"
3011 msgstr ""
3012
3013-#: ../MusicStart.qml:33 ../music-app.qml:366 ../music-app.qml:1002
3014+#: ../MusicStart.qml:37 ../music-app.qml:373 ../music-app.qml:767
3015 #: com.ubuntu.music_music.desktop.in.in.h:1
3016 msgid "Music"
3017 msgstr ""
3018
3019-#: ../MusicStart.qml:80
3020+#: ../MusicStart.qml:84
3021 msgid "Recent"
3022 msgstr ""
3023
3024-#: ../MusicStart.qml:108
3025+#: ../MusicStart.qml:112
3026 msgid "Clear History"
3027 msgstr ""
3028
3029-#: ../MusicStart.qml:217
3030+#: ../MusicStart.qml:222
3031 msgid "Genres"
3032 msgstr ""
3033
3034@@ -237,38 +237,38 @@
3035 msgid "Tap play or any item to start"
3036 msgstr ""
3037
3038-#: ../MusicTracks.qml:33
3039+#: ../MusicTracks.qml:37
3040 msgid "Songs"
3041 msgstr ""
3042
3043-#: ../MusicaddtoPlaylist.qml:38 ../common/ExpanderItems/AddToPlaylist.qml:59
3044-#: ../music-app.qml:934
3045+#: ../MusicaddtoPlaylist.qml:39 ../common/ExpanderItems/AddToPlaylist.qml:59
3046+#: ../music-app.qml:699
3047 msgid "Select playlist"
3048 msgstr ""
3049
3050-#: ../MusicaddtoPlaylist.qml:130
3051+#: ../MusicaddtoPlaylist.qml:131
3052 msgid "New playlist"
3053 msgstr ""
3054
3055-#: ../common/AlbumsSheet.qml:163 ../common/AlbumsSheet.qml:351
3056-#: ../common/SongsSheet.qml:158
3057+#: ../common/AlbumsSheet.qml:180 ../common/AlbumsSheet.qml:382
3058+#: ../common/SongsSheet.qml:173
3059 msgid "Play all"
3060 msgstr ""
3061
3062-#: ../common/AlbumsSheet.qml:214 ../common/AlbumsSheet.qml:399
3063-#: ../common/ExpanderItems/AddToQueue.qml:45 ../common/SongsSheet.qml:209
3064-#: ../music-app.qml:909
3065+#: ../common/AlbumsSheet.qml:228 ../common/AlbumsSheet.qml:427
3066+#: ../common/ExpanderItems/AddToQueue.qml:45 ../common/SongsSheet.qml:225
3067+#: ../music-app.qml:674
3068 msgid "Add to queue"
3069 msgstr ""
3070
3071-#: ../common/AlbumsSheet.qml:323 ../common/SongsSheet.qml:128
3072+#: ../common/AlbumsSheet.qml:352 ../common/SongsSheet.qml:143
3073 #, qt-format
3074 msgid " | %1 song"
3075 msgid_plural " | %1 songs"
3076 msgstr[0] ""
3077 msgstr[1] ""
3078
3079-#: ../common/ExpanderItems/AddToPlaylist.qml:47 ../music-app.qml:923
3080+#: ../common/ExpanderItems/AddToPlaylist.qml:47 ../music-app.qml:688
3081 msgid "Add to playlist"
3082 msgstr ""
3083
3084@@ -290,112 +290,104 @@
3085 msgid "Clear"
3086 msgstr ""
3087
3088-#: ../meta-database.js:444 ../meta-database.js:447 ../music-app.qml:695
3089+#: ../meta-database.js:90 ../meta-database.js:92
3090 msgid "Unknown Album"
3091 msgstr ""
3092
3093-#: ../meta-database.js:445 ../music-app.qml:694
3094+#: ../meta-database.js:91
3095 msgid "Unknown Artist"
3096 msgstr ""
3097
3098-#: ../music-app.qml:142
3099+#: ../music-app.qml:143
3100 msgid "Search Track"
3101 msgstr ""
3102
3103-#: ../music-app.qml:154
3104+#: ../music-app.qml:155
3105 msgid "Next"
3106 msgstr ""
3107
3108-#: ../music-app.qml:155
3109+#: ../music-app.qml:156
3110 msgid "Next Track"
3111 msgstr ""
3112
3113-#: ../music-app.qml:161
3114+#: ../music-app.qml:162
3115 msgid "Pause"
3116 msgstr ""
3117
3118-#: ../music-app.qml:161
3119+#: ../music-app.qml:162
3120 msgid "Play"
3121 msgstr ""
3122
3123-#: ../music-app.qml:163
3124+#: ../music-app.qml:164
3125 msgid "Pause Playback"
3126 msgstr ""
3127
3128-#: ../music-app.qml:163
3129+#: ../music-app.qml:164
3130 msgid "Continue or start playback"
3131 msgstr ""
3132
3133-#: ../music-app.qml:168
3134+#: ../music-app.qml:169
3135 msgid "Back"
3136 msgstr ""
3137
3138-#: ../music-app.qml:169
3139+#: ../music-app.qml:170
3140 msgid "Go back to last page"
3141 msgstr ""
3142
3143-#: ../music-app.qml:177
3144+#: ../music-app.qml:178
3145 msgid "Previous"
3146 msgstr ""
3147
3148-#: ../music-app.qml:178
3149+#: ../music-app.qml:179
3150 msgid "Previous Track"
3151 msgstr ""
3152
3153-#: ../music-app.qml:183
3154+#: ../music-app.qml:184
3155 msgid "Stop"
3156 msgstr ""
3157
3158-#: ../music-app.qml:184
3159+#: ../music-app.qml:185
3160 msgid "Stop Playback"
3161 msgstr ""
3162
3163-#: ../music-app.qml:192
3164+#: ../music-app.qml:193
3165 msgid "Music Settings"
3166 msgstr ""
3167
3168 #. TRANSLATORS: this refers to a number of songs greater than one. The actual number will be prepended to the string automatically (plural forms are not yet fully supported in usermetrics, the library that displays that string)
3169-#: ../music-app.qml:294
3170+#: ../music-app.qml:288
3171 msgid "songs played today"
3172 msgstr ""
3173
3174-#: ../music-app.qml:295
3175+#: ../music-app.qml:289
3176 msgid "No songs played today"
3177 msgstr ""
3178
3179-#: ../music-app.qml:392
3180+#: ../music-app.qml:396
3181 msgid "Debug: "
3182 msgstr ""
3183
3184-#: ../music-app.qml:701
3185-msgid "Unknown Year"
3186-msgstr ""
3187-
3188-#: ../music-app.qml:702
3189-msgid "Unknown Genre"
3190-msgstr ""
3191-
3192-#: ../music-app.qml:947
3193+#: ../music-app.qml:712
3194 msgid "New Playlist"
3195 msgstr ""
3196
3197-#: ../music-app.qml:948
3198+#: ../music-app.qml:713
3199 msgid "Name your playlist."
3200 msgstr ""
3201
3202-#: ../music-app.qml:952
3203+#: ../music-app.qml:717
3204 msgid "Name"
3205 msgstr ""
3206
3207-#: ../music-app.qml:960
3208+#: ../music-app.qml:725
3209 msgid "Create"
3210 msgstr ""
3211
3212-#: ../music-app.qml:976
3213+#: ../music-app.qml:741
3214 msgid "Error: "
3215 msgstr ""
3216
3217-#: ../music-app.qml:981
3218+#: ../music-app.qml:746
3219 msgid "Error: You didn't type a name."
3220 msgstr ""
3221
3222
3223=== modified file 'po/lv.po'
3224--- po/lv.po 2014-06-07 06:34:52 +0000
3225+++ po/lv.po 2014-06-19 01:49:30 +0000
3226@@ -6,11 +6,12 @@
3227 msgid ""
3228 msgstr ""
3229 "Project-Id-Version: music-app\n"
3230-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
3231+"Report-Msgid-Bugs-To: \n"
3232 "POT-Creation-Date: 2014-06-06 12:31+0100\n"
3233 "PO-Revision-Date: 2014-06-06 17:55+0000\n"
3234 "Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
3235 "Language-Team: Latvian <lv@li.org>\n"
3236+"Language: lv\n"
3237 "MIME-Version: 1.0\n"
3238 "Content-Type: text/plain; charset=UTF-8\n"
3239 "Content-Transfer-Encoding: 8bit\n"
3240
3241=== modified file 'tests/autopilot/music_app/__init__.py'
3242--- tests/autopilot/music_app/__init__.py 2013-06-21 23:06:10 +0000
3243+++ tests/autopilot/music_app/__init__.py 2014-06-19 01:49:30 +0000
3244@@ -1,5 +1,5 @@
3245 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3246-# Copyright 2013 Canonical
3247+# Copyright 2013, 2014 Canonical
3248 #
3249 # This program is free software: you can redistribute it and/or modify it
3250 # under the terms of the GNU General Public License version 3, as published
3251
3252=== modified file 'tests/autopilot/music_app/content/1.ogg'
3253Binary files tests/autopilot/music_app/content/1.ogg 2013-09-27 15:14:10 +0000 and tests/autopilot/music_app/content/1.ogg 2014-06-19 01:49:30 +0000 differ
3254=== removed directory 'tests/autopilot/music_app/content/mediascanner'
3255=== added directory 'tests/autopilot/music_app/content/mediascanner-2.0'
3256=== added file 'tests/autopilot/music_app/content/mediascanner-2.0/mediastore.db'
3257Binary files tests/autopilot/music_app/content/mediascanner-2.0/mediastore.db 1970-01-01 00:00:00 +0000 and tests/autopilot/music_app/content/mediascanner-2.0/mediastore.db 2014-06-19 01:49:30 +0000 differ
3258=== added file 'tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql'
3259--- tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql 1970-01-01 00:00:00 +0000
3260+++ tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql 2014-06-19 01:49:30 +0000
3261@@ -0,0 +1,39 @@
3262+BEGIN TRANSACTION;
3263+DROP TABLE media;
3264+CREATE TABLE media (
3265+ filename TEXT PRIMARY KEY NOT NULL,
3266+ content_type TEXT,
3267+ etag TEXT,
3268+ title TEXT,
3269+ date TEXT,
3270+ artist TEXT, -- Only relevant to audio
3271+ album TEXT, -- Only relevant to audio
3272+ album_artist TEXT, -- Only relevant to audio
3273+ genre TEXT, -- Only relevant to audio
3274+ disc_number INTEGER, -- Only relevant to audio
3275+ track_number INTEGER, -- Only relevant to audio
3276+ duration INTEGER,
3277+ width INTEGER, -- Only relevant to video/images
3278+ height INTEGER, -- Only relevant to video/images
3279+ latitude DOUBLE,
3280+ longitude DOUBLE,
3281+ type INTEGER -- 0=Audio, 1=Video
3282+);
3283+INSERT INTO "media" VALUES('/home/phablet/Music/1.ogg','audio/ogg','1401368666:257952','Gran Vals','1902','Francisco Tárrega','','Francisco Tárrega','',0,0,202,0,0,0.0,0.0,1);
3284+INSERT INTO "media" VALUES('/home/phablet/Music/2.ogg','audio/ogg','1401457265:78191','Swansong','','Josh Woodward','','Josh Woodward','',0,0,62,0,0,0.0,0.0,1);
3285+INSERT INTO "media" VALUES('/home/phablet/Music/3.mp3','audio/mpeg','1401457265:78191','TestMP3Title','','TestMP3Artist','TestMP3Album','TestMP3Artist','',0,0,6,0,0,0.0,0.0,1);
3286+
3287+CREATE INDEX media_album_album_artist_idx ON media(album, album_artist);
3288+CREATE TRIGGER media_ai AFTER INSERT ON media BEGIN
3289+ INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album);
3290+END;
3291+CREATE TRIGGER media_au AFTER UPDATE ON media BEGIN
3292+ INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album);
3293+END;
3294+CREATE TRIGGER media_bd BEFORE DELETE ON media BEGIN
3295+ DELETE FROM media_fts WHERE docid=old.rowid;
3296+END;
3297+CREATE TRIGGER media_bu BEFORE UPDATE ON media BEGIN
3298+ DELETE FROM media_fts WHERE docid=old.rowid;
3299+END;
3300+COMMIT;
3301
3302=== removed directory 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab'
3303=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments.gen'
3304Binary files tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments.gen 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments.gen 1970-01-01 00:00:00 +0000 differ
3305=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments_1'
3306Binary files tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments_1 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments_1 1970-01-01 00:00:00 +0000 differ
3307=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/write.lock'
3308=== removed directory 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7'
3309=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_0.cfs'
3310Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_0.cfs 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_0.cfs 1970-01-01 00:00:00 +0000 differ
3311=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1.cfs'
3312Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1.cfs 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1.cfs 1970-01-01 00:00:00 +0000 differ
3313=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1_1.del'
3314Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1_1.del 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1_1.del 1970-01-01 00:00:00 +0000 differ
3315=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_2.cfs'
3316Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_2.cfs 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_2.cfs 1970-01-01 00:00:00 +0000 differ
3317=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3.cfs'
3318Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3.cfs 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3.cfs 1970-01-01 00:00:00 +0000 differ
3319=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3_1.del'
3320Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3_1.del 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3_1.del 1970-01-01 00:00:00 +0000 differ
3321=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_4.cfs'
3322Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_4.cfs 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_4.cfs 1970-01-01 00:00:00 +0000 differ
3323=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments.gen'
3324Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments.gen 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments.gen 1970-01-01 00:00:00 +0000 differ
3325=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments_6'
3326Binary files tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments_6 2013-10-15 18:19:36 +0000 and tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments_6 1970-01-01 00:00:00 +0000 differ
3327=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/write.lock'
3328=== removed file 'tests/autopilot/music_app/content/mediascanner/mediaindex'
3329--- tests/autopilot/music_app/content/mediascanner/mediaindex 2013-10-15 18:19:36 +0000
3330+++ tests/autopilot/music_app/content/mediascanner/mediaindex 1970-01-01 00:00:00 +0000
3331@@ -1,10 +0,0 @@
3332-[global]
3333-format=Ubuntu Media Scanner Meta Index 1.0
3334-
3335-[media:home/autopilot-music-app/Music]
3336-segments=d15682c3-89f1-4e41-abfc-531e4740e5a7
3337-relative-path=home/autopilot-music-app/Music
3338-
3339-[media:]
3340-segments=7d246d8c-5e6d-4fe8-8271-1472eb02c3ab
3341-relative-path=
3342
3343=== modified file 'tests/autopilot/music_app/emulators.py'
3344--- tests/autopilot/music_app/emulators.py 2014-05-03 15:54:38 +0000
3345+++ tests/autopilot/music_app/emulators.py 2014-06-19 01:49:30 +0000
3346@@ -1,5 +1,5 @@
3347 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3348-# Copyright 2013 Canonical
3349+# Copyright 2013, 2014 Canonical
3350 #
3351 # This program is free software: you can redistribute it and/or modify it
3352 # under the terms of the GNU General Public License version 3, as published
3353@@ -51,6 +51,23 @@
3354
3355 self.pointing_device.drag(x1, y1, x1, y1 - toolbar.fullHeight)
3356
3357+ def add_to_queue_from_albums_tab_album_sheet(self, artistName, trackTitle):
3358+ # switch to albums tab
3359+ self.switch_to_tab("albumstab")
3360+
3361+ #select album
3362+ albumartist = self.get_albums_albumartist(artistName)
3363+ self.pointing_device.click_object(albumartist)
3364+
3365+ #get track item to add to queue
3366+ trackicon = self.get_album_sheet_listview_trackicon(
3367+ trackTitle)
3368+ self.pointing_device.click_object(trackicon)
3369+
3370+ #click on Add to queue
3371+ queueTrackLabel = self.get_album_sheet_queuetrack_label()
3372+ self.pointing_device.click_object(queueTrackLabel)
3373+
3374 def get_player(self):
3375 return self.select_single("*", objectName="player")
3376
3377@@ -83,7 +100,9 @@
3378 return self.wait_select_single("*", objectName="genreItemObject")
3379
3380 def get_back_button(self):
3381- return self.select_single("*", objectName="nowPlayingBackButtonObject")
3382+ backButton = self.select_single("AbstractButton",
3383+ objectName="backButton")
3384+ return backButton
3385
3386 def get_albumstab(self):
3387 return self.select_single("Tab", objectName="albumstab")
3388
3389=== modified file 'tests/autopilot/music_app/tests/__init__.py'
3390--- tests/autopilot/music_app/tests/__init__.py 2014-04-30 15:24:44 +0000
3391+++ tests/autopilot/music_app/tests/__init__.py 2014-06-19 01:49:30 +0000
3392@@ -1,5 +1,5 @@
3393 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3394-# Copyright 2013 Canonical
3395+# Copyright 2013, 2014 Canonical
3396 #
3397 # This program is free software: you can redistribute it and/or modify it
3398 # under the terms of the GNU General Public License version 3, as published
3399@@ -7,28 +7,26 @@
3400
3401 """Music app autopilot tests."""
3402
3403-import tempfile
3404-try:
3405- from unittest import mock
3406-except ImportError:
3407- import mock
3408 import os
3409+import subprocess
3410 import os.path
3411 import shutil
3412-#import subprocess
3413+import sqlite3
3414 import logging
3415 import music_app
3416
3417+import fixtures
3418+from music_app import emulators
3419+
3420+from autopilot import logging as autopilot_logging
3421 from autopilot.input import Mouse, Touch, Pointer
3422 from autopilot.platform import model
3423 from autopilot.testcase import AutopilotTestCase
3424
3425-from music_app import emulators
3426-
3427 from ubuntuuitoolkit import (
3428 base,
3429 emulators as toolkit_emulators,
3430- environment
3431+ fixture_setup as toolkit_fixtures
3432 )
3433
3434
3435@@ -50,6 +48,8 @@
3436 local_location_dir = os.path.dirname(os.path.dirname(working_dir))
3437 local_location = local_location_dir + "/music-app.qml"
3438 installed_location = "/usr/share/music-app/music-app.qml"
3439+ backup_root = os.path.join(
3440+ os.path.expanduser('~'), '.local/share/com.ubuntu.music/backups')
3441
3442 def setup_environment(self):
3443 if os.path.exists(self.local_location):
3444@@ -64,67 +64,199 @@
3445 return launch, test_type
3446
3447 def setUp(self):
3448+ subprocess.call(["stop", "mediascanner-2.0"])
3449+
3450+ try:
3451+ pid = subprocess.check_output(["pidof", "mediascanner-dbus-2.0"])
3452+ except subprocess.CalledProcessError:
3453+ logger.debug("mediascanner-dbus-2.0 not running")
3454+ else:
3455+ pid = pid.decode("utf-8")
3456+ pid = pid.split(None, 1)[0]
3457+ subprocess.call(["kill", "-9", pid])
3458+
3459+ # Stop any mediascanner-dbus and restart mediascanner on exit
3460+ self.addCleanup(subprocess.call,
3461+ 'kill -9 \
3462+ `pidof \
3463+ /usr/lib/*/mediascanner-2.0/mediascanner-dbus-2.0`',
3464+ shell=True)
3465+ self.addCleanup(subprocess.call, ["start", "mediascanner-2.0"])
3466+
3467 launch, self.test_type = self.setup_environment()
3468- self.home_dir = self._patch_home()
3469+
3470+ #Use backup and restore to setup test environment
3471+ #################################################
3472+ #for now, we will use real /home
3473+ logger.debug("Backup root folder %s" % self.backup_root)
3474+
3475+ #backup and wipe before testing
3476+ sqlite_dir = os.path.join(
3477+ os.environ.get('HOME'), '.local/share/com.ubuntu.music/Databases')
3478+ self.backup_folder(sqlite_dir)
3479+ self.addCleanup(lambda: self.restore_folder(sqlite_dir))
3480+
3481+ #backup Music folder and restore it after testing
3482+ self.backup_folder(os.path.join(os.environ.get('HOME'), 'Music'))
3483+ self.addCleanup(lambda: self.restore_folder(
3484+ os.path.join(os.environ.get('HOME'), 'Music')))
3485+
3486+ #backup mediascanner folder and restore it after testing
3487+ self.backup_folder(os.path.join(os.environ.get('HOME'),
3488+ '.cache/mediascanner-2.0'))
3489+ self.addCleanup(lambda: self.restore_folder(os.path.join(
3490+ os.environ.get('HOME'),
3491+ '.cache/mediascanner-2.0')))
3492+
3493+ self.home_dir = os.environ['HOME']
3494 self._create_music_library()
3495+ #################################################
3496+ #Use backup and restore to setup test environment
3497+
3498+ #Use mocking fakehome
3499+ #####################
3500+ #self.home_dir = self._patch_home()
3501+
3502+ #self._create_music_library()
3503+
3504+ ##we need to also tell upstart about our fake home
3505+ ##and we need to do this all in one shell,
3506+ ##also passing along our fake env (env=env)
3507+ #logger.debug("Launching mediascanner")
3508+ #env = os.environ.copy()
3509+ #sethome = "initctl set-env HOME=" + self.home_dir
3510+ #retcode = subprocess.check_output(sethome + "; \
3511+ #start mediascanner-2.0",
3512+ #env=env,
3513+ #stderr=subprocess.STDOUT,
3514+ #shell=True)
3515+ #logger.debug("mediascanner launched %s" % retcode)
3516+ #time.sleep(10)
3517+
3518+ #logger.debug("Launching mediascanner-dbus")
3519+ #retcode = subprocess.call(
3520+ #"/usr/lib/*/mediascanner-2.0/mediascanner-dbus-2.0 &",
3521+ #env=env, stderr=subprocess.STDOUT, shell=True)
3522+ #logger.debug("mediascanner-dbus launched %s" % retcode)
3523+
3524+ ##we attempt to reset home for future upstart jobs
3525+ #retcode = subprocess.check_output("initctl reset-env",
3526+ #env=env, shell=True)
3527+ #retcode = subprocess.check_output("initctl get-env HOME",
3528+ #env=env, shell=True)
3529+ #logger.debug("reset initctl home %s" % retcode)
3530+ #####################
3531+ #Use mocking fakehome
3532+
3533 self.pointing_device = Pointer(self.input_device_class.create())
3534 super(MusicTestCase, self).setUp()
3535 launch()
3536
3537+ @autopilot_logging.log_action(logger.info)
3538 def launch_test_local(self):
3539- logger.debug("Running via local installation")
3540 self.app = self.launch_test_application(
3541 base.get_qmlscene_launch_command(),
3542 self.local_location,
3543+ "debug",
3544 app_type='qt',
3545 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
3546
3547+ @autopilot_logging.log_action(logger.info)
3548 def launch_test_installed(self):
3549- logger.debug("Running via installed debian package")
3550 self.app = self.launch_test_application(
3551 base.get_qmlscene_launch_command(),
3552 self.installed_location,
3553+ "debug",
3554 app_type='qt',
3555 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
3556
3557+ @autopilot_logging.log_action(logger.info)
3558 def launch_test_click(self):
3559- logger.debug("Running via click package")
3560 self.app = self.launch_click_package(
3561 "com.ubuntu.music",
3562 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
3563
3564- def _patch_home(self):
3565- #make a temp dir
3566- temp_dir = tempfile.mkdtemp()
3567-
3568- #delete it, and recreate it to the length
3569- #required so our patching the db works
3570- #require a length of 25
3571- shutil.rmtree(temp_dir)
3572- temp_dir = temp_dir.ljust(25, 'X')
3573- os.mkdir(temp_dir)
3574- logger.debug("Created fake home directory " + temp_dir)
3575- self.addCleanup(shutil.rmtree, temp_dir)
3576-
3577- #if the Xauthority file is in home directory
3578- #make sure we copy it to temp home, otherwise do nothing
3579- xauth = os.path.expanduser(os.path.join('~', '.Xauthority'))
3580+ def _copy_xauthority_file(self, directory):
3581+ """ Copy .Xauthority file to directory, if it exists in /home
3582+ """
3583+ #If running under xvfb, as jenkins does,
3584+ #xsession will fail to start without xauthority file
3585+ #Thus if the Xauthority file is in the home directory
3586+ #make sure we copy it to our temp home directory
3587+
3588+ xauth = os.path.expanduser(os.path.join(os.environ.get('HOME'),
3589+ '.Xauthority'))
3590 if os.path.isfile(xauth):
3591- logger.debug("Copying .Xauthority to fake home " + temp_dir)
3592+ logger.debug("Copying .Xauthority to %s" % directory)
3593 shutil.copyfile(
3594- os.path.expanduser(os.path.join('~', '.Xauthority')),
3595- os.path.join(temp_dir, '.Xauthority'))
3596+ os.path.expanduser(os.path.join(os.environ.get('HOME'),
3597+ '.Xauthority')),
3598+ os.path.join(directory, '.Xauthority'))
3599
3600- #click can use initctl env (upstart), but desktop still requires mock
3601+ def _patch_home(self):
3602+ """ mock /home for testing purposes to preserve user data
3603+ """
3604+ #click requires apparmor profile, and writing to special dir
3605+ #but the desktop can write to a traditional /tmp directory
3606 if self.test_type == 'click':
3607- environment.set_initctl_env_var('HOME', temp_dir)
3608- self.addCleanup(environment.unset_initctl_env_var, 'HOME')
3609+ env_dir = os.path.join(os.environ.get('HOME'), 'autopilot',
3610+ 'fakeenv')
3611+
3612+ if not os.path.exists(env_dir):
3613+ os.makedirs(env_dir)
3614+
3615+ temp_dir_fixture = fixtures.TempDir(env_dir)
3616+ self.useFixture(temp_dir_fixture)
3617+
3618+ #apparmor doesn't allow the app to create needed directories,
3619+ #so we create them now
3620+ temp_dir = temp_dir_fixture.path
3621+ temp_dir_cache = os.path.join(temp_dir, '.cache')
3622+ temp_dir_cache_font = os.path.join(temp_dir_cache, 'fontconfig')
3623+ temp_dir_cache_media = os.path.join(temp_dir_cache, 'media-art')
3624+ temp_dir_cache_write = os.path.join(temp_dir_cache,
3625+ 'tncache-write-text.null')
3626+ temp_dir_config = os.path.join(temp_dir, '.config')
3627+ temp_dir_toolkit = os.path.join(temp_dir_config,
3628+ 'ubuntu-ui-toolkit')
3629+ temp_dir_font = os.path.join(temp_dir_cache, '.fontconfig')
3630+ temp_dir_local = os.path.join(temp_dir, '.local', 'share')
3631+ temp_dir_confined = os.path.join(temp_dir, 'confined')
3632+
3633+ if not os.path.exists(temp_dir_cache):
3634+ os.makedirs(temp_dir_cache)
3635+ if not os.path.exists(temp_dir_cache_font):
3636+ os.makedirs(temp_dir_cache_font)
3637+ if not os.path.exists(temp_dir_cache_media):
3638+ os.makedirs(temp_dir_cache_media)
3639+ if not os.path.exists(temp_dir_cache_write):
3640+ os.makedirs(temp_dir_cache_write)
3641+ if not os.path.exists(temp_dir_config):
3642+ os.makedirs(temp_dir_config)
3643+ if not os.path.exists(temp_dir_toolkit):
3644+ os.makedirs(temp_dir_toolkit)
3645+ if not os.path.exists(temp_dir_font):
3646+ os.makedirs(temp_dir_font)
3647+ if not os.path.exists(temp_dir_local):
3648+ os.makedirs(temp_dir_local)
3649+ if not os.path.exists(temp_dir_confined):
3650+ os.makedirs(temp_dir_confined)
3651+
3652+ #before we set fixture, copy xauthority if needed
3653+ self._copy_xauthority_file(temp_dir)
3654+ self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
3655+ HOME=temp_dir))
3656 else:
3657- patcher = mock.patch.dict('os.environ', {'HOME': temp_dir})
3658- patcher.start()
3659- self.addCleanup(patcher.stop)
3660-
3661- logger.debug("Patched home to fake home directory " + temp_dir)
3662+ temp_dir_fixture = fixtures.TempDir()
3663+ self.useFixture(temp_dir_fixture)
3664+ temp_dir = temp_dir_fixture.path
3665+
3666+ #before we set fixture, copy xauthority if needed
3667+ self._copy_xauthority_file(temp_dir)
3668+ self.useFixture(fixtures.EnvironmentVariable('HOME',
3669+ newvalue=temp_dir))
3670+
3671+ logger.debug("Patched home to fake home directory %s" % temp_dir)
3672 return temp_dir
3673
3674 def _create_music_library(self):
3675@@ -132,8 +264,10 @@
3676 logger.debug("Home set to %s" % self.home_dir)
3677 musicpath = os.path.join(self.home_dir, 'Music')
3678 logger.debug("Music path set to %s" % musicpath)
3679- mediascannerpath = os.path.join(self.home_dir, '.cache/mediascanner')
3680- os.mkdir(musicpath)
3681+ mediascannerpath = os.path.join(self.home_dir,
3682+ '.cache/mediascanner-2.0')
3683+ if not os.path.exists(musicpath):
3684+ os.makedirs(musicpath)
3685 logger.debug("Mediascanner path set to %s" % mediascannerpath)
3686
3687 #set content path
3688@@ -147,11 +281,12 @@
3689 shutil.copy(os.path.join(content_dir, '2.ogg'), musicpath)
3690 shutil.copy(os.path.join(content_dir, '3.mp3'), musicpath)
3691 shutil.copytree(
3692- os.path.join(content_dir, 'mediascanner'), mediascannerpath)
3693+ os.path.join(content_dir, 'mediascanner-2.0'), mediascannerpath)
3694
3695 logger.debug("Music copied, files " + str(os.listdir(musicpath)))
3696
3697 self._patch_mediascanner_home(mediascannerpath)
3698+
3699 logger.debug(
3700 "Mediascanner database copied, files " +
3701 str(os.listdir(mediascannerpath)))
3702@@ -160,18 +295,24 @@
3703 #do some inline db patching
3704 #patch mediaindex to proper home
3705 #these values are dependent upon our sampled db
3706- logger.debug("Patching fake mediascanner database")
3707+ logger.debug("Patching fake mediascanner database in %s" %
3708+ mediascannerpath)
3709+ logger.debug(
3710+ "Mediascanner database files " +
3711+ str(os.listdir(mediascannerpath)))
3712+
3713 relhome = self.home_dir[1:]
3714- dblocation = "home/autopilot-music-app"
3715- dbfoldername = "d15682c3-89f1-4e41-abfc-531e4740e5a7"
3716+ dblocation = "home/phablet"
3717 #patch mediaindex
3718 self._file_find_replace(mediascannerpath +
3719- "/mediaindex", dblocation, relhome)
3720+ "/mediastore.sql", dblocation, relhome)
3721
3722- #patch file indexes
3723- index_template = '%s/%s/_%%s.cfs' % (mediascannerpath, dbfoldername)
3724- for i in range(5):
3725- self._file_find_replace(index_template % i, dblocation, relhome)
3726+ con = sqlite3.connect(mediascannerpath + "/mediastore.db")
3727+ f = open(mediascannerpath + "/mediastore.sql", 'r')
3728+ sql = f.read()
3729+ cur = con.cursor()
3730+ cur.executescript(sql)
3731+ con.close()
3732
3733 def _file_find_replace(self, in_filename, find, replace):
3734 #replace all occurences of string find with string replace
3735@@ -188,6 +329,55 @@
3736 os.remove(in_filename)
3737 os.rename(out_filename, in_filename)
3738
3739+ def backup_folder(self, folder):
3740+ backup_dir = os.path.join(self.backup_root, os.path.basename(folder))
3741+ logger.debug('Backup dir set to %s' % backup_dir)
3742+ try:
3743+ shutil.rmtree(backup_dir)
3744+ except:
3745+ pass
3746+ else:
3747+ logger.warning("Prexisting backup found and removed")
3748+
3749+ try:
3750+ shutil.move(folder, backup_dir)
3751+ except shutil.Error as e:
3752+ logger.error('Backup error for %s: %s' % (folder, e))
3753+ except IOError as e:
3754+ logger.error('Backup error for %s: %s' % (folder, e.strerror))
3755+ except:
3756+ logger.error("Unknown error backing up %s" % folder)
3757+ else:
3758+ logger.debug('Backed up %s to %s' % (folder, backup_dir))
3759+
3760+ def restore_folder(self, folder):
3761+ backup_dir = os.path.join(self.backup_root, os.path.basename(folder))
3762+ logger.debug('Backup dir set to %s' % backup_dir)
3763+ if os.path.exists(backup_dir):
3764+ if os.path.exists(folder):
3765+ try:
3766+ shutil.rmtree(folder)
3767+ except shutil.Error as e:
3768+ logger.error('Restore error for %s: %s' % (folder, e))
3769+ except IOError as e:
3770+ logger.error('Restore error for %s: %s' %
3771+ (folder, e.strerror))
3772+ except:
3773+ logger.error("Failed to remove test data for %s" % folder)
3774+ return
3775+ try:
3776+ shutil.move(backup_dir, folder)
3777+ except shutil.Error as e:
3778+ logger.error('Restore error for %s: %s' % (folder, e))
3779+ except IOError as e:
3780+ logger.error('Restore error for %s: %s' % (folder, e.strerror))
3781+ except:
3782+ logger.error('Unknown error restoring %s' % folder)
3783+ else:
3784+ logger.debug('Restored %s from %s' % (folder, backup_dir))
3785+ else:
3786+ logger.warn('No backup found to restore for %s' % folder)
3787+
3788 @property
3789 def player(self):
3790 return self.main_view.get_player()
3791
3792=== modified file 'tests/autopilot/music_app/tests/test_music.py'
3793--- tests/autopilot/music_app/tests/test_music.py 2014-05-21 07:23:35 +0000
3794+++ tests/autopilot/music_app/tests/test_music.py 2014-06-19 01:49:30 +0000
3795@@ -1,5 +1,5 @@
3796 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3797-# Copyright 2013 Canonical
3798+# Copyright 2013, 2014 Canonical
3799 #
3800 # This program is free software: you can redistribute it and/or modify it
3801 # under the terms of the GNU General Public License version 3, as published
3802@@ -21,25 +21,35 @@
3803
3804
3805 class TestMainWindow(MusicTestCase):
3806- FIRST_TITLE = "Foss Yeaaaah! (Radio Edit)"
3807- LAST_TITLE = "TestMP3Title"
3808
3809 def setUp(self):
3810 super(TestMainWindow, self).setUp()
3811 self.assertThat(
3812 self.main_view.visible, Eventually(Equals(True)))
3813+
3814 #wait for activity indicator to stop spinning
3815 spinner = lambda: self.main_view.get_spinner().running
3816 self.assertThat(spinner, Eventually(Equals(False)))
3817+ self.trackTitle = u"Gran Vals"
3818+ self.artistName = u"Francisco Tárrega"
3819+ self.lastTrackTitle = u"TestMP3Title"
3820
3821 def populate_and_play_queue(self):
3822 first_genre_item = self.main_view.get_first_genre_item()
3823 self.pointing_device.click_object(first_genre_item)
3824
3825- title = self.FIRST_TITLE
3826- song = self.main_view.get_album_sheet_listview_tracktitle(title)
3827+ song = self.main_view.get_album_sheet_listview_tracktitle(
3828+ self.trackTitle)
3829 self.pointing_device.click_object(song)
3830
3831+ def populate_and_play_queue_from_songs_tab(self):
3832+ # switch to songs tab
3833+ self.main_view.switch_to_tab("trackstab")
3834+
3835+ # get track item to add to queue
3836+ trackitem = self.main_view.get_songs_tab_tracktitle(self.trackTitle)
3837+ self.pointing_device.click_object(trackitem)
3838+
3839 def turn_shuffle_off(self):
3840 if self.player.shuffle:
3841 shufflebutton = self.main_view.get_shuffle_button()
3842@@ -81,55 +91,63 @@
3843 fake mediascanner database"""
3844
3845 # populate queue
3846- first_genre_item = self.main_view.get_first_genre_item()
3847- self.pointing_device.click_object(first_genre_item)
3848- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3849- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
3850- self.pointing_device.click_object(song)
3851+ self.populate_and_play_queue_from_songs_tab()
3852
3853 title = lambda: self.player.currentMetaTitle
3854 artist = lambda: self.player.currentMetaArtist
3855- self.assertThat(title,
3856- Eventually(Equals("Foss Yeaaaah! (Radio Edit)")))
3857- self.assertThat(artist, Eventually(Equals("Benjamin Kerensa")))
3858+ self.assertThat(title, Eventually(Equals(self.trackTitle)))
3859+ self.assertThat(artist, Eventually(Equals(self.artistName)))
3860
3861 def test_play_pause_library(self):
3862 """ Test playing and pausing a track (Music Library must exist) """
3863
3864- # populate queue
3865- first_genre_item = self.main_view.get_first_genre_item()
3866- self.pointing_device.click_object(first_genre_item)
3867- button = self.main_view.get_add_to_queue_button()
3868- self.pointing_device.click_object(button)
3869-
3870- # click on close button to close genre sheet
3871+ # get number of tracks in queue before queuing a track
3872+ initialtracksCount = self.main_view.get_queue_track_count()
3873+
3874+ self.main_view.add_to_queue_from_albums_tab_album_sheet(
3875+ self.artistName, self.trackTitle)
3876+
3877+ # verify track queue has added one to initial value
3878+ endtracksCount = self.main_view.get_queue_track_count()
3879+ self.assertThat(endtracksCount, Equals(initialtracksCount + 1))
3880+
3881+ #Assert that the song added to the list is not playing
3882+ self.assertThat(self.player.currentIndex,
3883+ Eventually(NotEquals(endtracksCount)))
3884+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
3885+
3886+ #verify song's metadata matches the item added to the Now Playing view
3887+ queueArtistName = self.main_view.get_queue_now_playing_artist(
3888+ self.artistName)
3889+ self.assertThat(queueArtistName.text, Equals(self.artistName))
3890+ queueTrackTitle = self.main_view.get_queue_now_playing_title(
3891+ self.trackTitle)
3892+ self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
3893+
3894+ # click on close button to close album sheet
3895 closebutton = self.main_view.get_album_sheet_close_button()
3896 self.pointing_device.click_object(closebutton)
3897+ self.assertThat(self.main_view.get_albumstab(), Not(Is(None)))
3898
3899 if self.main_view.wideAspect:
3900- playbutton = self.main_view.get_now_playing_play_button()
3901+ play_button = self.main_view.get_now_playing_play_button()
3902 else:
3903- playbutton = self.main_view.get_play_button()
3904-
3905- self.main_view.show_toolbar()
3906+ play_button = self.main_view.get_play_button()
3907+ self.main_view.show_toolbar()
3908
3909 """ Track is playing"""
3910- self.pointing_device.click_object(playbutton)
3911+ self.pointing_device.click_object(play_button)
3912 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
3913
3914 """ Track is not playing"""
3915- self.pointing_device.click_object(playbutton)
3916+ self.pointing_device.click_object(play_button)
3917 self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
3918
3919 def test_play_pause_now_playing(self):
3920 """ Test playing and pausing a track (Music Library must exist) """
3921
3922 # populate queue
3923- first_genre_item = self.main_view.get_first_genre_item()
3924- self.pointing_device.click_object(first_genre_item)
3925- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3926- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
3927- self.pointing_device.click_object(song)
3928+ self.populate_and_play_queue_from_songs_tab()
3929
3930 playbutton = self.main_view.get_now_playing_play_button()
3931
3932@@ -148,11 +166,7 @@
3933 """ Test going to next track (Music Library must exist) """
3934
3935 # populate queue
3936- first_genre_item = self.main_view.get_first_genre_item()
3937- self.pointing_device.click_object(first_genre_item)
3938- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3939- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
3940- self.pointing_device.click_object(song)
3941+ self.populate_and_play_queue_from_songs_tab()
3942
3943 playbutton = self.main_view.get_now_playing_play_button()
3944
3945@@ -206,11 +220,7 @@
3946 """ Test that mp3 "plays" or at least doesn't crash on load """
3947
3948 # populate queue
3949- first_genre_item = self.main_view.get_first_genre_item()
3950- self.pointing_device.click_object(first_genre_item)
3951- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3952- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
3953- self.pointing_device.click_object(song)
3954+ self.populate_and_play_queue_from_songs_tab()
3955
3956 playbutton = self.main_view.get_now_playing_play_button()
3957
3958@@ -253,11 +263,7 @@
3959 """ Test shuffle (Music Library must exist) """
3960
3961 # populate queue
3962- first_genre_item = self.main_view.get_first_genre_item()
3963- self.pointing_device.click_object(first_genre_item)
3964- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3965- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
3966- self.pointing_device.click_object(song)
3967+ self.populate_and_play_queue_from_songs_tab()
3968
3969 """ Track is playing, shuffle is turned on"""
3970 forwardbutton = self.main_view.get_forward_button()
3971@@ -326,18 +332,16 @@
3972 def test_show_albums_sheet(self):
3973 """tests navigating to the Albums tab and displaying the album sheet"""
3974
3975- artistName = "Benjamin Kerensa"
3976-
3977 # switch to albums tab
3978 self.main_view.switch_to_tab("albumstab")
3979
3980 #select album
3981- albumartist = self.main_view.get_albums_albumartist(artistName)
3982+ albumartist = self.main_view.get_albums_albumartist(self.artistName)
3983 self.pointing_device.click_object(albumartist)
3984
3985 #get album sheet album artist
3986 sheet_albumartist = self.main_view.get_album_sheet_artist()
3987- self.assertThat(sheet_albumartist.text, Equals(artistName))
3988+ self.assertThat(sheet_albumartist.text, Equals(self.artistName))
3989
3990 # click on close button to close album sheet
3991 closebutton = self.main_view.get_album_sheet_close_button()
3992@@ -347,31 +351,11 @@
3993 def test_add_song_to_queue_from_albums_sheet(self):
3994 """tests navigating to the Albums tab and adding a song to queue"""
3995
3996- trackTitle = "Foss Yeaaaah! (Radio Edit)"
3997- artistName = "Benjamin Kerensa"
3998-
3999 # get number of tracks in queue before queuing a track
4000 initialtracksCount = self.main_view.get_queue_track_count()
4001
4002- # switch to albums tab
4003- self.main_view.switch_to_tab("albumstab")
4004-
4005- #select album
4006- albumartist = self.main_view.get_albums_albumartist(artistName)
4007- self.pointing_device.click_object(albumartist)
4008-
4009- #get album sheet album artist
4010- sheet_albumartist = self.main_view.get_album_sheet_artist()
4011- self.assertThat(sheet_albumartist.text, Equals(artistName))
4012-
4013- #get track item to add to queue
4014- trackicon = self.main_view.get_album_sheet_listview_trackicon(
4015- trackTitle)
4016- self.pointing_device.click_object(trackicon)
4017-
4018- #click on Add to queue
4019- queueTrackLabel = self.main_view.get_album_sheet_queuetrack_label()
4020- self.pointing_device.click_object(queueTrackLabel)
4021+ self.main_view.add_to_queue_from_albums_tab_album_sheet(
4022+ self.artistName, self.trackTitle)
4023
4024 # verify track queue has added one to initial value
4025 endtracksCount = self.main_view.get_queue_track_count()
4026@@ -384,11 +368,11 @@
4027
4028 #verify song's metadata matches the item added to the Now Playing view
4029 queueArtistName = self.main_view.get_queue_now_playing_artist(
4030- artistName)
4031- self.assertThat(str(queueArtistName.text), Equals(artistName))
4032+ self.artistName)
4033+ self.assertThat(queueArtistName.text, Equals(self.artistName))
4034 queueTrackTitle = self.main_view.get_queue_now_playing_title(
4035- trackTitle)
4036- self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))
4037+ self.trackTitle)
4038+ self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
4039
4040 # click on close button to close album sheet
4041 closebutton = self.main_view.get_album_sheet_close_button()
4042@@ -399,18 +383,11 @@
4043 """tests navigating to the Songs tab and adding the library to the
4044 queue with the selected item being played. """
4045
4046- trackTitle = "Foss Yeaaaah! (Radio Edit)"
4047- artistName = "Benjamin Kerensa"
4048-
4049 # get number of tracks in queue before queuing a track
4050 initialtracksCount = self.main_view.get_queue_track_count()
4051
4052- # switch to songs tab
4053- self.main_view.switch_to_tab("trackstab")
4054-
4055- # get track item to add to queue
4056- trackitem = self.main_view.get_songs_tab_tracktitle(trackTitle)
4057- self.pointing_device.click_object(trackitem)
4058+ # populate queue
4059+ self.populate_and_play_queue_from_songs_tab()
4060
4061 # verify track queue has added all songs to initial value
4062 endtracksCount = self.main_view.get_queue_track_count()
4063@@ -423,19 +400,16 @@
4064
4065 # verify song's metadata matches the item added to the Now Playing view
4066 queueArtistName = self.main_view.get_queue_now_playing_artist(
4067- artistName)
4068- self.assertThat(str(queueArtistName.text), Equals(artistName))
4069+ self.artistName)
4070+ self.assertThat(queueArtistName.text, Equals(self.artistName))
4071 queueTrackTitle = self.main_view.get_queue_now_playing_title(
4072- trackTitle)
4073- self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))
4074+ self.trackTitle)
4075+ self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
4076
4077 def test_add_song_to_queue_from_songs_tab(self):
4078 """tests navigating to the Songs tab and adding a song from the library
4079 to the queue via the expandable list view item. """
4080
4081- trackTitle = "Foss Yeaaaah! (Radio Edit)"
4082- artistName = "Benjamin Kerensa"
4083-
4084 # get number of tracks in queue before queuing a track
4085 initialtracksCount = self.main_view.get_queue_track_count()
4086
4087@@ -443,7 +417,7 @@
4088 self.main_view.switch_to_tab("trackstab")
4089
4090 # get track item to add to queue
4091- trackitem = self.main_view.get_songs_tab_trackimage(trackTitle)
4092+ trackitem = self.main_view.get_songs_tab_trackimage(self.trackTitle)
4093 self.pointing_device.click_object(trackitem)
4094 addtoqueueLabel = self.main_view.get_songs_tab_add_to_queue_label()
4095 self.pointing_device.click_object(addtoqueueLabel)
4096@@ -459,23 +433,21 @@
4097
4098 # verify song's metadata matches the item added to the Now Playing view
4099 queueArtistName = self.main_view.get_queue_now_playing_artist(
4100- artistName)
4101- self.assertThat(str(queueArtistName.text), Equals(artistName))
4102+ self.artistName)
4103+ self.assertThat(queueArtistName.text, Equals(self.artistName))
4104 queueTrackTitle = self.main_view.get_queue_now_playing_title(
4105- trackTitle)
4106- self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))
4107+ self.trackTitle)
4108+ self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
4109
4110 def test_create_playlist_from_songs_tab(self):
4111 """tests navigating to the Songs tab and creating a playlist by
4112 selecting a song to add it to a new playlist. """
4113
4114- trackTitle = "Foss Yeaaaah! (Radio Edit)"
4115-
4116 # switch to songs tab
4117 self.main_view.switch_to_tab("trackstab")
4118
4119 # get track item to add to queue
4120- trackitem = self.main_view.get_songs_tab_trackimage(trackTitle)
4121+ trackitem = self.main_view.get_songs_tab_trackimage(self.trackTitle)
4122 self.pointing_device.click_object(trackitem)
4123 addtoplaylistLbl = self.main_view.get_songs_tab_add_to_playlist_label()
4124 self.pointing_device.click_object(addtoplaylistLbl)
4125@@ -513,9 +485,6 @@
4126 def test_artists_tab_album(self):
4127 """tests navigating to the Artists tab and playing an album"""
4128
4129- artistName = "Benjamin Kerensa"
4130- trackTitle = "Foss Yeaaaah! (Radio Edit)"
4131-
4132 # get number of tracks in queue before queuing a track
4133 initialtracksCount = self.main_view.get_queue_track_count()
4134
4135@@ -523,12 +492,12 @@
4136 self.main_view.switch_to_tab("artiststab")
4137
4138 #select artist
4139- artist = self.main_view.get_artists_artist(artistName)
4140+ artist = self.main_view.get_artists_artist(self.artistName)
4141 self.pointing_device.click_object(artist)
4142
4143 #get album sheet album artist
4144 sheet_albumartist = self.main_view.get_artist_sheet_artist()
4145- self.assertThat(sheet_albumartist.text, Equals(artistName))
4146+ self.assertThat(sheet_albumartist.text, Equals(self.artistName))
4147
4148 # click on album to shows the artists
4149 sheet_albumartist = self.main_view.get_artist_sheet_artist_cover()
4150@@ -536,13 +505,14 @@
4151
4152 #get song sheet album artist
4153 sheet_albumartist = self.main_view.get_album_sheet_artist()
4154- self.assertThat(sheet_albumartist.text, Equals(artistName))
4155+ self.assertThat(sheet_albumartist.text, Equals(self.artistName))
4156
4157 # click on song to populate queue and start playing
4158 self.pointing_device.click_object(sheet_albumartist)
4159
4160 #select artist
4161- track = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
4162+ track = self.main_view.get_album_sheet_listview_tracktitle(
4163+ self.trackTitle)
4164 self.pointing_device.click_object(track)
4165
4166 # verify track queue has added all songs to initial value
4167@@ -556,31 +526,25 @@
4168
4169 # verify song's metadata matches the item added to the Now Playing view
4170 queueArtistName = self.main_view.get_queue_now_playing_artist(
4171- artistName)
4172- self.assertThat(str(queueArtistName.text), Equals(artistName))
4173+ self.artistName)
4174+ self.assertThat(queueArtistName.text, Equals(self.artistName))
4175 queueTrackTitle = self.main_view.get_queue_now_playing_title(
4176- trackTitle)
4177- self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))
4178+ self.trackTitle)
4179+ self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
4180
4181 def test_swipe_to_delete_song(self):
4182 """tests navigating to the Now Playing queue, swiping to delete a
4183 track, and confirming the delete action. """
4184
4185- artistName = "Benjamin Kerensa"
4186-
4187 # populate queue
4188- first_genre_item = self.main_view.get_first_genre_item()
4189- self.pointing_device.click_object(first_genre_item)
4190- trackTitle = "Foss Yeaaaah! (Radio Edit)"
4191- song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
4192- self.pointing_device.click_object(song)
4193+ self.populate_and_play_queue_from_songs_tab()
4194
4195 # get initial queue count
4196 initialqueueCount = self.main_view.get_queue_track_count()
4197
4198 # get song to delete
4199 artistToDelete = self.main_view.get_queue_now_playing_artist(
4200- artistName)
4201+ self.artistName)
4202 musicnowplayingpage = self.main_view.get_MusicNowPlaying_page()
4203
4204 # get coordinates to delete song
4205@@ -604,7 +568,10 @@
4206
4207 def test_playback_stops_when_last_song_ends_and_repeat_off(self):
4208 """Check that playback stops when the last song in the queue ends"""
4209- self.populate_and_play_queue()
4210+
4211+ # populate queue
4212+ self.populate_and_play_queue_from_songs_tab()
4213+
4214 self.turn_shuffle_off()
4215 self.turn_repeat_off()
4216
4217@@ -620,7 +587,10 @@
4218
4219 def test_playback_repeats_when_last_song_ends_and_repeat_on(self):
4220 """With repeat on, the 1st song should play after the last one ends"""
4221- self.populate_and_play_queue()
4222+
4223+ # populate queue
4224+ self.populate_and_play_queue_from_songs_tab()
4225+
4226 self.turn_shuffle_off()
4227 self.turn_repeat_on()
4228
4229@@ -632,12 +602,15 @@
4230
4231 #Make sure we loop back to first song after last song ends
4232 actual_title = lambda: self.player.currentMetaTitle
4233- self.assertThat(actual_title, Eventually(Equals(self.FIRST_TITLE)))
4234+ self.assertThat(actual_title, Eventually(Equals(self.trackTitle)))
4235 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
4236
4237 def test_pressing_next_from_last_song_plays_first_when_repeat_on(self):
4238 """With repeat on, skipping the last song jumps to the first track"""
4239- self.populate_and_play_queue()
4240+
4241+ # populate queue
4242+ self.populate_and_play_queue_from_songs_tab()
4243+
4244 self.turn_shuffle_off()
4245 self.turn_repeat_on()
4246
4247@@ -648,12 +621,15 @@
4248 self.pointing_device.click_object(forward_button)
4249
4250 actual_title = lambda: self.player.currentMetaTitle
4251- self.assertThat(actual_title, Eventually(Equals(self.FIRST_TITLE)))
4252+ self.assertThat(actual_title, Eventually(Equals(self.trackTitle)))
4253 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
4254
4255 def test_pressing_prev_from_first_song_plays_last_when_repeat_on(self):
4256 """With repeat on, 'previous' from the 1st song plays the last one."""
4257- self.populate_and_play_queue()
4258+
4259+ # populate queue
4260+ self.populate_and_play_queue_from_songs_tab()
4261+
4262 self.turn_shuffle_off()
4263 self.turn_repeat_on()
4264
4265@@ -667,5 +643,5 @@
4266 self.pointing_device.click_object(prev_button)
4267
4268 actual_title = lambda: self.player.currentMetaTitle
4269- self.assertThat(actual_title, Eventually(Equals(self.LAST_TITLE)))
4270+ self.assertThat(actual_title, Eventually(Equals(self.lastTrackTitle)))
4271 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
4272
4273=== modified file 'worker-library-loader.js'
4274--- worker-library-loader.js 2013-12-05 00:23:08 +0000
4275+++ worker-library-loader.js 2014-06-19 01:49:30 +0000
4276@@ -1,7 +1,8 @@
4277 /*
4278- * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
4279- * Daniel Holm <d.holmen@gmail.com>
4280- * Victor Thompson <victor.thompson@gmail.com>
4281+ * Copyright (C) 2013, 2014
4282+ * Andrew Hayzen <ahayzen@gmail.com>
4283+ * Daniel Holm <d.holmen@gmail.com>
4284+ * Victor Thompson <victor.thompson@gmail.com>
4285 *
4286 * This program is free software; you can redistribute it and/or modify
4287 * it under the terms of the GNU General Public License as published by

Subscribers

People subscribed via source and target branches

to status/vote changes: