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

Proposed by Victor Thompson
Status: Merged
Approved by: Nicholas Skaggs
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
David Planella Needs Information
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.
Revision history for this message
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)

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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
Revision history for this message
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.

Revision history for this message
Victor Thompson (vthompson) wrote :

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
452. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
453. By Victor Thompson

* Initial test updates for mediascanner migration.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
454. By Victor Thompson

Merge trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
455. By Victor Thompson

Fix pyflakes error

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
456. By Victor Thompson

Fix bad merge conflict resolution

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
457. By Victor Thompson

empty

458. By Victor Thompson

Fix unused variable

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
459. By Victor Thompson

Merge trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
460. By Victor Thompson

Update test_play_pause_library test

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
461. By Andrew Hayzen

* Fix for test_play_pause_library touching indicators

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
462. By Victor Thompson

Find new customBackButton

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
463. By Victor Thompson

Patch db

464. By Andrew Hayzen

* Fixes for Pep8

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
465. By Victor Thompson

Patch SQLite DB.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
466. By Victor Thompson

* Fix play button on device
* Fix check for playing

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
467. By Victor Thompson

Test for only backButton

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
468. By Andrew Hayzen

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
469. By Andrew Hayzen

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

470. By Andrew Hayzen

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

471. By Victor Thompson

Re-write test_play_pause_library test

472. By Victor Thompson

Use new 14.10-dev1 framework

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
473. By Andrew Hayzen

* Fix for cover art

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
474. By Andrew Hayzen

* Fix for pyflakes

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
475. By Andrew Hayzen

* Revert framework bump for testing

476. By Andrew Hayzen

* Fix for blur background empty at startup

477. By Andrew Hayzen

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
478. By Victor Thompson

* Update app armor for confinement
* Update policy version

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
479. By Victor Thompson

Update copyright

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
480. By Andrew Hayzen

* Fix for patching home_dir

481. By Nicholas Skaggs

tweak env patching apparmor setup

482. By Nicholas Skaggs

simplify logic per thomi for local_location_qml

483. By Nicholas Skaggs

revert local_location tweak

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

* Readd genre support

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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)
Revision history for this message
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

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

update ap env isolation (mediascanner still will whine)

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

Fix play all of an album from the Artist albums sheet

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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)
Revision history for this message
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

revert to using backup and restore for database and Music folders

494. By Nicholas Skaggs

fix flake8, also backup mediascanner path

Revision history for this message
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

set /home to be ~/

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

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

wip

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
497. By Nicholas Skaggs

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
498. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

#blocked mediascanner2 bug 1326753

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
499. By Victor Thompson

Limiting SongsModels to 500 to ensure they at least load

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
500. By Andrew Hayzen

* Hack to limit all SongsModels to 500 tracks

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
501. By Victor Thompson

Merge trunk

Revision history for this message
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?

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

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

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

Fix destructive tests

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
503. By Victor Thompson

Stop and start mediascanner2 and also wait 10 seconds

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
504. By Nicholas Skaggs

all the hackiness at once

505. By Nicholas Skaggs

slightly saner version

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
506. By Nicholas Skaggs

pull out dbus launch

507. By Andrew Hayzen

* Add cleanups to autopilot

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
508. By Nicholas Skaggs

new fake db based on schema 6

509. By Nicholas Skaggs

rebase

510. By Nicholas Skaggs

removing debugging prints and waits; fix for dbus call

511. By Nicholas Skaggs

remove extraneous function

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
512. By Nicholas Skaggs

remove sys.exit from test

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
513. By Nicholas Skaggs

remove extra comment

514. By Nicholas Skaggs

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

515. By Nicholas Skaggs

more comments

516. By Nicholas Skaggs

merged victor's artist fix

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
517. By Nicholas Skaggs

add back backup/restore to create_music_libraray

518. By Nicholas Skaggs

flake8 goodness

519. By Victor Thompson

Cleanup

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
520. By Victor Thompson

revert last commit

521. By Nicholas Skaggs

remove extra cleanup steps

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
522. By Nicholas Skaggs

fix up isolation code

523. By Nicholas Skaggs

add back _patch_mediascanner_home

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
524. By Nicholas Skaggs

simplify backup/restore code

525. By Nicholas Skaggs

simplification, better expansion, pep8 fixes

526. By Victor Thompson

Update calls

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
527. By Victor Thompson

remove time

528. By Nicholas Skaggs

switch to subprocess

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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 . . .

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'LibraryListModel.qml'
--- LibraryListModel.qml 2014-02-22 21:46:04 +0000
+++ LibraryListModel.qml 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,7 +23,11 @@
21import "playlists.js" as Playlists23import "playlists.js" as Playlists
2224
23Item {25Item {
24 property ListModel model : ListModel { id: libraryModel }26 id: libraryListModelItem
27 property ListModel model : ListModel {
28 id: libraryModel
29 property var linkLibraryListModel: libraryListModelItem
30 }
25 property alias count: libraryModel.count31 property alias count: libraryModel.count
26 property var query: null32 property var query: null
27 property var param: null33 property var param: null
@@ -37,6 +43,13 @@
37 }43 }
38 }44 }
3945
46 /* Pretent to be like a mediascanner2 listmodel */
47 property alias rowCount: libraryModel.count
48
49 function get(index, role) {
50 return model.get(index);
51 }
52
40 WorkerScript {53 WorkerScript {
41 id: worker54 id: worker
42 source: "worker-library-loader.js"55 source: "worker-library-loader.js"
@@ -105,66 +118,6 @@
105 return -1;118 return -1;
106 }119 }
107120
108 function populate() {
109 console.log("called LibraryListModel::populate()")
110
111 // Save query for queue
112 query = Library.getAll
113 param = null
114
115 worker.list = Library.getAll();
116 }
117
118 function filterArtists() {
119 console.log("called LibraryListModel::filterArtists()")
120
121 // Save query for queue
122 query = Library.getArtists
123 param = null
124
125 worker.list = Library.getArtists();
126 }
127
128 function filterArtistTracks(artist) {
129 console.log("called LibraryListModel::filterArtistTracks()")
130
131 // Save query for queue
132 query = Library.getArtistTracks
133 param = artist
134
135 worker.list = Library.getArtistTracks(artist);
136 }
137
138 function filterArtistAlbums(artist) {
139 console.log("called LibraryListModel::filterArtistAlbums()")
140
141 // Save query for queue
142 query = Library.getArtistAlbums
143 param = artist
144
145 worker.list = Library.getArtistAlbums(artist);
146 }
147
148 function filterAlbums() {
149 console.log("called LibraryListModel::filterAlbums()")
150
151 // Save query for queue
152 query = Library.getAlbums
153 param = null
154
155 worker.list = Library.getAlbums();
156 }
157
158 function filterAlbumTracks(album) {
159 console.log("called LibraryListModel::filterAlbumTracks()")
160
161 // Save query for queue
162 query = Library.getAlbumTracks
163 param = album
164
165 worker.list = Library.getAlbumTracks(album);
166 }
167
168 function filterPlaylists() {121 function filterPlaylists() {
169 console.log("called LibraryListModel::filterPlaylistTracks()")122 console.log("called LibraryListModel::filterPlaylistTracks()")
170123
@@ -195,36 +148,10 @@
195 worker.list = Library.getRecent();148 worker.list = Library.getRecent();
196 }149 }
197150
198 function filterGenres() {
199 console.log("called LibraryListModel::filterGenres()")
200
201 // Save query for queue
202 query = Library.getGenres
203 param = null
204
205 worker.list = Library.getGenres();
206 }
207
208 function filterGenreTracks(genre) {
209 console.log("called LibraryListModel::filterGenreTracks()")
210
211 // Save query for queue
212 query = Library.getGenreTracks
213 param = genre
214
215 worker.list = Library.getGenreTracks(genre);
216 }
217
218 function clear() {151 function clear() {
219 if (worker.list !== null)152 if (worker.list !== null)
220 {153 {
221 worker.sendMessage({'clear': true, 'model': libraryModel})154 worker.sendMessage({'clear': true, 'model': libraryModel})
222 }155 }
223 }156 }
224
225 function filterSearch(searchQuery) {
226 query = Library.search
227 param = searchQuery
228 worker.list = Library.search(searchQuery)
229 }
230}157}
231158
=== modified file 'LoginLastFM.qml'
--- LoginLastFM.qml 2014-01-11 17:41:20 +0000
+++ LoginLastFM.qml 2014-06-19 01:49:30 +0000
@@ -24,7 +24,6 @@
24import QtQuick.LocalStorage 2.024import QtQuick.LocalStorage 2.0
25import QtQuick.XmlListModel 2.025import QtQuick.XmlListModel 2.0
26import "settings.js" as Settings26import "settings.js" as Settings
27import "meta-database.js" as Library
28import "scrobble.js" as Scrobble27import "scrobble.js" as Scrobble
2928
30// Last.fm login dialog29// Last.fm login dialog
3130
=== modified file 'MusicAlbums.qml'
--- MusicAlbums.qml 2014-04-17 14:42:51 +0000
+++ MusicAlbums.qml 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -17,14 +19,16 @@
1719
18import QtQuick 2.020import QtQuick 2.0
19import Ubuntu.Components 0.121import Ubuntu.Components 0.1
22import Ubuntu.Components 1.1 as Toolkit
20import Ubuntu.Components.ListItems 0.123import Ubuntu.Components.ListItems 0.1
21import Ubuntu.Components.Popups 0.124import Ubuntu.Components.Popups 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem25import Ubuntu.Components.ListItems 0.1 as ListItem
26import Ubuntu.MediaScanner 0.1
27import Ubuntu.Thumbnailer 0.1
23import QtMultimedia 5.028import QtMultimedia 5.0
24import QtQuick.LocalStorage 2.029import QtQuick.LocalStorage 2.0
25import QtGraphicalEffects 1.030import QtGraphicalEffects 1.0
26import "settings.js" as Settings31import "settings.js" as Settings
27import "meta-database.js" as Library
28import "playlists.js" as Playlists32import "playlists.js" as Playlists
29import "common"33import "common"
3034
@@ -59,21 +63,23 @@
59 anchors.bottomMargin: units.gu(1)63 anchors.bottomMargin: units.gu(1)
60 cellHeight: height/364 cellHeight: height/3
61 cellWidth: height/365 cellWidth: height/3
62 model: albumModel.model66 model: Toolkit.SortFilterModel {
67 id: albumsModelFilter
68 property alias rowCount: albumsModel.rowCount
69 model: AlbumsModel {
70 id: albumsModel
71 store: musicStore
72 }
73 sort.property: "title"
74 sort.order: Qt.AscendingOrder
75 }
76
63 delegate: albumDelegate77 delegate: albumDelegate
64 flow: GridView.TopToBottom78 flow: GridView.TopToBottom
6579
66 Component {80 Component {
67 id: albumDelegate81 id: albumDelegate
68 Item {82 Item {
69 property string artist: model.artist
70 property string album: model.album
71 property string title: model.title
72 property string cover: model.cover !== "" ? model.cover : Qt.resolvedUrl("images/music-app-cover@30.png")
73 property string length: model.length
74 property string file: model.file
75 property string year: model.year
76
77 id: albumItem83 id: albumItem
78 height: albumlist.cellHeight - units.gu(1)84 height: albumlist.cellHeight - units.gu(1)
79 width: albumlist.cellHeight - units.gu(1)85 width: albumlist.cellHeight - units.gu(1)
@@ -85,7 +91,7 @@
85 image: Image {91 image: Image {
86 id: icon92 id: icon
87 fillMode: Image.Stretch93 fillMode: Image.Stretch
88 source: cover94 source: "image://albumart/artist=" + model.artist + "&album=" + model.title
89 onStatusChanged: {95 onStatusChanged: {
90 if (status === Image.Error) {96 if (status === Image.Error) {
91 source = Qt.resolvedUrl("images/music-app-cover@30.png")97 source = Qt.resolvedUrl("images/music-app-cover@30.png")
@@ -118,7 +124,7 @@
118 anchors.rightMargin: units.gu(1)124 anchors.rightMargin: units.gu(1)
119 color: styleMusic.nowPlaying.labelSecondaryColor125 color: styleMusic.nowPlaying.labelSecondaryColor
120 elide: Text.ElideRight126 elide: Text.ElideRight
121 text: artist127 text: model.artist
122 fontSize: "x-small"128 fontSize: "x-small"
123 }129 }
124 Label {130 Label {
@@ -131,7 +137,7 @@
131 anchors.rightMargin: units.gu(1)137 anchors.rightMargin: units.gu(1)
132 color: styleMusic.common.white138 color: styleMusic.common.white
133 elide: Text.ElideRight139 elide: Text.ElideRight
134 text: album140 text: model.title
135 fontSize: "small"141 fontSize: "small"
136 }142 }
137 }143 }
@@ -143,14 +149,12 @@
143 onPressAndHold: {149 onPressAndHold: {
144 }150 }
145 onClicked: {151 onClicked: {
146 albumTracksModel.filterAlbumTracks(album)152 songsSheet.album = model.title;
147153 songsSheet.genre = undefined
148 songsSheet.line1 = artist154 songsSheet.line1 = model.artist
149 songsSheet.line2 = album155 songsSheet.line2 = model.title
150 songsSheet.isAlbum = true156 songsSheet.isAlbum = true
151 songsSheet.file = file157 songsSheet.covers = [{author: model.artist, album: model.title}]
152 songsSheet.year = year
153 songsSheet.covers = [cover]
154 PopupUtils.open(songsSheet.sheet)158 PopupUtils.open(songsSheet.sheet)
155 }159 }
156 }160 }
157161
=== modified file 'MusicArtists.qml'
--- MusicArtists.qml 2014-05-19 23:13:39 +0000
+++ MusicArtists.qml 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -20,6 +22,8 @@
20import Ubuntu.Components.ListItems 0.122import Ubuntu.Components.ListItems 0.1
21import Ubuntu.Components.Popups 0.123import Ubuntu.Components.Popups 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem24import Ubuntu.Components.ListItems 0.1 as ListItem
25import Ubuntu.MediaScanner 0.1
26import Ubuntu.Thumbnailer 0.1
23import QtMultimedia 5.027import QtMultimedia 5.0
24import QtQuick.LocalStorage 2.028import QtQuick.LocalStorage 2.0
25import "settings.js" as Settings29import "settings.js" as Settings
@@ -47,7 +51,12 @@
47 id: artistlist51 id: artistlist
48 anchors.fill: parent52 anchors.fill: parent
49 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight53 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
50 model: artistModel.model54 model: ArtistsModel {
55 id: artistsModel
56 albumArtists: true
57 store: musicStore
58 }
59
51 delegate: artistDelegate60 delegate: artistDelegate
5261
53 Component {62 Component {
@@ -55,9 +64,46 @@
5564
56 ListItem.Standard {65 ListItem.Standard {
57 id: track66 id: track
58 property string artist: model.artist
59 height: styleMusic.common.itemHeight67 height: styleMusic.common.itemHeight
6068
69 AlbumsModel {
70 id: albumArtistModel
71 albumArtist: model.artist
72 store: musicStore
73 }
74
75 Repeater {
76 id: albumArtistModelRepeater
77 model: albumArtistModel
78 delegate: Item {
79 property string author: model.artist
80 property string album: model.title
81 }
82 property var covers: []
83 signal finished()
84
85 onFinished: {
86 coverRow.count = count
87 coverRow.covers = covers
88 }
89 onItemAdded: {
90 covers.push({author: item.author, album: item.album});
91
92 if (index === count - 1) {
93 finished();
94 }
95 }
96 }
97
98 SongsModel {
99 id: songArtistModel
100 albumArtist: model.artist
101 // HACK: Temporarily setting limit to 500 to ensure model
102 // is populated. See lp:1326753
103 limit: 500
104 store: musicStore
105 }
106
61 CoverRow {107 CoverRow {
62 id: coverRow108 id: coverRow
63 anchors {109 anchors {
@@ -65,9 +111,10 @@
65 left: parent.left111 left: parent.left
66 margins: units.gu(1)112 margins: units.gu(1)
67 }113 }
68 count: parseInt(Library.getArtistCovers(artist).length)114
115 count: 0
69 size: styleMusic.common.albumSize116 size: styleMusic.common.albumSize
70 covers: Library.getArtistCovers(artist)117 covers: []
71 }118 }
72119
73 Label {120 Label {
@@ -86,7 +133,7 @@
86 rightMargin: units.gu(1.5)133 rightMargin: units.gu(1.5)
87 }134 }
88 elide: Text.ElideRight135 elide: Text.ElideRight
89 text: artist136 text: model.artist
90 }137 }
91138
92 Label {139 Label {
@@ -103,8 +150,7 @@
103 rightMargin: units.gu(1.5)150 rightMargin: units.gu(1.5)
104 }151 }
105 elide: Text.ElideRight152 elide: Text.ElideRight
106 // model for number of albums?153 text: i18n.tr("%1 album", "%1 albums", albumArtistModel.rowCount).arg(albumArtistModel.rowCount)
107 text: i18n.tr("%1 album", "%1 albums", Library.getArtistAlbumCount(artist)).arg(Library.getArtistAlbumCount(artist))
108 }154 }
109155
110 Label {156 Label {
@@ -121,7 +167,7 @@
121 rightMargin: units.gu(1.5)167 rightMargin: units.gu(1.5)
122 }168 }
123 elide: Text.ElideRight169 elide: Text.ElideRight
124 text: i18n.tr("%1 song", "%1 songs", Library.getArtistTracks(artist).length).arg(Library.getArtistTracks(artist).length)170 text: i18n.tr("%1 song", "%1 songs", songArtistModel.rowCount).arg(songArtistModel.rowCount)
125 }171 }
126 onFocusChanged: {172 onFocusChanged: {
127 }173 }
@@ -132,14 +178,11 @@
132 onPressAndHold: {178 onPressAndHold: {
133 }179 }
134 onClicked: {180 onClicked: {
135 artistAlbumsModel.filterArtistAlbums(artist)181 artistSheet.artist = model.artist
136 artistSheet.artist = artist
137 artistSheet.covers = coverRow.covers182 artistSheet.covers = coverRow.covers
138 PopupUtils.open(artistSheet.sheet)183 PopupUtils.open(artistSheet.sheet)
139 }184 }
140 }185 }
141 Component.onCompleted: {
142 }
143 }186 }
144 }187 }
145 }188 }
146189
=== modified file 'MusicNowPlaying.qml'
--- MusicNowPlaying.qml 2014-05-08 22:58:35 +0000
+++ MusicNowPlaying.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -22,9 +23,9 @@
22import QtQuick.LocalStorage 2.023import QtQuick.LocalStorage 2.0
23import Ubuntu.Components 0.124import Ubuntu.Components 0.1
24import Ubuntu.Components.ListItems 0.1 as ListItem25import Ubuntu.Components.ListItems 0.1 as ListItem
26import Ubuntu.Thumbnailer 0.1
25import "common"27import "common"
26import "common/ExpanderItems"28import "common/ExpanderItems"
27import "meta-database.js" as Library
28import "settings.js" as Settings29import "settings.js" as Settings
2930
30Page {31Page {
@@ -254,8 +255,8 @@
254255
255 onClicked: {256 onClicked: {
256 collapseSwipeDelete(-1); // collapse all expands257 collapseSwipeDelete(-1); // collapse all expands
257 customdebug("File: " + model.file) // debugger258 customdebug("File: " + model.filename) // debugger
258 trackClicked(trackQueue, index) // play track259 trackQueueClick(index); // toggle track state
259 }260 }
260261
261 onMouseXChanged: {262 onMouseXChanged: {
@@ -309,7 +310,7 @@
309 {310 {
310 collapseSwipeDelete(-1); // collapse all swipedeletes311 collapseSwipeDelete(-1); // collapse all swipedeletes
311 collapseExpand(); // collapse all312 collapseExpand(); // collapse all
312 customdebug("Pressed and held queued track "+model.file)313 customdebug("Pressed and held queued track "+model.filename)
313 queuelist.state = "reorder"; // enable reordering state314 queuelist.state = "reorder"; // enable reordering state
314 trackContainerReorderAnimation.start();315 trackContainerReorderAnimation.start();
315 }316 }
@@ -490,7 +491,6 @@
490491
491 // Remove item from queue and clear caches492 // Remove item from queue and clear caches
492 trackQueue.model.remove(index);493 trackQueue.model.remove(index);
493 queueChanged = true;
494 }494 }
495 }495 }
496 }496 }
@@ -537,7 +537,7 @@
537 height: (queueListItem.state === "current" ? queuelist.currentHeight - units.gu(8) : queuelist.normalHeight) - units.gu(2)537 height: (queueListItem.state === "current" ? queuelist.currentHeight - units.gu(8) : queuelist.normalHeight) - units.gu(2)
538 width: height538 width: height
539 image: Image {539 image: Image {
540 source: cover !== "" ? cover : "images/music-app-cover@30.png"540 source: "image://albumart/artist=" + model.author + "&album=" + model.album
541 onStatusChanged: {541 onStatusChanged: {
542 if (status === Image.Error) {542 if (status === Image.Error) {
543 source = Qt.resolvedUrl("images/music-app-cover@30.png")543 source = Qt.resolvedUrl("images/music-app-cover@30.png")
@@ -579,7 +579,7 @@
579 color: styleMusic.nowPlaying.labelSecondaryColor579 color: styleMusic.nowPlaying.labelSecondaryColor
580 elide: Text.ElideRight580 elide: Text.ElideRight
581 height: units.gu(1)581 height: units.gu(1)
582 text: artist582 text: model.author
583 fontSize: 'small'583 fontSize: 'small'
584 width: parent.width - trackImage.width - units.gu(3.5)584 width: parent.width - trackImage.width - units.gu(3.5)
585 x: trackImage.x + trackImage.width + units.gu(1)585 x: trackImage.x + trackImage.width + units.gu(1)
@@ -591,7 +591,7 @@
591 color: styleMusic.common.white591 color: styleMusic.common.white
592 elide: Text.ElideRight592 elide: Text.ElideRight
593 height: units.gu(1)593 height: units.gu(1)
594 text: title594 text: model.title
595 fontSize: 'medium'595 fontSize: 'medium'
596 width: parent.width - trackImage.width - units.gu(3.5)596 width: parent.width - trackImage.width - units.gu(3.5)
597 x: trackImage.x + trackImage.width + units.gu(1)597 x: trackImage.x + trackImage.width + units.gu(1)
@@ -603,7 +603,7 @@
603 color: styleMusic.nowPlaying.labelSecondaryColor603 color: styleMusic.nowPlaying.labelSecondaryColor
604 elide: Text.ElideRight604 elide: Text.ElideRight
605 height: units.gu(1)605 height: units.gu(1)
606 text: album606 text: model.album
607 fontSize: 'x-small'607 fontSize: 'x-small'
608 width: parent.width - trackImage.width - units.gu(3.5)608 width: parent.width - trackImage.width - units.gu(3.5)
609 x: trackImage.x + trackImage.width + units.gu(1)609 x: trackImage.x + trackImage.width + units.gu(1)
610610
=== modified file 'MusicPlaylists.qml'
--- MusicPlaylists.qml 2014-04-26 17:13:02 +0000
+++ MusicPlaylists.qml 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Daniel Holm <d.holmen@gmail.com>2 * Copyright (C) 2013, 2014
3 Victor Thompson <victor.thompson@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 *7 *
6 * This program is free software; you can redistribute it and/or modify8 * This program is free software; you can redistribute it and/or modify
@@ -24,7 +26,6 @@
24import QtMultimedia 5.026import QtMultimedia 5.0
25import QtQuick.LocalStorage 2.027import QtQuick.LocalStorage 2.0
26import "settings.js" as Settings28import "settings.js" as Settings
27import "meta-database.js" as Library
28import "scrobble.js" as Scrobble29import "scrobble.js" as Scrobble
29import "playlists.js" as Playlists30import "playlists.js" as Playlists
30import "common"31import "common"
3132
=== modified file 'MusicSearch.qml'
--- MusicSearch.qml 2014-05-14 23:39:19 +0000
+++ MusicSearch.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,9 +22,10 @@
21import Ubuntu.Components 0.122import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem23import Ubuntu.Components.ListItems 0.1 as ListItem
23import Ubuntu.Components.Popups 0.124import Ubuntu.Components.Popups 0.1
25import Ubuntu.MediaScanner 0.1
26import Ubuntu.Thumbnailer 0.1
24import QtQuick.LocalStorage 2.027import QtQuick.LocalStorage 2.0
25import "playlists.js" as Playlists28import "playlists.js" as Playlists
26import "meta-database.js" as Library
27import "common"29import "common"
28import "common/ExpanderItems"30import "common/ExpanderItems"
2931
@@ -93,18 +95,13 @@
93 // Provide a small pause before search95 // Provide a small pause before search
94 Timer {96 Timer {
95 id: searchTimer97 id: searchTimer
96 interval: 150098 interval: 500
97 repeat: false99 repeat: false
98 onTriggered: {100 onTriggered: {
99 if(searchField.text) {101 songsSearchModel.query = searchField.text;
100 searchModel.filterSearch(searchField.text) // query the databse102 searchActivity.running = true // start the activity indicator
101 searchActivity.running = true // start the activity indicator103
102 }104 indicatorTimer.start()
103 else {
104 customdebug("No search terms.")
105 searchModel.filterSearch("empty somehow?")
106 }
107 indicatorTimer.start()
108 }105 }
109 }106 }
110 // and onother one for the indicator107 // and onother one for the indicator
@@ -133,6 +130,7 @@
133 width: parent.width130 width: parent.width
134 height: parent.height131 height: parent.height
135 color: "transparent"132 color: "transparent"
133 visible: searchField.text
136 clip: true134 clip: true
137 anchors {135 anchors {
138 top: searchField.bottom136 top: searchField.bottom
@@ -147,7 +145,10 @@
147 objectName: "searchtrackview"145 objectName: "searchtrackview"
148 width: parent.width146 width: parent.width
149 height: parent.width147 height: parent.width
150 model: searchModel.model148 model: SongsSearchModel {
149 id: songsSearchModel
150 store: musicStore
151 }
151152
152 onMovementStarted: {153 onMovementStarted: {
153 searchTrackView.forceActiveFocus()154 searchTrackView.forceActiveFocus()
@@ -158,18 +159,12 @@
158 objectName: "playlist"159 objectName: "playlist"
159 width: parent.width160 width: parent.width
160 height: styleMusic.common.itemHeight161 height: styleMusic.common.itemHeight
161 property string title: model.title
162 property string artist: model.artist
163 property string file: model.file
164 property string album: model.album
165 property string cover: model.cover
166 property string genre: model.genre
167162
168 onClicked: {163 onClicked: {
169 console.debug("Debug: "+title+" added to queue")164 console.debug("Debug: "+title+" added to queue")
170 // now play this track, but keep current queue165 // now play this track, but keep current queue
171 trackQueue.append(model)166 trackQueue.append(model)
172 trackClicked(trackQueue, trackQueue.model.count - 1, true)167 trackQueueClick(trackQueue.model.count - 1);
173 onDoneClicked: PopupUtils.close(searchTrack)168 onDoneClicked: PopupUtils.close(searchTrack)
174 }169 }
175170
@@ -189,7 +184,7 @@
189 width: styleMusic.common.albumSize184 width: styleMusic.common.albumSize
190 height: styleMusic.common.albumSize185 height: styleMusic.common.albumSize
191 image: Image {186 image: Image {
192 source: cover !== "" ? cover : Qt.resolvedUrl("images/music-app-cover@30.png")187 source: "image://albumart/artist=" + model.author + "&album=" + model.title
193 onStatusChanged: {188 onStatusChanged: {
194 if (status === Image.Error) {189 if (status === Image.Error) {
195 source = Qt.resolvedUrl("images/music-app-cover@30.png")190 source = Qt.resolvedUrl("images/music-app-cover@30.png")
@@ -211,7 +206,7 @@
211 anchors.right: parent.right206 anchors.right: parent.right
212 anchors.rightMargin: units.gu(1.5)207 anchors.rightMargin: units.gu(1.5)
213 elide: Text.ElideRight208 elide: Text.ElideRight
214 text: artist209 text: model.author
215 }210 }
216 Label {211 Label {
217 id: trackTitle212 id: trackTitle
@@ -227,7 +222,7 @@
227 anchors.right: parent.right222 anchors.right: parent.right
228 anchors.rightMargin: units.gu(1.5)223 anchors.rightMargin: units.gu(1.5)
229 elide: Text.ElideRight224 elide: Text.ElideRight
230 text: title225 text: model.title
231 }226 }
232 Label {227 Label {
233 id: trackAlbum228 id: trackAlbum
@@ -242,7 +237,7 @@
242 anchors.right: parent.right237 anchors.right: parent.right
243 anchors.rightMargin: units.gu(1.5)238 anchors.rightMargin: units.gu(1.5)
244 elide: Text.ElideRight239 elide: Text.ElideRight
245 text: album240 text: model.album
246 }241 }
247 Label {242 Label {
248 id: trackDuration243 id: trackDuration
@@ -267,7 +262,7 @@
267 fill: parent262 fill: parent
268 }263 }
269 listItem: search264 listItem: search
270 model: searchModel.model.get(index)265 model: songsSearchModel.get(index, songsSearchModel.RoleModelData)
271 row: Row {266 row: Row {
272 AddToPlaylist {267 AddToPlaylist {
273268
274269
=== modified file 'MusicStart.qml'
--- MusicStart.qml 2014-06-02 15:51:48 +0000
+++ MusicStart.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -18,9 +19,12 @@
1819
19import QtQuick 2.020import QtQuick 2.0
20import Ubuntu.Components 0.121import Ubuntu.Components 0.1
22import Ubuntu.Components 1.1 as Toolkit
21import Ubuntu.Components.ListItems 0.123import Ubuntu.Components.ListItems 0.1
22import Ubuntu.Components.Popups 0.124import Ubuntu.Components.Popups 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem25import Ubuntu.Components.ListItems 0.1 as ListItem
26import Ubuntu.MediaScanner 0.1
27import Ubuntu.Thumbnailer 0.1
24import QtMultimedia 5.028import QtMultimedia 5.0
25import QtQuick.LocalStorage 2.029import QtQuick.LocalStorage 2.0
26import "settings.js" as Settings30import "settings.js" as Settings
@@ -94,7 +98,7 @@
94 model: recentModel.model98 model: recentModel.model
95 delegate: recentDelegate99 delegate: recentDelegate
96 header: Item {100 header: Item {
97 id: spacer101 id: recentSpacer
98 width: units.gu(1)102 width: units.gu(1)
99 }103 }
100 footer: Item {104 footer: Item {
@@ -121,7 +125,7 @@
121 Item {125 Item {
122 property string title: model.title126 property string title: model.title
123 property string title2: model.title2127 property string title2: model.title2
124 property var covers: type === "playlist" ? Playlists.getPlaylistCovers(title) : [Library.getAlbumCover(title)]128 property var covers: type === "playlist" ? Playlists.getPlaylistCovers(title) : [{author: model.title2, album: model.title}]
125 property string type: model.type129 property string type: model.type
126 property string time: model.time130 property string time: model.time
127 property string key: model.key131 property string key: model.key
@@ -187,8 +191,9 @@
187 if (type === "playlist") {191 if (type === "playlist") {
188 albumTracksModel.filterPlaylistTracks(key)192 albumTracksModel.filterPlaylistTracks(key)
189 } else {193 } else {
190 albumTracksModel.filterAlbumTracks(title)194 songsSheet.album = title;
191 }195 }
196 songsSheet.genre = undefined;
192197
193 songsSheet.line1 = title2198 songsSheet.line1 = title2
194 songsSheet.line2 = title199 songsSheet.line2 = title
@@ -226,10 +231,13 @@
226 anchors.topMargin: units.gu(1)231 anchors.topMargin: units.gu(1)
227 spacing: units.gu(1)232 spacing: units.gu(1)
228 height: units.gu(18)233 height: units.gu(18)
229 model: genreModel.model234 model: GenresModel {
235 store: musicStore
236 }
237
230 delegate: genreDelegate238 delegate: genreDelegate
231 header: Item {239 header: Item {
232 id: spacer240 id: genreSpacer
233 width: units.gu(1)241 width: units.gu(1)
234 }242 }
235 orientation: ListView.Horizontal243 orientation: ListView.Horizontal
@@ -237,19 +245,47 @@
237 Component {245 Component {
238 id: genreDelegate246 id: genreDelegate
239 Item {247 Item {
240 property string artist: model.artist
241 property string album: model.album
242 property string title: model.title
243 property var covers: Library.getGenreCovers(model.genre)
244 property string length: model.length
245 property string file: model.file
246 property string year: model.year
247 property string genre: model.genre
248
249 id: genreItem248 id: genreItem
250 objectName: "genreItemObject"249 objectName: "genreItemObject"
251 height: genrelist.height - units.gu(1)250 height: genrelist.height - units.gu(1)
252 width: height251 width: height
252
253 Repeater {
254 id: albumGenreModelRepeater
255 model: AlbumsModel {
256 genre: model.genre
257 store: musicStore
258 }
259
260 delegate: Item {
261 property string author: model.artist
262 property string album: model.title
263 }
264 property var covers: []
265 signal finished()
266
267 onFinished: {
268 genreShape.count = count
269 genreShape.covers = covers
270 }
271 onItemAdded: {
272 covers.push({author: item.author, album: item.album});
273
274 if (index === count - 1) {
275 finished();
276 }
277 }
278 }
279
280 SongsModel {
281 id: songGenreModel
282 genre: model.genre
283 // HACK: Temporarily setting limit to 500 to ensure model
284 // is populated. See lp:1326753
285 limit: 500
286 store: musicStore
287 }
288
253 CoverRow {289 CoverRow {
254 id: genreShape290 id: genreShape
255 anchors {291 anchors {
@@ -257,19 +293,20 @@
257 left: parent.left293 left: parent.left
258 verticalCenter: parent.verticalCenter294 verticalCenter: parent.verticalCenter
259 }295 }
260 count: genreItem.covers.length296 count: 0
261 size: genreItem.width297 size: genreItem.width
262 covers: genreItem.covers298 covers: []
263 spacing: units.gu(2)299 spacing: units.gu(2)
264 }300 }
265 MouseArea {301 MouseArea {
266 anchors.fill: parent302 anchors.fill: parent
267 onClicked: {303 onClicked: {
268 albumTracksModel.filterGenreTracks(genre)304 songsSheet.album = undefined
305 songsSheet.genre = model.genre
269 songsSheet.line1 = "Genre"306 songsSheet.line1 = "Genre"
270 songsSheet.line2 = genre307 songsSheet.line2 = model.genre
271 songsSheet.isAlbum = false308 songsSheet.isAlbum = true
272 songsSheet.covers = covers309 songsSheet.covers = genreShape.covers
273 PopupUtils.open(songsSheet.sheet)310 PopupUtils.open(songsSheet.sheet)
274 }311 }
275 }312 }
@@ -298,7 +335,7 @@
298 anchors.rightMargin: units.gu(1)335 anchors.rightMargin: units.gu(1)
299 color: styleMusic.common.white336 color: styleMusic.common.white
300 elide: Text.ElideRight337 elide: Text.ElideRight
301 text: genre338 text: model.genre
302 fontSize: "small"339 fontSize: "small"
303 }340 }
304 Label {341 Label {
@@ -311,7 +348,7 @@
311 anchors.rightMargin: units.gu(1)348 anchors.rightMargin: units.gu(1)
312 color: styleMusic.nowPlaying.labelSecondaryColor349 color: styleMusic.nowPlaying.labelSecondaryColor
313 elide: Text.ElideRight350 elide: Text.ElideRight
314 text: i18n.tr("%1 song", "%1 songs", model.total).arg(model.total)351 text: i18n.tr("%1 song", "%1 songs", songGenreModel.rowCount).arg(songGenreModel.rowCount)
315 fontSize: "x-small"352 fontSize: "x-small"
316 }353 }
317 }354 }
@@ -343,7 +380,16 @@
343 anchors.topMargin: units.gu(1)380 anchors.topMargin: units.gu(1)
344 spacing: units.gu(1)381 spacing: units.gu(1)
345 height: units.gu(18)382 height: units.gu(18)
346 model: albumModel.model383 model: Toolkit.SortFilterModel {
384 id: albumsModelFilter
385 property alias rowCount: albumsModel.rowCount
386 model: AlbumsModel {
387 id: albumsModel
388 store: musicStore
389 }
390 sort.property: "title"
391 sort.order: Qt.AscendingOrder
392 }
347 delegate: albumDelegate393 delegate: albumDelegate
348 header: Item {394 header: Item {
349 id: albumSpacer395 id: albumSpacer
@@ -355,12 +401,8 @@
355 id: albumDelegate401 id: albumDelegate
356 Item {402 Item {
357 property string artist: model.artist403 property string artist: model.artist
358 property string album: model.album404 property string album: model.title
359 property var covers: [Library.getAlbumCover(album)]405 property var covers: [{author: model.artist, album: model.title}]
360 property string length: model.length
361 property string file: model.file
362 property string year: model.year
363 property string genre: model.genre
364406
365 id: albumItem407 id: albumItem
366 objectName: "albumItemObject"408 objectName: "albumItemObject"
@@ -381,7 +423,8 @@
381 MouseArea {423 MouseArea {
382 anchors.fill: parent424 anchors.fill: parent
383 onClicked: {425 onClicked: {
384 albumTracksModel.filterAlbumTracks(album)426 songsSheet.album = album;
427 songsSheet.genre = undefined
385 songsSheet.line1 = artist428 songsSheet.line1 = artist
386 songsSheet.line2 = album429 songsSheet.line2 = album
387 songsSheet.isAlbum = true430 songsSheet.isAlbum = true
388431
=== modified file 'MusicToolbar.qml'
--- MusicToolbar.qml 2014-05-04 05:30:54 +0000
+++ MusicToolbar.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,7 +22,6 @@
21import QtMultimedia 5.022import QtMultimedia 5.0
22import Ubuntu.Components 0.123import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 0.124import Ubuntu.Components.Popups 0.1
24import "meta-database.js" as Library
25import "settings.js" as Settings25import "settings.js" as Settings
2626
27Item {27Item {
2828
=== modified file 'MusicTracks.qml'
--- MusicTracks.qml 2014-04-26 17:13:02 +0000
+++ MusicTracks.qml 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -17,12 +19,14 @@
1719
18import QtQuick 2.020import QtQuick 2.0
19import Ubuntu.Components 0.121import Ubuntu.Components 0.1
22import Ubuntu.Components 1.1 as Toolkit
20import Ubuntu.Components.ListItems 0.123import Ubuntu.Components.ListItems 0.1
21import Ubuntu.Components.ListItems 0.1 as ListItem24import Ubuntu.Components.ListItems 0.1 as ListItem
25import Ubuntu.MediaScanner 0.1
26import Ubuntu.Thumbnailer 0.1
22import QtMultimedia 5.027import QtMultimedia 5.0
23import QtQuick.LocalStorage 2.028import QtQuick.LocalStorage 2.0
24import "settings.js" as Settings29import "settings.js" as Settings
25import "meta-database.js" as Library
26import "playlists.js" as Playlists30import "playlists.js" as Playlists
27import "common"31import "common"
28import "common/ExpanderItems"32import "common/ExpanderItems"
@@ -43,23 +47,30 @@
43 }47 }
44 }48 }
4549
50
46 ListView {51 ListView {
47 id: tracklist52 id: tracklist
48 anchors.fill: parent53 anchors.fill: parent
49 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight54 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
50 highlightFollowsCurrentItem: false55 highlightFollowsCurrentItem: false
51 model: libraryModel.model56 model: Toolkit.SortFilterModel {
57 id: songsModelFilter
58 property alias rowCount: songsModel.rowCount
59 model: SongsModel {
60 id: songsModel
61 // HACK: Temporarily setting limit to 500 to ensure model
62 // is populated. See lp:1326753
63 limit: 500
64 store: musicStore
65 }
66 sort.property: "title"
67 sort.order: Qt.AscendingOrder
68 }
52 delegate: trackDelegate69 delegate: trackDelegate
53 Component {70 Component {
54 id: trackDelegate71 id: trackDelegate
55 ListItem.Standard {72 ListItem.Standard {
56 id: track73 id: track
57 property string artist: model.artist
58 property string album: model.album
59 property string title: model.title
60 property string cover: model.cover
61 property string length: model.length
62 property string file: model.file
63 width: parent.width74 width: parent.width
64 height: styleMusic.common.itemHeight75 height: styleMusic.common.itemHeight
6576
@@ -70,7 +81,7 @@
70 focus = true81 focus = true
71 }82 }
7283
73 trackClicked(libraryModel, index) // play track84 trackClicked(tracklist.model, index) // play track
74 }85 }
75 }86 }
7687
@@ -90,7 +101,7 @@
90 width: styleMusic.common.albumSize101 width: styleMusic.common.albumSize
91 height: styleMusic.common.albumSize102 height: styleMusic.common.albumSize
92 image: Image {103 image: Image {
93 source: cover !== "" ? cover : Qt.resolvedUrl("images/music-app-cover@30.png")104 source: "image://albumart/artist=" + model.author + "&album=" + model.album
94 onStatusChanged: {105 onStatusChanged: {
95 if (status === Image.Error) {106 if (status === Image.Error) {
96 source = Qt.resolvedUrl("images/music-app-cover@30.png")107 source = Qt.resolvedUrl("images/music-app-cover@30.png")
@@ -111,7 +122,7 @@
111 anchors.right: parent.right122 anchors.right: parent.right
112 anchors.rightMargin: units.gu(1.5)123 anchors.rightMargin: units.gu(1.5)
113 elide: Text.ElideRight124 elide: Text.ElideRight
114 text: artist125 text: model.author
115 }126 }
116 Label {127 Label {
117 id: trackTitle128 id: trackTitle
@@ -127,7 +138,7 @@
127 anchors.right: parent.right138 anchors.right: parent.right
128 anchors.rightMargin: units.gu(1.5)139 anchors.rightMargin: units.gu(1.5)
129 elide: Text.ElideRight140 elide: Text.ElideRight
130 text: track.title141 text: model.title
131 }142 }
132 Label {143 Label {
133 id: trackAlbum144 id: trackAlbum
@@ -142,7 +153,7 @@
142 anchors.right: parent.right153 anchors.right: parent.right
143 anchors.rightMargin: units.gu(1.5)154 anchors.rightMargin: units.gu(1.5)
144 elide: Text.ElideRight155 elide: Text.ElideRight
145 text: album156 text: model.album
146 }157 }
147 Label {158 Label {
148 id: trackDuration159 id: trackDuration
@@ -157,7 +168,7 @@
157 anchors.rightMargin: units.gu(1.5)168 anchors.rightMargin: units.gu(1.5)
158 elide: Text.ElideRight169 elide: Text.ElideRight
159 visible: false170 visible: false
160 text: ""171 text: "" // model.duration
161 }172 }
162 }173 }
163174
@@ -167,7 +178,7 @@
167 fill: parent178 fill: parent
168 }179 }
169 listItem: track180 listItem: track
170 model: libraryModel.model.get(index)181 model: songsModelFilter.get(index, songsModelFilter.RoleModelData)
171 row: Row {182 row: Row {
172 AddToPlaylist {183 AddToPlaylist {
173184
174185
=== modified file 'MusicaddtoPlaylist.qml'
--- MusicaddtoPlaylist.qml 2014-04-26 17:13:02 +0000
+++ MusicaddtoPlaylist.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -78,13 +79,13 @@
78 property string name: model.name79 property string name: model.name
79 property string count: model.count80 property string count: model.count
80 onClicked: {81 onClicked: {
81 console.debug("Debug: "+chosenElement.file+" added to "+name)82 console.debug("Debug: "+chosenElement.filename+" added to "+name)
82 Playlists.addtoPlaylist(name,83 Playlists.addtoPlaylist(name,
83 chosenElement.file,84 chosenElement.filename,
84 chosenElement.artist,85 chosenElement.author,
85 chosenElement.title,86 chosenElement.title,
86 chosenElement.album,87 chosenElement.album,
87 chosenElement.cover,88 chosenElement.art,
88 "","","","")89 "","","","")
89 count = Playlists.getPlaylistCount(name) // get the new count90 count = Playlists.getPlaylistCount(name) // get the new count
90 playlistModel.model.set(index, {"count": count}) // update number ot tracks in playlist91 playlistModel.model.set(index, {"count": count}) // update number ot tracks in playlist
9192
=== modified file 'Player.qml'
--- Player.qml 2014-04-16 11:58:54 +0000
+++ Player.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -33,7 +34,6 @@
3334
34 property string currentMetaAlbum: ""35 property string currentMetaAlbum: ""
35 property string currentMetaArtist: ""36 property string currentMetaArtist: ""
36 property string currentMetaCover: ""
37 property string currentMetaFile: ""37 property string currentMetaFile: ""
38 property string currentMetaTitle: ""38 property string currentMetaTitle: ""
39 property int currentIndex: -139 property int currentIndex: -1
@@ -61,9 +61,9 @@
61 onCountChanged: {61 onCountChanged: {
62 if (trackQueue.model.count === 1) {62 if (trackQueue.model.count === 1) {
63 player.currentIndex = 0;63 player.currentIndex = 0;
64 player.source = Qt.resolvedUrl(trackQueue.model.get(0).file)64 player.source = Qt.resolvedUrl(trackQueue.model.get(0).filename)
65 } else if (trackQueue.model.count === 0) {65 } else if (trackQueue.model.count === 0) {
66 player.currentMetaCover = ""66 currentMetaFile = ""
67 }67 }
68 }68 }
69 }69 }
@@ -117,11 +117,11 @@
117 }117 }
118118
119 if (startPlaying) { // only start the track if told119 if (startPlaying) { // only start the track if told
120 playSong(trackQueue.model.get(newIndex).file, newIndex)120 playSong(trackQueue.model.get(newIndex).filename, newIndex)
121 }121 }
122 else {122 else {
123 currentIndex = newIndex123 currentIndex = newIndex
124 source = Qt.resolvedUrl(trackQueue.model.get(newIndex).file)124 source = Qt.resolvedUrl(trackQueue.model.get(newIndex).filename)
125 }125 }
126 }126 }
127127
@@ -183,9 +183,8 @@
183 else {183 else {
184 var obj = trackQueue.model.get(player.currentIndex);184 var obj = trackQueue.model.get(player.currentIndex);
185 currentMetaAlbum = obj.album;185 currentMetaAlbum = obj.album;
186 currentMetaArtist = obj.artist;186 currentMetaArtist = obj.author;
187 currentMetaCover = obj.cover;187 currentMetaFile = obj.filename;
188 currentMetaFile = obj.file;
189 currentMetaTitle = obj.title;188 currentMetaTitle = obj.title;
190 }189 }
191190
192191
=== modified file 'Style.qml'
--- Style.qml 2014-04-25 17:05:03 +0000
+++ Style.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
89
=== modified file 'click/apparmor.json'
--- click/apparmor.json 2014-05-30 14:23:43 +0000
+++ click/apparmor.json 2014-06-19 01:49:30 +0000
@@ -1,5 +1,5 @@
1{1{
2 "policy_version": 1.1,2 "policy_version": 1.2,
3 "template": "unconfined",3 "template": "unconfined",
4 "policy_groups": []4 "policy_groups": []
5}5}
66
=== modified file 'click/manifest.json.in'
--- click/manifest.json.in 2014-06-06 11:35:53 +0000
+++ click/manifest.json.in 2014-06-19 01:49:30 +0000
@@ -1,7 +1,6 @@
1{1{
2 "description": "A music application for Ubuntu",2 "description": "A music application for ubuntu",
3 "framework": "ubuntu-sdk-14.04-dev1",3 "framework": "ubuntu-sdk-14.10-qml-dev1",
4 "architecture": "armhf",
5 "hooks": {4 "hooks": {
6 "@APP_NAME@": {5 "@APP_NAME@": {
7 "apparmor": "apparmor.json",6 "apparmor": "apparmor.json",
87
=== modified file 'common/AlbumsSheet.qml'
--- common/AlbumsSheet.qml 2014-05-19 23:13:39 +0000
+++ common/AlbumsSheet.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,8 @@
21import Ubuntu.Components.ListItems 0.122import Ubuntu.Components.ListItems 0.1
22import Ubuntu.Components.Popups 0.123import Ubuntu.Components.Popups 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem24import Ubuntu.Components.ListItems 0.1 as ListItem
25import Ubuntu.MediaScanner 0.1
26import Ubuntu.Thumbnailer 0.1
24import QtQuick.LocalStorage 2.027import QtQuick.LocalStorage 2.0
25import "../meta-database.js" as Library28import "../meta-database.js" as Library
2629
@@ -58,7 +61,12 @@
58 width: parent.width61 width: parent.width
59 anchors.top: parent.top62 anchors.top: parent.top
60 anchors.bottom: parent.bottom63 anchors.bottom: parent.bottom
61 model: artistAlbumsModel.model64 model: AlbumsModel {
65 id: artistsModel
66 albumArtist: sheetItem.artist
67 store: musicStore
68 }
69
62 delegate: albumTracksDelegate70 delegate: albumTracksDelegate
63 header: artistHeaderDelegate71 header: artistHeaderDelegate
6472
@@ -132,6 +140,15 @@
132 fontSize: "large"140 fontSize: "large"
133 }141 }
134142
143 SongsModel {
144 id: songArtistModel
145 albumArtist: sheetItem.artist
146 // HACK: Temporarily setting limit to 500 to ensure model
147 // is populated. See lp:1326753
148 limit: 500
149 store: musicStore
150 }
151
135 // Play152 // Play
136 Rectangle {153 Rectangle {
137 id: playRow154 id: playRow
@@ -167,10 +184,7 @@
167 MouseArea {184 MouseArea {
168 anchors.fill: parent185 anchors.fill: parent
169 onClicked: {186 onClicked: {
170 albumTracksModel.filterArtistTracks(artist)187 trackClicked(songArtistModel, 0, true)
171 trackQueue.model.clear()
172 addQueueFromModel(albumTracksModel)
173 trackClicked(trackQueue, 0) // play track
174188
175 // TODO: add links to recent189 // TODO: add links to recent
176190
@@ -218,8 +232,7 @@
218 MouseArea {232 MouseArea {
219 anchors.fill: parent233 anchors.fill: parent
220 onClicked: {234 onClicked: {
221 albumTracksModel.filterArtistTracks(artist)235 addQueueFromModel(songArtistModel)
222 addQueueFromModel(albumTracksModel)
223 }236 }
224 }237 }
225 }238 }
@@ -236,6 +249,23 @@
236 width: parent.width249 width: parent.width
237 height: units.gu(20)250 height: units.gu(20)
238251
252 SongsModel {
253 id: songAlbumArtistModel
254 albumArtist: model.artist
255 album: model.title
256 // HACK: Temporarily setting limit to 500 to ensure model
257 // is populated. See lp:1326753
258 limit: 500
259 store: musicStore
260 }
261 Repeater {
262 id: songAlbumArtistModelRepeater
263 model: songAlbumArtistModel
264 delegate: Text { text: new Date(model.date).toLocaleString(Qt.locale(),'yyyy'); visible: false }
265 property string year: ""
266 onItemAdded: year = item.text
267 }
268
239 CoverRow {269 CoverRow {
240 id: albumImage270 id: albumImage
241 anchors {271 anchors {
@@ -246,7 +276,7 @@
246 }276 }
247 count: 1277 count: 1
248 size: parent.height278 size: parent.height
249 covers: [Library.getAlbumCover(model.album)]279 covers: [{author: artist, album: model.title}]
250 objectName: "artistsheet-albumcover"280 objectName: "artistsheet-albumcover"
251 spacing: units.gu(2)281 spacing: units.gu(2)
252282
@@ -259,13 +289,12 @@
259 focus = true289 focus = true
260 }290 }
261291
262 albumTracksModel.filterAlbumTracks(album)292 albumSheet.album = model.title;
293
263 albumSheet.line1 = artist294 albumSheet.line1 = artist
264 albumSheet.line2 = model.album295 albumSheet.line2 = model.title
265 albumSheet.isAlbum = true296 albumSheet.isAlbum = true
266 albumSheet.file = file297 albumSheet.covers = [{author: artist, album: model.title}]
267 albumSheet.year = year
268 albumSheet.covers = [Library.getAlbumCover(model.album) || Qt.resolvedUrl("../images/music-app-cover@30.png")]
269 PopupUtils.open(albumSheet.sheet)298 PopupUtils.open(albumSheet.sheet)
270299
271 // TODO: This closes the SDK defined sheet300 // TODO: This closes the SDK defined sheet
@@ -305,7 +334,7 @@
305 anchors.right: parent.right334 anchors.right: parent.right
306 anchors.rightMargin: units.gu(1.5)335 anchors.rightMargin: units.gu(1.5)
307 elide: Text.ElideRight336 elide: Text.ElideRight
308 text: album337 text: model.title
309 }338 }
310 Label {339 Label {
311 id: albumYear340 id: albumYear
@@ -320,7 +349,9 @@
320 anchors.right: parent.right349 anchors.right: parent.right
321 anchors.rightMargin: units.gu(1.5)350 anchors.rightMargin: units.gu(1.5)
322 elide: Text.ElideRight351 elide: Text.ElideRight
323 text: i18n.tr(model.year + " | %1 song", model.year + " | %1 songs", Library.getAlbumTracks(album).length).arg(Library.getAlbumTracks(album).length)352 text: i18n.tr(songAlbumArtistModelRepeater.year + " | %1 song",
353 songAlbumArtistModelRepeater.year + " | %1 songs",
354 songAlbumArtistModelRepeater.count).arg(songAlbumArtistModelRepeater.count)
324 }355 }
325356
326 // Play357 // Play
@@ -355,13 +386,10 @@
355 MouseArea {386 MouseArea {
356 anchors.fill: parent387 anchors.fill: parent
357 onClicked: {388 onClicked: {
358 albumTracksModel.filterAlbumTracks(album)389 Library.addRecent(model.title, artist, "", model.title, "album")
359 Library.addRecent(album, artist, Library.getAlbumCover(album), album, "album")
360 mainView.hasRecent = true390 mainView.hasRecent = true
361 recentModel.filterRecent()391 recentModel.filterRecent()
362 trackQueue.model.clear()392 trackClicked(songAlbumArtistModel, 0, true)
363 addQueueFromModel(albumTracksModel)
364 trackClicked(trackQueue, 0) // play track
365393
366 // TODO: This closes the SDK defined sheet394 // TODO: This closes the SDK defined sheet
367 // component. It should be able to close395 // component. It should be able to close
@@ -403,8 +431,7 @@
403 MouseArea {431 MouseArea {
404 anchors.fill: parent432 anchors.fill: parent
405 onClicked: {433 onClicked: {
406 albumTracksModel.filterAlbumTracks(album)434 addQueueFromModel(songAlbumArtistModel)
407 addQueueFromModel(albumTracksModel)
408 }435 }
409 }436 }
410 }437 }
411438
=== modified file 'common/BlurredBackground.qml'
--- common/BlurredBackground.qml 2014-04-12 01:06:28 +0000
+++ common/BlurredBackground.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -23,15 +24,14 @@
23// Blurred background24// Blurred background
24Rectangle {25Rectangle {
25 anchors.fill: parent26 anchors.fill: parent
26 property string cover: player.currentMetaCover !== "" ?27 property string art: player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : "image://albumart/artist=" + player.currentMetaArtist + "&album=" + player.currentMetaAlbum
27 player.currentMetaCover :28
28 "../images/music-app-cover@30.png"
29 // the album art29 // the album art
30 Image {30 Image {
31 id: backgroundImage31 id: backgroundImage
32 anchors.horizontalCenter: parent.horizontalCenter32 anchors.horizontalCenter: parent.horizontalCenter
33 anchors.verticalCenter: parent.verticalCenter33 anchors.verticalCenter: parent.verticalCenter
34 source: cover // this has to be fixed for the default cover art to work - cant find in this dir34 source: art // this has to be fixed for the default cover art to work - cant find in this dir
35 height: Math.max(parent.height, parent.width)35 height: Math.max(parent.height, parent.width)
36 width: Math.max(parent.height, parent.width)36 width: Math.max(parent.height, parent.width)
37 visible: false37 visible: false
@@ -54,10 +54,10 @@
54 color: "white"54 color: "white"
55 opacity: 0.755 opacity: 0.7
56 }56 }
57 onCoverChanged: {57 onArtChanged: {
58 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,58 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,
59 // there should be a better way of getting the blur to repaint59 // there should be a better way of getting the blur to repaint
60 backgroundImage.source = cover60 backgroundImage.source = art
61 backgroundBlur.source = null61 backgroundBlur.source = null
62 backgroundBlur.source = backgroundImage62 backgroundBlur.source = backgroundImage
63 }63 }
6464
=== modified file 'common/CoverRow.qml'
--- common/CoverRow.qml 2014-04-11 18:18:12 +0000
+++ common/CoverRow.qml 2014-06-19 01:49:30 +0000
@@ -1,5 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Nekhelesh Ramananthan <krnekhelesh@gmail.com>2 * Copyright (C) 2013, 2014
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
3 *6 *
4 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -52,8 +55,9 @@
52 delegate: Image {55 delegate: Image {
53 width: coverRow.size56 width: coverRow.size
54 height: width57 height: width
55 source: coverRow.count === 0 || coverRow.covers[index] === ""58 source: coverRow.count !== 0 && coverRow.covers[index] !== "" && coverRow.covers[index] !== undefined
56 ? Qt.resolvedUrl("../images/music-app-cover@30.png") : coverRow.covers[index]59 ? "image://albumart/artist=" + coverRow.covers[index].author + "&album=" + coverRow.covers[index].album
60 : Qt.resolvedUrl("../images/music-app-cover@30.png")
57 onStatusChanged: {61 onStatusChanged: {
58 if (status === Image.Error) {62 if (status === Image.Error) {
59 source = Qt.resolvedUrl("../images/music-app-cover@30.png")63 source = Qt.resolvedUrl("../images/music-app-cover@30.png")
6064
=== modified file 'common/Expander.qml'
--- common/Expander.qml 2014-04-30 01:59:41 +0000
+++ common/Expander.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
89
=== modified file 'common/SongsSheet.qml'
--- common/SongsSheet.qml 2014-04-26 17:13:02 +0000
+++ common/SongsSheet.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,8 @@
21import Ubuntu.Components.ListItems 0.122import Ubuntu.Components.ListItems 0.1
22import Ubuntu.Components.Popups 0.123import Ubuntu.Components.Popups 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem24import Ubuntu.Components.ListItems 0.1 as ListItem
25import Ubuntu.MediaScanner 0.1
26import Ubuntu.Thumbnailer 0.1
24import QtQuick.LocalStorage 2.027import QtQuick.LocalStorage 2.0
25import "../meta-database.js" as Library28import "../meta-database.js" as Library
26import "ExpanderItems"29import "ExpanderItems"
@@ -38,6 +41,17 @@
38 property bool isAlbum: false41 property bool isAlbum: false
39 property alias sheet: sheetComponent42 property alias sheet: sheetComponent
4043
44 property alias album: songsModel.album
45 property alias genre: songsModel.genre
46
47 SongsModel {
48 id: songsModel
49 // HACK: Temporarily setting limit to 500 to ensure model
50 // is populated. See lp:1326753
51 limit: 500
52 store: musicStore
53 }
54
41 Component {55 Component {
42 id: sheetComponent56 id: sheetComponent
43 DefaultSheet {57 DefaultSheet {
@@ -61,7 +75,8 @@
61 width: parent.width75 width: parent.width
62 anchors.top: parent.top76 anchors.top: parent.top
63 anchors.bottom: parent.bottom77 anchors.bottom: parent.bottom
64 model: albumTracksModel.model78 model: isAlbum ? songsModel : albumTracksModel.model
79
65 delegate: albumTracksDelegate80 delegate: albumTracksDelegate
66 header: ListItem.Standard {81 header: ListItem.Standard {
67 id: albumInfo82 id: albumInfo
@@ -125,8 +140,8 @@
125 anchors.right: parent.right140 anchors.right: parent.right
126 anchors.rightMargin: units.gu(1.5)141 anchors.rightMargin: units.gu(1.5)
127 elide: Text.ElideRight142 elide: Text.ElideRight
128 text: isAlbum ? i18n.tr(year + " | %1 song", year + " | %1 songs", albumTracksModel.model.count).arg(albumTracksModel.model.count)143 text: isAlbum ? i18n.tr(year + " | %1 song", year + " | %1 songs", albumtrackslist.count).arg(albumtrackslist.count)
129 : i18n.tr("%1 song", "%1 songs", albumTracksModel.model.count).arg(albumTracksModel.model.count)144 : i18n.tr("%1 song", "%1 songs", albumtrackslist.count).arg(albumtrackslist.count)
130145
131 }146 }
132147
@@ -162,13 +177,14 @@
162 MouseArea {177 MouseArea {
163 anchors.fill: parent178 anchors.fill: parent
164 onClicked: {179 onClicked: {
165 trackClicked(albumTracksModel, 0) // play track180 trackClicked(albumtrackslist.model, 0) // play track
166 if (isAlbum) {181
167 Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.cover, sheetItem.line2, "album")182 if (isAlbum && sheetItem.line1 != "Genre") {
183 Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.covers[0], sheetItem.line2, "album")
168 mainView.hasRecent = true184 mainView.hasRecent = true
169 recentModel.filterRecent()185 recentModel.filterRecent()
170 } else if (sheetItem.line1 == "Playlist") {186 } else if (sheetItem.line1 == "Playlist") {
171 Library.addRecent(sheetItem.line2, "Playlist", sheetItem.cover, sheetItem.line2, "playlist")187 Library.addRecent(sheetItem.line2, "Playlist", sheetItem.covers[0], sheetItem.line2, "playlist")
172 mainView.hasRecent = true188 mainView.hasRecent = true
173 recentModel.filterRecent()189 recentModel.filterRecent()
174 }190 }
@@ -213,7 +229,7 @@
213 MouseArea {229 MouseArea {
214 anchors.fill: parent230 anchors.fill: parent
215 onClicked: {231 onClicked: {
216 addQueueFromModel(albumTracksModel)232 addQueueFromModel(albumtrackslist.model)
217 }233 }
218 }234 }
219 }235 }
@@ -237,13 +253,13 @@
237 if (focus == false) {253 if (focus == false) {
238 focus = true254 focus = true
239 }255 }
240 trackClicked(albumTracksModel, index) // play track256 trackClicked(albumtrackslist.model, index) // play track
241 if (isAlbum) {257 if (isAlbum && sheetItem.line1 != "Genre") {
242 Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.cover, sheetItem.line2, "album")258 Library.addRecent(sheetItem.line2, sheetItem.line1, sheetItem.covers[0], sheetItem.line2, "album")
243 mainView.hasRecent = true259 mainView.hasRecent = true
244 recentModel.filterRecent()260 recentModel.filterRecent()
245 } else if (sheetItem.line1 == "Playlist") {261 } else if (sheetItem.line1 == "Playlist") {
246 Library.addRecent(sheetItem.line2, "Playlist", sheetItem.cover, sheetItem.line2, "playlist")262 Library.addRecent(sheetItem.line2, "Playlist", sheetItem.covers[0], sheetItem.line2, "playlist")
247 mainView.hasRecent = true263 mainView.hasRecent = true
248 recentModel.filterRecent()264 recentModel.filterRecent()
249 }265 }
@@ -275,7 +291,7 @@
275 height: styleMusic.common.albumSize291 height: styleMusic.common.albumSize
276 visible: !isAlbum292 visible: !isAlbum
277 image: Image {293 image: Image {
278 source: model.cover !== "" ? model.cover : Qt.resolvedUrl("../images/music-app-cover@30.png")294 source: "image://albumart/artist=" + model.author + "&album=" + model.album
279 onStatusChanged: {295 onStatusChanged: {
280 if (status === Image.Error) {296 if (status === Image.Error) {
281 source = Qt.resolvedUrl("../images/music-app-cover@30.png")297 source = Qt.resolvedUrl("../images/music-app-cover@30.png")
@@ -300,7 +316,7 @@
300 rightMargin: units.gu(1.5)316 rightMargin: units.gu(1.5)
301 }317 }
302 elide: Text.ElideRight318 elide: Text.ElideRight
303 text: model.artist319 text: model.author
304 }320 }
305321
306 Label {322 Label {
@@ -348,7 +364,7 @@
348 fill: parent364 fill: parent
349 }365 }
350 listItem: track366 listItem: track
351 model: albumTracksModel.model.get(index)367 model: isAlbum ? albumtrackslist.model.get(index, albumTracksModel.model.RoleModelData) : albumtrackslist.model.get(index)
352 row: Row {368 row: Row {
353 AddToPlaylist {369 AddToPlaylist {
354370
@@ -360,10 +376,10 @@
360 }376 }
361377
362 Component.onCompleted: {378 Component.onCompleted: {
363 if (index === 0)379 if (model.date !== undefined && sheetItem.year === "")
364 {380 {
365 sheetItem.file = model.file;381 sheetItem.file = model.filename;
366 sheetItem.year = model.year;382 sheetItem.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
367 }383 }
368 }384 }
369 }385 }
370386
=== modified file 'debian/control'
--- debian/control 2014-06-06 11:35:53 +0000
+++ debian/control 2014-06-19 01:49:30 +0000
@@ -13,18 +13,21 @@
1313
14Package: music-app14Package: music-app
15Architecture: all15Architecture: all
16Depends: grilo-plugins-0.2-mediascanner,16Depends: mediascanner2.0,
17 mediascanner,17 gstreamer0.10-fluendo-mp3,
18 gstreamer1.0-fluendo-mp3,
18 qmlscene,19 qmlscene,
19 qtdeclarative5-localstorage-plugin,20 qtdeclarative5-localstorage-plugin,
20 qtdeclarative5-particles-plugin,21 qtdeclarative5-particles-plugin,
21 qtdeclarative5-qtgrilo0.1,
22 qtdeclarative5-qtmultimedia-plugin,22 qtdeclarative5-qtmultimedia-plugin,
23 qtdeclarative5-qtquick2-plugin,23 qtdeclarative5-qtquick2-plugin,
24 qtdeclarative5-ubuntu-mediascanner0.1,
25 qtdeclarative5-ubuntu-thumbnailer0.1,
24 qtdeclarative5-ubuntu-ui-toolkit-plugin,26 qtdeclarative5-ubuntu-ui-toolkit-plugin,
25 qtdeclarative5-usermetrics0.1,27 qtdeclarative5-usermetrics0.1,
26 qtdeclarative5-window-plugin,28 qtdeclarative5-window-plugin,
27 qtdeclarative5-xmllistmodel-plugin,29 qtdeclarative5-xmllistmodel-plugin,
30 thumbnailer-service,
28 suru-icon-theme | ubuntu-mobile-icons,31 suru-icon-theme | ubuntu-mobile-icons,
29 ${misc:Depends},32 ${misc:Depends},
30Description: Music player for Ubuntu Touch33Description: Music player for Ubuntu Touch
@@ -32,9 +35,7 @@
3235
33Package: music-app-autopilot36Package: music-app-autopilot
34Architecture: all37Architecture: all
35Depends: gstreamer0.10-fluendo-mp3,38Depends: libautopilot-qt (>= 1.4),
36 gstreamer1.0-fluendo-mp3,
37 libautopilot-qt (>= 1.4),
38 libqt5test5,39 libqt5test5,
39 music-app (= ${source:Version}),40 music-app (= ${source:Version}),
40 python-mock,41 python-mock,
4142
=== modified file 'meta-database.js'
--- meta-database.js 2014-05-02 04:03:50 +0000
+++ meta-database.js 2014-06-19 01:49:30 +0000
@@ -1,6 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Victor Thompson <victor.thompson@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
4 *6 *
5 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -15,9 +17,6 @@
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */18 */
1719
18var buffer = []; // Buffer of metadata to write to the db
19var maxBufferLength = 8000; // Maximum size of buffer before auto write to db
20
21// First, let's create a short helper function to get the database connection20// First, let's create a short helper function to get the database connection
22function getDatabase() {21function getDatabase() {
23 return LocalStorage.openDatabaseSync("music-app-metadata", "1.0", "StorageDatabase", 1000000);22 return LocalStorage.openDatabaseSync("music-app-metadata", "1.0", "StorageDatabase", 1000000);
@@ -30,19 +29,8 @@
30 function(tx) {29 function(tx) {
31 // Create the table if it doesn't already exist30 // Create the table if it doesn't already exist
32 // If the table exists, this is skipped31 // If the table exists, this is skipped
33 //tx.executeSql('DROP TABLE metadata');32 tx.executeSql('DROP TABLE IF EXISTS metadata'); // TODO: drop recent as well to reset data
34 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)');33
35 createRecent();
36 });
37}
38function reset() {
39 var db = getDatabase();
40 db.transaction(
41 function(tx) {
42 // Create the table if it doesn't already exist
43 // If the table exists, this is skipped
44 tx.executeSql('DROP TABLE IF EXISTS metadata');
45 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)');
46 createRecent();34 createRecent();
47 });35 });
48}36}
@@ -67,348 +55,6 @@
67 });55 });
68}56}
6957
70// This function is used to flush the buffer of metadata to the db
71function writeDb()
72{
73 var db = getDatabase();
74 var res = "";
75 var i;
76
77 console.debug("Writing DB");
78 console.debug(buffer.length);
79
80 // Keep within one transaction for performance win
81 db.transaction(function(tx) {
82 // Loop through all the metadata in the buffer
83 for (i=0; i < buffer.length; i++)
84 {
85 var res = tx.executeSql('INSERT OR REPLACE INTO metadata VALUES (?,?,?,?,?,?,?,?,?);', buffer[i]);
86
87 if (res.rowsAffected <= 0)
88 {
89 // Nothing was added error occured?
90 console.debug("Error occured writing to db for ", buffer[i]);
91 }
92 }
93 });
94
95 buffer = []; // Clear buffer
96}
97
98// This function is used to write meta data into the database
99function setMetadata(record) {
100 buffer.push([record.file,record.title,record.artist,record.album,record.cover,record.year,record.number,record.length,record.genre]); // Add metadata to buffer
101
102 if (buffer.length >= maxBufferLength)
103 {
104 console.debug("Buffer full, flushing buffer to disk");
105 writeDb();
106 }
107}
108
109
110function removeFiles(files)
111{
112 var db = getDatabase();
113
114 db.transaction(function(tx) {
115 for (var i=0; i < files.length; i++)
116 {
117 for (var k in files[i])
118 {
119 tx.executeSql('DELETE FROM metadata WHERE file=?;', files[i]["file"]);
120 }
121 }
122 });
123}
124
125// This function is used to retrieve meta data from the database
126function getMetadata(file) {
127 var db = getDatabase();
128 var res="";
129
130 try {
131 db.transaction(function(tx) {
132 var rs = tx.executeSql('SELECT * FROM metadata WHERE file=?;', [file]); // tries to get the title of track
133
134 if (rs.rows.length > 0) {
135 res = rs.rows.item(0);
136 } else {
137 res = "Unknown";
138 }
139 })
140 } catch(e) {
141 return "";
142 }
143
144 // The function returns “Unknown” if the setting was not found in the database
145 // For more advanced projects, this should probably be handled through error codes
146 return res
147}
148
149// This function is used to retrieve meta data from the database
150function hasCover(file) {
151 var db = getDatabase();
152 var res = false;
153
154 try {
155 db.transaction(function(tx) {
156 var rs = tx.executeSql('SELECT cover FROM metadata WHERE file = ?;', [file]); // tries to get the cover art of track
157
158 if (rs.rows.length > 0) {
159 res = rs.rows.item(0).cover !== ""
160 }
161 })
162 } catch(e) {
163 return false;
164 }
165
166 // The function returns false if cover art was not found in the database
167 return res
168}
169
170
171function printValues() {
172 var db = getDatabase();
173 db.transaction( function(tx) {
174 var rs = tx.executeSql("SELECT * FROM metadata");
175 for(var i = 0; i < rs.rows.length; i++) {
176 var dbItem = rs.rows.item(i);
177 console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover);
178 }
179 });
180}
181
182
183function getAll() {
184 var res = [];
185 var db = getDatabase();
186 db.transaction( function(tx) {
187 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");
188 for(var i = 0; i < rs.rows.length; i++) {
189 var dbItem = rs.rows.item(i);
190 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
191 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});
192 }
193 });
194 return res;
195}
196
197function getAllFileOrder() {
198 var res = [];
199 var db = getDatabase();
200 db.transaction( function(tx) {
201 var rs = tx.executeSql("SELECT * FROM metadata ORDER BY file COLLATE NOCASE ASC");
202 for(var i = 0; i < rs.rows.length; i++) {
203 var dbItem = rs.rows.item(i);
204 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
205 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});
206 }
207 });
208 return res;
209}
210
211function getArtists() {
212 var res = [];
213 var db = getDatabase();
214 db.transaction( function(tx) {
215 var rs = tx.executeSql("SELECT * FROM metadata GROUP BY artist ORDER BY artist COLLATE NOCASE ASC");
216 for(var i = 0; i < rs.rows.length; i++) {
217 var dbItem = rs.rows.item(i);
218 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
219 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});
220 }
221 });
222 return res;
223}
224
225function getArtistTracks(artist) {
226 var res = [];
227 var db = getDatabase();
228 db.transaction( function(tx) {
229 var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? ORDER BY artist COLLATE NOCASE ASC, year ASC, CAST(number AS int) ASC", [artist]);
230 for(var i = 0; i < rs.rows.length; i++) {
231 var dbItem = rs.rows.item(i);
232 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
233 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});
234 }
235 });
236 return res;
237}
238
239function getArtistAlbums(artist) {
240 var res = [];
241 var db = getDatabase();
242 db.transaction( function(tx) {
243 var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? GROUP BY album ORDER BY year ASC, CAST(number AS int) ASC", [artist]);
244 for(var i = 0; i < rs.rows.length; i++) {
245 var dbItem = rs.rows.item(i);
246 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
247 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});
248 }
249 });
250 return res;
251}
252
253function getArtistCovers(artist) {
254 var res = [];
255 var db = getDatabase();
256 try {
257 db.transaction( function(tx) {
258 var rs = tx.executeSql("SELECT cover FROM metadata WHERE artist=? AND cover <> '' ORDER BY album COLLATE NOCASE ASC", [artist]);
259 for(var i = 0; i < rs.rows.length; i++) {
260 var dbItem = rs.rows.item(i);
261 //console.log("Cover:"+ dbItem.cover+" Size:"+res.length);
262 if (res.indexOf(dbItem.cover) == -1) res.push(dbItem.cover);
263 }
264 });
265 } catch(e) {
266 return [];
267 }
268
269 return res;
270}
271
272function getAlbumCover(album) {
273 var res = "";
274 var db = getDatabase();
275 try {
276 db.transaction( function(tx) {
277 var rs = tx.executeSql("SELECT cover FROM metadata WHERE album=? ORDER BY cover DESC", [album]);
278 var dbItem = rs.rows.item(0);
279 //console.log("Cover:"+ dbItem.cover+" Size:"+res.length);
280 if (rs.rows.length > 0) res = rs.rows.item(0).cover;
281 });
282 } catch(e) {
283 return "";
284 }
285
286 return res;
287}
288
289function getArtistAlbumCount(artist) {
290 var res = 0;
291 var db = getDatabase();
292 db.transaction( function(tx) {
293 var rs = tx.executeSql("SELECT count(DISTINCT album) AS value FROM metadata WHERE artist=?", [artist]);
294 if (rs.rows.item(0).value > 0) {
295 res = rs.rows.item(0).value;
296 } else {
297 res = 0;
298 }
299 });
300 return res;
301}
302
303function getAlbums() {
304 var res = [];
305 var db = getDatabase();
306 try {
307 db.transaction( function(tx) {
308 var rs = tx.executeSql("SELECT * FROM metadata GROUP BY album ORDER BY album COLLATE NOCASE ASC");
309 for(var i = 0; i < rs.rows.length; i++) {
310 var dbItem = rs.rows.item(i);
311 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
312 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});
313 }
314 });
315 } catch(e) {
316 return [];
317 }
318
319 return res;
320}
321
322function getAlbumTracks(album) {
323 var res = [];
324 var db = getDatabase();
325 //console.log("Album: " + album);
326 db.transaction( function(tx) {
327 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]);
328 for(var i = 0; i < rs.rows.length; i++) {
329 var dbItem = rs.rows.item(i);
330 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
331 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});
332 }
333 });
334 return res;
335}
336
337function getArtistAlbumTracks(artist, album) {
338 var res = [];
339 var db = getDatabase();
340 //console.log("Album: " + album);
341 db.transaction( function(tx) {
342 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]);
343 for(var i = 0; i < rs.rows.length; i++) {
344 var dbItem = rs.rows.item(i);
345 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
346 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});
347 }
348 });
349 return res;
350}
351
352function getGenres() {
353 var res = [];
354 var db = getDatabase();
355 db.transaction( function(tx) {
356 var rs = tx.executeSql("SELECT *, count(genre) AS total FROM metadata GROUP BY genre ORDER BY genre COLLATE NOCASE ASC");
357 for(var i = 0; i < rs.rows.length; i++) {
358 var dbItem = rs.rows.item(i);
359 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
360 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});
361 }
362 });
363 return res;
364}
365
366function getGenreTracks(genre) {
367 var res = [];
368 var db = getDatabase();
369 //console.log("Genre: " + genre);
370 db.transaction( function(tx) {
371 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]);
372 for(var i = 0; i < rs.rows.length; i++) {
373 var dbItem = rs.rows.item(i);
374 //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
375 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});
376 }
377 });
378 return res;
379}
380
381function getGenreCovers(genre) {
382 var res = [];
383 var db = getDatabase();
384 try {
385 db.transaction( function(tx) {
386 var rs = tx.executeSql("SELECT cover FROM metadata WHERE genre=? AND cover <> '' ORDER BY artist COLLATE NOCASE ASC", [genre]);
387 for(var i = 0; i < rs.rows.length; i++) {
388 if (res.indexOf(rs.rows.item(i).cover) === -1) {
389 res.push(rs.rows.item(i).cover);
390 }
391 }
392 });
393 } catch(e) {
394 return [];
395 }
396
397 return res;
398}
399
400
401function size() {
402 var db = getDatabase();
403 var res="";
404
405 db.transaction( function(tx) {
406 var rs = tx.executeSql("SELECT count(*) FROM metadata");
407 res = rs.rows.item(0).value;
408 });
409 return res;
410}
411
412// This function is used to insert a recent item into the database58// This function is used to insert a recent item into the database
413function addRecent(title, title2, cover, key, type) {59function addRecent(title, title2, cover, key, type) {
414 var db = getDatabase();60 var db = getDatabase();
@@ -436,21 +82,20 @@
436 var rs = tx.executeSql("SELECT * FROM recent ORDER BY time DESC LIMIT 15");82 var rs = tx.executeSql("SELECT * FROM recent ORDER BY time DESC LIMIT 15");
437 for(var i = 0; i < rs.rows.length; i++) {83 for(var i = 0; i < rs.rows.length; i++) {
438 var dbItem = rs.rows.item(i);84 var dbItem = rs.rows.item(i);
439 console.log("Time:"+ dbItem.time + ", Key:"+dbItem.key + ", Title:"+dbItem.title + ", Title2:"+dbItem.title2 + ", Cover:"+dbItem.cover + ", Type:"+dbItem.type);85 console.log("Time:"+ dbItem.time + ", Key:"+dbItem.key + ", Title:"+dbItem.title + ", Title2:"+dbItem.title2 + ", Type:"+dbItem.type);
44086
441 if (dbItem.type === "album")87 if (dbItem.type === "album")
442 {88 {
443 res.push({time:dbItem.time,89 res.push({time:dbItem.time,
444 title:dbItem.title || i18n.tr("Unknown Album"),90 title:dbItem.title || i18n.tr("Unknown Album"),
445 title2:dbItem.title2 || i18n.tr("Unknown Artist"),91 title2:dbItem.title2 || i18n.tr("Unknown Artist"),
446 cover:dbItem.cover,
447 key:dbItem.key || i18n.tr("Unknown Album"),92 key:dbItem.key || i18n.tr("Unknown Album"),
448 type:dbItem.type93 type:dbItem.type
449 });94 });
450 }95 }
451 else96 else
452 {97 {
453 res.push({time:dbItem.time, title:dbItem.title, title2:dbItem.title2, cover:dbItem.cover, key:dbItem.key, type:dbItem.type});98 res.push({time:dbItem.time, title:dbItem.title, title2:dbItem.title2, key:dbItem.key, type:dbItem.type});
454 }99 }
455 }100 }
456 });101 });
@@ -474,20 +119,3 @@
474 );119 );
475 return res === 0;120 return res === 0;
476}121}
477
478// Search track LIKE
479function search(input) {
480 console.debug("Got a new search: "+input)
481 input = "%" + input + "%" // workaround
482 var res = [];
483 var db = getDatabase();
484 db.transaction( function(tx) {
485 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?
486 for(var i = 0; i < rs.rows.length; i++) {
487 var dbItem = rs.rows.item(i);
488 console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
489 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});
490 }
491 });
492 return res;
493}
494122
=== modified file 'music-app.qml'
--- music-app.qml 2014-05-08 22:58:35 +0000
+++ music-app.qml 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * it under the terms of the GNU General Public License as published by
@@ -21,8 +22,8 @@
21import Ubuntu.Components.ListItems 0.122import Ubuntu.Components.ListItems 0.1
22import Ubuntu.Components.Popups 0.123import Ubuntu.Components.Popups 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem24import Ubuntu.Components.ListItems 0.1 as ListItem
25import Ubuntu.MediaScanner 0.1
24import Ubuntu.Unity.Action 1.0 as UnityActions26import Ubuntu.Unity.Action 1.0 as UnityActions
25import org.nemomobile.grilo 0.1
26import QtMultimedia 5.027import QtMultimedia 5.0
27import QtQuick.LocalStorage 2.028import QtQuick.LocalStorage 2.0
28import QtQuick.XmlListModel 2.029import QtQuick.XmlListModel 2.0
@@ -199,9 +200,6 @@
199 actions: [searchAction, nextAction, playsAction, prevAction, stopAction, backAction]200 actions: [searchAction, nextAction, playsAction, prevAction, stopAction, backAction]
200201
201 // signal to open new URIs202 // signal to open new URIs
202 // TODO currently this only allows playing file:// URIs of known files
203 // (already in the database), not e.g. http:// URIs or files in directories
204 // not picked up by Grilo
205 Connections {203 Connections {
206 id: uriHandler204 id: uriHandler
207 target: UriHandler205 target: UriHandler
@@ -214,33 +212,29 @@
214 return;212 return;
215 }213 }
216214
217 // Get tracks215 // Filter by artist and album
218 var tracks = Library.getArtistAlbumTracks(decodeURIComponent(split[0]), decodeURIComponent(split[1]));216 songsAlbumArtistModel.artist = decodeURIComponent(split[0]);
217 songsAlbumArtistModel.album = decodeURIComponent(split[1]);
219218
220 if (tracks.length === 0) {219 // Play album it tracks exist
220 if (songsAlbumArtistModel.rowCount > 0) {
221 trackClicked(songsAlbumArtistModel, 0, true);
222 }
223 else {
221 console.debug("Unknown artist-album " + uri + ", skipping")224 console.debug("Unknown artist-album " + uri + ", skipping")
222 return;225 return;
223 }226 }
224
225 // Enqueue
226 for (var track in tracks) {
227 trackQueue.append(tracks[track]);
228 }
229
230 // Play first track
231 trackClicked(trackQueue, 0, true);
232 }227 }
233228
234 function processFile(uri, play) {229 function processFile(uri, play) {
235 uri = decodeURIComponent(uri);230 uri = decodeURIComponent(uri);
236231
237 // search for path in library
238 var library = Library.getAll();
239 var track = false;232 var track = false;
240233
241 for (var item in library) {234 // Search for track in songs model
242 if (decodeURIComponent(library[item].file) === uri) {235 for (var i=0; i < allSongsModel.rowCount; i++) {
243 track = library[item];236 if (decodeURIComponent(allSongsModel.get(i, allSongsModel.RoleModelData).filename) === uri) {
237 track = allSongsModel.get(i, allSongsModel.RoleModelData);
244 break;238 break;
245 }239 }
246 }240 }
@@ -255,7 +249,7 @@
255249
256 // play first URI250 // play first URI
257 if (play) {251 if (play) {
258 trackClicked(trackQueue, 0, true)252 trackQueueClick(0);
259 }253 }
260 }254 }
261255
@@ -340,8 +334,7 @@
340 Settings.setSetting("repeat", "0") // default state of repeat334 Settings.setSetting("repeat", "0") // default state of repeat
341 //Settings.setSetting("scrobble", "0") // default state of scrobble335 //Settings.setSetting("scrobble", "0") // default state of scrobble
342 }336 }
343 Library.reset()337 Library.initialize();
344 //Library.initialize();
345338
346 // initialize playlists339 // initialize playlists
347 Playlists.initializePlaylists()340 Playlists.initializePlaylists()
@@ -355,13 +348,27 @@
355 // push the page to view348 // push the page to view
356 pageStack.push(tabs)349 pageStack.push(tabs)
357350
351 loadedUI = true;
352
358 // TODO: Switch tabs back and forth to get the background color in the353 // TODO: Switch tabs back and forth to get the background color in the
359 // header to work properly.354 // header to work properly.
360 tabs.selectedTabIndex = 1355 tabs.selectedTabIndex = 1
361 tabs.selectedTabIndex = 0356 tabs.selectedTabIndex = 0
357
358 // Run post load
359 tabs.ensurePopulated(tabs.selectedTab);
360
361 if (args.values.url) {
362 uriHandler.process(args.values.url, true);
363 }
364
365 // Show toolbar and start timer if there is music
366 if (!emptyPage.noMusic) {
367 musicToolbar.showToolbar();
368 musicToolbar.startAutohideTimer();
369 }
362 }370 }
363371
364
365 // VARIABLES372 // VARIABLES
366 property string musicName: i18n.tr("Music")373 property string musicName: i18n.tr("Music")
367 property string appVersion: '1.2'374 property string appVersion: '1.2'
@@ -371,16 +378,13 @@
371 property string lastfmpassword378 property string lastfmpassword
372 property string timestamp // used to scrobble379 property string timestamp // used to scrobble
373 property var chosenElement: null380 property var chosenElement: null
374 property LibraryListModel currentModel: null // Current model being used
375 property var currentQuery: null
376 property var currentParam: null
377 property bool queueChanged: false
378 property bool toolbarShown: musicToolbar.shown381 property bool toolbarShown: musicToolbar.shown
379 signal collapseExpand();382 signal collapseExpand();
380 signal collapseSwipeDelete(int index);383 signal collapseSwipeDelete(int index);
381 signal onToolbarShownChanged(bool shown, var currentPage, var currentTab)384 signal onToolbarShownChanged(bool shown, var currentPage, var currentTab)
382385
383 property bool wideAspect: width >= units.gu(70)386 property bool wideAspect: width >= units.gu(70)
387 property bool loadedUI: false // property to detect if the UI has finished
384388
385 // FUNCTIONS389 // FUNCTIONS
386390
@@ -393,28 +397,20 @@
393 }397 }
394 }398 }
395399
396 // Add items from a stored query in libraryModel into the queue400 function addQueueFromModel(model)
397 function addQueueFromModel(libraryModel)
398 {401 {
399 var items;402 // TODO: remove once playlists uses U1DB
400403 if (model.hasOwnProperty("linkLibraryListModel")) {
401 if (libraryModel.query === null)404 model = model.linkLibraryListModel;
402 {405 }
403 return406
404 }407 for (var i=0; i < model.rowCount; i++) {
405408 var item = model.get(i, model.RoleModelData);
406 if (libraryModel.param === null)409 if (item.art !== undefined && (item.art === "" || item.art === null)) {
407 {410 item.art = "image://albumart/artist=" + item.author + "&album=" + item.album
408 items = libraryModel.query()411 }
409 }412
410 else413 trackQueue.model.append(makeDict(item));
411 {
412 items = libraryModel.query(libraryModel.param)
413 }
414
415 for (var key in items)
416 {
417 trackQueue.append(items[key])
418 }414 }
419 }415 }
420416
@@ -430,64 +426,39 @@
430 return minutes + ":" + (seconds<10 ? "0"+seconds : seconds);426 return minutes + ":" + (seconds<10 ? "0"+seconds : seconds);
431 }427 }
432428
433 function trackClicked(libraryModel, index, play)429 // Make dictionary from model item
434 {430 function makeDict(model) {
431 return {
432 album: model.album,
433 author: model.author,
434 filename: model.filename,
435 title: model.title
436 };
437 }
438
439 function trackClicked(model, index, play) {
440 // TODO: remove once playlists uses U1DB
441 if (model.hasOwnProperty("linkLibraryListModel")) {
442 model = model.linkLibraryListModel;
443 }
444
445 var file = Qt.resolvedUrl(model.get(index, model.RoleModelData).filename);
446
435 play = play === undefined ? true : play // default play to true447 play = play === undefined ? true : play // default play to true
436448
437 if (index > libraryModel.model.count - 1 || index < 0) {449 // If same track and on now playing page then toggle
438 customdebug("Incorrect index given to trackClicked.")450 if (musicToolbar.currentPage === nowPlaying &&
451 Qt.resolvedUrl(trackQueue.model.get(player.currentIndex).filename) === file) {
452 player.toggle()
439 return;453 return;
440 }454 }
441455
442 var file = Qt.resolvedUrl(libraryModel.model.get(index).file)456 trackQueue.model.clear(); // clear the old model
443457
444 // Clear the play queue and load the new tracks - if not trackQueue458 addQueueFromModel(model);
445 // Don't reload queue if model, query and parameters are the same459
446 // Same file different pages is treated as a new session460 if (play) {
447 if (libraryModel !== trackQueue &&461 player.playSong(file, index);
448 (currentModel !== libraryModel ||
449 currentQuery !== libraryModel.query ||
450 currentParam !== libraryModel.param ||
451 queueChanged === true))
452 {
453 trackQueue.model.clear()
454 addQueueFromModel(libraryModel)
455 }
456 else if (player.source == file &&
457 player.currentIndex === index)
458 {
459 // Same track so just toggle playing state
460 if (play === true) {
461 console.log("Is current track: "+player.playbackState)
462
463 // Show the Now Playing page and make sure the track is visible
464 tabs.pushNowPlaying();
465 nowPlaying.ensureVisibleIndex = index;
466
467 musicToolbar.showToolbar();
468
469 if (musicToolbar.currentPage == nowPlaying) {
470 player.toggle()
471 }
472 }
473
474 return
475 }
476
477 // Current index must be updated before player.source
478 currentModel = libraryModel
479 currentQuery = libraryModel.query
480 currentParam = libraryModel.param
481
482 if (Qt.resolvedUrl(trackQueue.model.get(index).file) != file) {
483 index = trackQueue.indexOf(file) // pick given index first
484 }
485 queueChanged = false
486
487 console.log("Click of fileName: " + file)
488
489 if (play === true) {
490 player.playSong(file, index)
491462
492 // Show the Now Playing page and make sure the track is visible463 // Show the Now Playing page and make sure the track is visible
493 tabs.pushNowPlaying();464 tabs.pushNowPlaying();
@@ -496,34 +467,59 @@
496 musicToolbar.showToolbar();467 musicToolbar.showToolbar();
497 }468 }
498 else {469 else {
499 player.source = file470 player.source = file;
500 }471 }
501472
502 collapseExpand(); // collapse all expands if track clicked473 collapseExpand(); // collapse all expands if track clicked
503474 }
504 return file475
476 function trackQueueClick(index) {
477 if (player.currentIndex === index) {
478 player.toggle();
479 }
480 else {
481 player.playSong(trackQueue.model.get(index).filename, index);
482 }
483
484 // Show the Now Playing page and make sure the track is visible
485 tabs.pushNowPlaying();
486 nowPlaying.ensureVisibleIndex = index;
487
488 musicToolbar.showToolbar();
505 }489 }
506490
507 function playRandomSong(shuffle)491 function playRandomSong(shuffle)
508 {492 {
509 trackQueue.model.clear();493 trackQueue.model.clear();
510494
511 var items = Library.getAll();
512
513 for (var key in items) {
514 trackQueue.append(items[key]);
515 }
516
517 var now = new Date();495 var now = new Date();
518 var seed = now.getSeconds();496 var seed = now.getSeconds();
519 var index = Math.floor(trackQueue.model.count * Math.random(seed));497 var index = Math.floor(allSongsModel.rowCount * Math.random(seed));
520
521 console.debug("THIS", index);
522498
523 player.shuffle = shuffle === undefined ? true : shuffle;499 player.shuffle = shuffle === undefined ? true : shuffle;
524 trackClicked(trackQueue,500
525 index,501 trackClicked(allSongsModel, index, true)
526 true);502 }
503
504 // Load mediascanner store
505 MediaStore {
506 id: musicStore
507 }
508
509 SongsModel {
510 id: allSongsModel
511 // HACK: Temporarily setting limit to 500 to ensure model
512 // is populated. See lp:1326753
513 limit: 500
514 store: musicStore
515 }
516
517 SongsModel {
518 id: songsAlbumArtistModel
519 // HACK: Temporarily setting limit to 500 to ensure model
520 // is populated. See lp:1326753
521 limit: 500
522 store: musicStore
527 }523 }
528524
529 // WHERE THE MAGIC HAPPENS525 // WHERE THE MAGIC HAPPENS
@@ -583,211 +579,19 @@
583 }579 }
584 }580 }
585581
586 GriloModel {582 // TODO: Used by playlisttracks move to U1DB
587 id: griloModel
588 property bool loaded: false
589
590 source: GriloBrowse {
591 id: browser
592 source: "grl-mediascanner"
593 registry: registry
594 metadataKeys: [GriloBrowse.Title]
595 typeFilter: [GriloBrowse.Audio]
596 Component.onCompleted: {
597 console.log(browser.supportedKeys);
598 console.log(browser.slowKeys);
599 refresh();
600 console.log("Refreshing");
601 }
602
603 onAvailableChanged: {
604 console.log("Available ? " + available);
605 if (available === true) {
606 console.log("griloModel.count " + griloModel.count)
607 }
608 }
609 onBaseMediaChanged: refresh();
610
611 /* Check if the file (needle) exists in the library (haystack)
612 * Searches the the haystack using a binary search
613 *
614 * false if the file in in grilo but not in the haystack
615 * positive if the file is the same (number is the actual index)
616 * negative if the file has changed, actual index is -(i + 1)
617 */
618 function exists(haystack, needle)
619 {
620 var keyToFind = needle["file"];
621
622 var upper = haystack.length - 1;
623 var lower = 0;
624 var i = Math.floor(haystack.length / 2);
625
626 while (upper >= lower)
627 {
628 var key = haystack[i]["file"];
629
630 if (keyToFind < key)
631 {
632 upper = i - 1;
633 }
634 else if (keyToFind > key)
635 {
636 lower = i + 1;
637 }
638 else
639 {
640 var found = false;
641
642 for (var k in haystack[i])
643 {
644 if (haystack[i][k] === needle[k])
645 {
646 found = true;
647 }
648 else
649 {
650 found = false;
651 break;
652 }
653 }
654
655 if (found === true)
656 {
657 return i; // in grilo and lib - same
658 }
659 else
660 {
661 return -i - 1; // in grilo and lib - different
662 }
663 }
664
665 i = Math.floor((upper + lower) / 2);
666 }
667
668 return false; // in grilo not in lib
669 }
670
671 onFinished: {
672 // FIXME: remove when grilo is fixed
673 var files = [];
674 var duplicates = 0;
675
676 for (var i = 0; i < griloModel.count; i++)
677 {
678 var media = griloModel.get(i)
679 var file = media.url.toString()
680 if (file.indexOf("file://") === 0)
681 {
682 file = file.slice(7, file.length)
683 }
684
685 // FIXME: grilo can supply duplicates
686 if (files.indexOf(file) > -1)
687 {
688 duplicates++;
689 continue;
690 }
691 files.push(file);
692
693 var record = {
694 artist: media.artist || i18n.tr("Unknown Artist"),
695 album: media.album || i18n.tr("Unknown Album"),
696 title: media.title || file,
697 file: file,
698 cover: media.thumbnail.toString() || "",
699 length: media.duration.toString(),
700 number: media.trackNumber,
701 year: media.year.toString() !== "0" ? media.year.toString(): i18n.tr("Unknown Year"),
702 genre: media.genre || i18n.tr("Unknown Genre")
703 };
704
705 //console.log("Artist:"+ media.artist + ", Album:"+media.album + ", Title:"+media.title + ", File:"+file + ", Cover:"+media.thumbnail + ", Number:"+media.trackNumber + ", Genre:"+media.genre);
706 Library.setMetadata(record)
707 }
708
709 Library.writeDb()
710
711 console.debug("Grilo duplicates:", duplicates); // FIXME: remove when grilo is fixed
712 griloModel.loaded = true
713
714 // Show toolbar and start timer if there is music
715 if (!emptyPage.noMusic || wideAspect) {
716 musicToolbar.showToolbar();
717 musicToolbar.startAutohideTimer();
718 }
719
720 tabs.ensurePopulated(tabs.selectedTab);
721
722 if (args.values.url) {
723 uriHandler.process(args.values.url, true);
724 }
725 }
726 }
727 }
728
729 GriloRegistry {
730 id: registry
731
732 Component.onCompleted: {
733 console.log("Registry is ready");
734 loadAll();
735 }
736 }
737
738 LibraryListModel {
739 id: libraryModel
740 onPreLoadCompleteChanged: {
741 if (preLoadComplete)
742 {
743 loading.visible = false
744 tracksTab.loading = false
745 tracksTab.populated = true
746 }
747 }
748 }
749
750 LibraryListModel {
751 id: artistModel
752 onPreLoadCompleteChanged: {
753 if (preLoadComplete)
754 {
755 loading.visible = false
756 artistsTab.loading = false
757 artistsTab.populated = true
758 }
759 }
760 }
761 LibraryListModel {
762 id: artistTracksModel
763 }
764 LibraryListModel {
765 id: artistAlbumsModel
766 }
767
768 LibraryListModel {
769 id: albumModel
770 onPreLoadCompleteChanged: {
771 if (preLoadComplete)
772 {
773 loading.visible = false
774 albumsTab.loading = false
775 albumsTab.populated = true
776 }
777 }
778 }
779 LibraryListModel {583 LibraryListModel {
780 id: albumTracksModel584 id: albumTracksModel
781 }585 }
782586
587 // TODO: used by recent items move to U1DB
783 LibraryListModel {588 LibraryListModel {
784 id: recentModel589 id: recentModel
785 property bool complete: false590 property bool complete: false
786 onPreLoadCompleteChanged: {591 onPreLoadCompleteChanged: {
787 complete = true;592 complete = true;
788593
789 if (preLoadComplete && (genreModel.complete ||594 if (preLoadComplete)
790 genreModel.query().length === 0))
791 {595 {
792 loading.visible = false596 loading.visible = false
793 startTab.loading = false597 startTab.loading = false
@@ -795,63 +599,29 @@
795 }599 }
796 }600 }
797 }601 }
602
603 // TODO: used by recent albums move to U1DB
798 LibraryListModel {604 LibraryListModel {
799 id: recentAlbumTracksModel605 id: recentAlbumTracksModel
800 }606 }
607
608 // TODO: used by recent playlists move to U1DB
801 LibraryListModel {609 LibraryListModel {
802 id: recentPlaylistTracksModel610 id: recentPlaylistTracksModel
803 }611 }
804612
805 LibraryListModel {
806 id: genreModel
807 property bool complete: false
808 onPreLoadCompleteChanged: {
809 complete = true;
810
811 if (preLoadComplete && (recentModel.complete ||
812 recentModel.query().length === 0))
813 {
814 loading.visible = false
815 startTab.loading = false
816 startTab.populated = true
817 }
818 }
819 }
820
821 LibraryListModel {
822 id: genreTracksModel
823 }
824
825 // list of tracks on startup. This is just during development613 // list of tracks on startup. This is just during development
826 LibraryListModel {614 LibraryListModel {
827 id: trackQueue615 id: trackQueue
828 Connections {
829 target: trackQueue.model
830 onCountChanged: queueChanged = true
831 }
832616
833 function append(listElement)617 function append(listElement)
834 {618 {
835 model.append({619 model.append(makeDict(listElement))
836 "album": listElement.album,620 console.debug(JSON.stringify(makeDict(listElement)));
837 "artist": listElement.artist,
838 "cover": listElement.cover,
839 "file": listElement.file,
840 "title": listElement.title
841 })
842 }621 }
843 }622 }
844623
845 // list of songs, which has been removed.624 // TODO: list of playlists move to U1DB
846 ListModel {
847 id: removedTrackQueue
848 }
849
850 // list of single tracks
851 ListModel {
852 id: singleTracksgriloMo
853 }
854
855 // create the listmodel to use for playlists625 // create the listmodel to use for playlists
856 LibraryListModel {626 LibraryListModel {
857 id: playlistModel627 id: playlistModel
@@ -866,11 +636,6 @@
866 }636 }
867 }637 }
868638
869 // search model
870 LibraryListModel {
871 id: searchModel
872 }
873
874 // load sheets (after model)639 // load sheets (after model)
875 SongsSheet {640 SongsSheet {
876 id: songsSheet641 id: songsSheet
@@ -1002,7 +767,7 @@
1002 title: i18n.tr("Music")767 title: i18n.tr("Music")
1003 visible: false768 visible: false
1004769
1005 property bool noMusic: griloModel.count === 0 && griloModel.loaded === true770 property bool noMusic: allSongsModel.rowCount === 0 && loadedUI
1006771
1007 onNoMusicChanged: {772 onNoMusicChanged: {
1008 if (noMusic)773 if (noMusic)
@@ -1027,7 +792,6 @@
1027 Column {792 Column {
1028 anchors.centerIn: parent793 anchors.centerIn: parent
1029794
1030
1031 Label {795 Label {
1032 anchors.horizontalCenter: parent.horizontalCenter796 anchors.horizontalCenter: parent.horizontalCenter
1033 color: styleMusic.libraryEmpty.labelColor797 color: styleMusic.libraryEmpty.labelColor
@@ -1059,9 +823,9 @@
1059 // First tab is all music823 // First tab is all music
1060 Tab {824 Tab {
1061 property bool populated: false825 property bool populated: false
1062 property var loader: [recentModel.filterRecent, genreModel.filterGenres, albumModel.filterAlbums]826 property var loader: [recentModel.filterRecent]
1063 property bool loading: false827 property bool loading: false
1064 property var model: [recentModel, genreModel, albumTracksModel]828 property var model: [recentModel, albumTracksModel]
1065 id: startTab829 id: startTab
1066 objectName: "starttab"830 objectName: "starttab"
1067 anchors.fill: parent831 anchors.fill: parent
@@ -1075,10 +839,10 @@
1075839
1076 // Second tab is arists840 // Second tab is arists
1077 Tab {841 Tab {
1078 property bool populated: false842 property bool populated: true
1079 property var loader: [artistModel.filterArtists]843 property var loader: []
1080 property bool loading: false844 property bool loading: false
1081 property var model: [artistModel, artistAlbumsModel, albumTracksModel]845 property var model: []
1082 id: artistsTab846 id: artistsTab
1083 objectName: "artiststab"847 objectName: "artiststab"
1084 anchors.fill: parent848 anchors.fill: parent
@@ -1092,10 +856,10 @@
1092856
1093 // third tab is albums857 // third tab is albums
1094 Tab {858 Tab {
1095 property bool populated: false859 property bool populated: true
1096 property var loader: [albumModel.filterAlbums]860 property var loader: []
1097 property bool loading: false861 property bool loading: false
1098 property var model: [albumModel, albumTracksModel]862 property var model: []
1099 id: albumsTab863 id: albumsTab
1100 objectName: "albumstab"864 objectName: "albumstab"
1101 anchors.fill: parent865 anchors.fill: parent
@@ -1109,10 +873,10 @@
1109873
1110 // fourth tab is all songs874 // fourth tab is all songs
1111 Tab {875 Tab {
1112 property bool populated: false876 property bool populated: true
1113 property var loader: [libraryModel.populate]877 property var loader: []
1114 property bool loading: false878 property bool loading: false
1115 property var model: [libraryModel]879 property var model: []
1116 id: tracksTab880 id: tracksTab
1117 objectName: "trackstab"881 objectName: "trackstab"
1118 anchors.fill: parent882 anchors.fill: parent
@@ -1158,7 +922,7 @@
1158 {922 {
1159 allowLoading(selectedTab, true); // allow loading of the models923 allowLoading(selectedTab, true); // allow loading of the models
1160924
1161 if (!selectedTab.populated && !selectedTab.loading && griloModel.loaded) {925 if (!selectedTab.populated && !selectedTab.loading && loadedUI) {
1162 loading.visible = true926 loading.visible = true
1163 selectedTab.loading = true927 selectedTab.loading = true
1164928
1165929
=== modified file 'playlists.js'
--- playlists.js 2014-03-26 12:16:43 +0000
+++ playlists.js 2014-06-19 01:49:30 +0000
@@ -182,12 +182,11 @@
182 for(var i = 0; i < rs.rows.length; i++) {182 for(var i = 0; i < rs.rows.length; i++) {
183 var dbItem = rs.rows.item(i);183 var dbItem = rs.rows.item(i);
184 //console.log("Cover: "+ dbItem.cover);184 //console.log("Cover: "+ dbItem.cover);
185 res[i] = {'file': dbItem.track,185 res[i] = {'filename': dbItem.track,
186 'title': dbItem.title,186 'title': dbItem.title,
187 'artist': dbItem.artist,187 'author': dbItem.artist,
188 'album': dbItem.album,188 'album': dbItem.album,
189 'cover': dbItem.cover,189 'date': dbItem.year,
190 'year': dbItem.year,
191 'number': dbItem.number,190 'number': dbItem.number,
192 'length': dbItem.length,191 'length': dbItem.length,
193 'genre': dbItem.genre,192 'genre': dbItem.genre,
@@ -227,10 +226,10 @@
227 // Get a list of unique covers for the playlist226 // Get a list of unique covers for the playlist
228 try {227 try {
229 db.transaction(function(tx) {228 db.transaction(function(tx) {
230 var rs = tx.executeSql("SELECT * FROM playlist WHERE playlist=? AND cover <> '' ;", [playlist]);229 var rs = tx.executeSql("SELECT * FROM playlist WHERE playlist=?;", [playlist]);
231 for(var i = 0; i < rs.rows.length; i++) {230 for(var i = 0; i < rs.rows.length; i++) {
232 if (res.indexOf(rs.rows.item(i).cover) === -1) {231 if (res.indexOf({author: rs.rows.item(i).artist, album: rs.rows.item(i).album}) === -1) {
233 res.push(rs.rows.item(i).cover);232 res.push({author: rs.rows.item(i).artist, album: rs.rows.item(i).album});
234 }233 }
235 }234 }
236 })235 })
237236
=== removed file 'plugins.json'
--- plugins.json 2014-03-14 00:03:53 +0000
+++ plugins.json 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
1[
2{
3 "package": "qtdeclarative5-qtgrilo0.1"
4}
5]
60
=== modified file 'po/com.ubuntu.music.pot'
--- po/com.ubuntu.music.pot 2014-06-06 11:35:53 +0000
+++ po/com.ubuntu.music.pot 2014-06-19 01:49:30 +0000
@@ -8,7 +8,7 @@
8msgstr ""8msgstr ""
9"Project-Id-Version: music-app\n"9"Project-Id-Version: music-app\n"
10"Report-Msgid-Bugs-To: \n"10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2014-06-06 12:31+0100\n"11"POT-Creation-Date: 2014-06-17 13:05-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,115 +18,115 @@
18"Content-Transfer-Encoding: 8bit\n"18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21#: ../LoginLastFM.qml:50 ../MusicSettings.qml:145 ../MusicSettings.qml:15321#: ../LoginLastFM.qml:49 ../MusicSettings.qml:145 ../MusicSettings.qml:153
22msgid "Last.fm"22msgid "Last.fm"
23msgstr ""23msgstr ""
2424
25#: ../LoginLastFM.qml:5625#: ../LoginLastFM.qml:55
26msgid "Login to be able to scrobble."26msgid "Login to be able to scrobble."
27msgstr ""27msgstr ""
2828
29#: ../LoginLastFM.qml:6429#: ../LoginLastFM.qml:63
30msgid "Username"30msgid "Username"
31msgstr ""31msgstr ""
3232
33#: ../LoginLastFM.qml:7433#: ../LoginLastFM.qml:73
34msgid "Password"34msgid "Password"
35msgstr ""35msgstr ""
3636
37#: ../LoginLastFM.qml:9637#: ../LoginLastFM.qml:95
38msgid "Login"38msgid "Login"
39msgstr ""39msgstr ""
4040
41#: ../LoginLastFM.qml:10341#: ../LoginLastFM.qml:102
42msgid "Trying to login..."42msgid "Trying to login..."
43msgstr ""43msgstr ""
4444
45#: ../LoginLastFM.qml:11745#: ../LoginLastFM.qml:116
46msgid "Login Successful"46msgid "Login Successful"
47msgstr ""47msgstr ""
4848
49#: ../LoginLastFM.qml:12349#: ../LoginLastFM.qml:122
50msgid "Login Failed"50msgid "Login Failed"
51msgstr ""51msgstr ""
5252
53#: ../LoginLastFM.qml:12953#: ../LoginLastFM.qml:128
54msgid "You forgot to set your username and/or password"54msgid "You forgot to set your username and/or password"
55msgstr ""55msgstr ""
5656
57#: ../MusicAlbums.qml:33 ../MusicStart.qml:33357#: ../MusicAlbums.qml:37 ../MusicStart.qml:370
58msgid "Albums"58msgid "Albums"
59msgstr ""59msgstr ""
6060
61#: ../MusicArtists.qml:3361#: ../MusicArtists.qml:37
62msgid "Artists"62msgid "Artists"
63msgstr ""63msgstr ""
6464
65#: ../MusicArtists.qml:107 ../common/AlbumsSheet.qml:11665#: ../MusicArtists.qml:153 ../common/AlbumsSheet.qml:124
66#, qt-format66#, qt-format
67msgid "%1 album"67msgid "%1 album"
68msgid_plural "%1 albums"68msgid_plural "%1 albums"
69msgstr[0] ""69msgstr[0] ""
70msgstr[1] ""70msgstr[1] ""
7171
72#: ../MusicArtists.qml:124 ../MusicPlaylists.qml:186 ../MusicStart.qml:31472#: ../MusicArtists.qml:170 ../MusicPlaylists.qml:187 ../MusicStart.qml:351
73#: ../common/SongsSheet.qml:12973#: ../common/SongsSheet.qml:144
74#, qt-format74#, qt-format
75msgid "%1 song"75msgid "%1 song"
76msgid_plural "%1 songs"76msgid_plural "%1 songs"
77msgstr[0] ""77msgstr[0] ""
78msgstr[1] ""78msgstr[1] ""
7979
80#: ../MusicNowPlaying.qml:3380#: ../MusicNowPlaying.qml:34
81msgid "Now Playing"81msgid "Now Playing"
82msgstr ""82msgstr ""
8383
84#. TRANSLATORS: this is the name of the playlists page shown in the tab header.84#. TRANSLATORS: this is the name of the playlists page shown in the tab header.
85#. Remember to keep the translation short to fit the screen width85#. Remember to keep the translation short to fit the screen width
86#: ../MusicPlaylists.qml:3886#: ../MusicPlaylists.qml:39
87msgid "Playlists"87msgid "Playlists"
88msgstr ""88msgstr ""
8989
90#. TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist90#. TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist
91#: ../MusicPlaylists.qml:5991#: ../MusicPlaylists.qml:60
92msgid "Change name"92msgid "Change name"
93msgstr ""93msgstr ""
9494
95#: ../MusicPlaylists.qml:6095#: ../MusicPlaylists.qml:61
96msgid "Enter the new name of the playlist."96msgid "Enter the new name of the playlist."
97msgstr ""97msgstr ""
9898
99#: ../MusicPlaylists.qml:7199#: ../MusicPlaylists.qml:72
100msgid "Change"100msgid "Change"
101msgstr ""101msgstr ""
102102
103#: ../MusicPlaylists.qml:84103#: ../MusicPlaylists.qml:85
104msgid "You didn't type in a name."104msgid "You didn't type in a name."
105msgstr ""105msgstr ""
106106
107#: ../MusicPlaylists.qml:89 ../MusicPlaylists.qml:115 ../music-app.qml:987107#: ../MusicPlaylists.qml:90 ../MusicPlaylists.qml:116 ../music-app.qml:752
108msgid "Cancel"108msgid "Cancel"
109msgstr ""109msgstr ""
110110
111#. TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist111#. TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist
112#: ../MusicPlaylists.qml:102112#: ../MusicPlaylists.qml:103
113msgid "Are you sure?"113msgid "Are you sure?"
114msgstr ""114msgstr ""
115115
116#: ../MusicPlaylists.qml:103116#: ../MusicPlaylists.qml:104
117msgid "This will delete your playlist."117msgid "This will delete your playlist."
118msgstr ""118msgstr ""
119119
120#: ../MusicPlaylists.qml:106120#: ../MusicPlaylists.qml:107
121msgid "Remove"121msgid "Remove"
122msgstr ""122msgstr ""
123123
124#: ../MusicSearch.qml:42 ../MusicToolbar.qml:508 ../music-app.qml:93124#: ../MusicSearch.qml:44 ../MusicToolbar.qml:508 ../music-app.qml:94
125#: ../music-app.qml:141 ../music-app.qml:147125#: ../music-app.qml:142 ../music-app.qml:148
126msgid "Search"126msgid "Search"
127msgstr ""127msgstr ""
128128
129#: ../MusicSettings.qml:30 ../music-app.qml:191129#: ../MusicSettings.qml:30 ../music-app.qml:192
130msgid "Settings"130msgid "Settings"
131msgstr ""131msgstr ""
132132
@@ -212,20 +212,20 @@
212msgid "Clean everything!"212msgid "Clean everything!"
213msgstr ""213msgstr ""
214214
215#: ../MusicStart.qml:33 ../music-app.qml:366 ../music-app.qml:1002215#: ../MusicStart.qml:37 ../music-app.qml:373 ../music-app.qml:767
216#: com.ubuntu.music_music.desktop.in.in.h:1216#: com.ubuntu.music_music.desktop.in.in.h:1
217msgid "Music"217msgid "Music"
218msgstr ""218msgstr ""
219219
220#: ../MusicStart.qml:80220#: ../MusicStart.qml:84
221msgid "Recent"221msgid "Recent"
222msgstr ""222msgstr ""
223223
224#: ../MusicStart.qml:108224#: ../MusicStart.qml:112
225msgid "Clear History"225msgid "Clear History"
226msgstr ""226msgstr ""
227227
228#: ../MusicStart.qml:217228#: ../MusicStart.qml:222
229msgid "Genres"229msgid "Genres"
230msgstr ""230msgstr ""
231231
@@ -237,38 +237,38 @@
237msgid "Tap play or any item to start"237msgid "Tap play or any item to start"
238msgstr ""238msgstr ""
239239
240#: ../MusicTracks.qml:33240#: ../MusicTracks.qml:37
241msgid "Songs"241msgid "Songs"
242msgstr ""242msgstr ""
243243
244#: ../MusicaddtoPlaylist.qml:38 ../common/ExpanderItems/AddToPlaylist.qml:59244#: ../MusicaddtoPlaylist.qml:39 ../common/ExpanderItems/AddToPlaylist.qml:59
245#: ../music-app.qml:934245#: ../music-app.qml:699
246msgid "Select playlist"246msgid "Select playlist"
247msgstr ""247msgstr ""
248248
249#: ../MusicaddtoPlaylist.qml:130249#: ../MusicaddtoPlaylist.qml:131
250msgid "New playlist"250msgid "New playlist"
251msgstr ""251msgstr ""
252252
253#: ../common/AlbumsSheet.qml:163 ../common/AlbumsSheet.qml:351253#: ../common/AlbumsSheet.qml:180 ../common/AlbumsSheet.qml:382
254#: ../common/SongsSheet.qml:158254#: ../common/SongsSheet.qml:173
255msgid "Play all"255msgid "Play all"
256msgstr ""256msgstr ""
257257
258#: ../common/AlbumsSheet.qml:214 ../common/AlbumsSheet.qml:399258#: ../common/AlbumsSheet.qml:228 ../common/AlbumsSheet.qml:427
259#: ../common/ExpanderItems/AddToQueue.qml:45 ../common/SongsSheet.qml:209259#: ../common/ExpanderItems/AddToQueue.qml:45 ../common/SongsSheet.qml:225
260#: ../music-app.qml:909260#: ../music-app.qml:674
261msgid "Add to queue"261msgid "Add to queue"
262msgstr ""262msgstr ""
263263
264#: ../common/AlbumsSheet.qml:323 ../common/SongsSheet.qml:128264#: ../common/AlbumsSheet.qml:352 ../common/SongsSheet.qml:143
265#, qt-format265#, qt-format
266msgid " | %1 song"266msgid " | %1 song"
267msgid_plural " | %1 songs"267msgid_plural " | %1 songs"
268msgstr[0] ""268msgstr[0] ""
269msgstr[1] ""269msgstr[1] ""
270270
271#: ../common/ExpanderItems/AddToPlaylist.qml:47 ../music-app.qml:923271#: ../common/ExpanderItems/AddToPlaylist.qml:47 ../music-app.qml:688
272msgid "Add to playlist"272msgid "Add to playlist"
273msgstr ""273msgstr ""
274274
@@ -290,112 +290,104 @@
290msgid "Clear"290msgid "Clear"
291msgstr ""291msgstr ""
292292
293#: ../meta-database.js:444 ../meta-database.js:447 ../music-app.qml:695293#: ../meta-database.js:90 ../meta-database.js:92
294msgid "Unknown Album"294msgid "Unknown Album"
295msgstr ""295msgstr ""
296296
297#: ../meta-database.js:445 ../music-app.qml:694297#: ../meta-database.js:91
298msgid "Unknown Artist"298msgid "Unknown Artist"
299msgstr ""299msgstr ""
300300
301#: ../music-app.qml:142301#: ../music-app.qml:143
302msgid "Search Track"302msgid "Search Track"
303msgstr ""303msgstr ""
304304
305#: ../music-app.qml:154305#: ../music-app.qml:155
306msgid "Next"306msgid "Next"
307msgstr ""307msgstr ""
308308
309#: ../music-app.qml:155309#: ../music-app.qml:156
310msgid "Next Track"310msgid "Next Track"
311msgstr ""311msgstr ""
312312
313#: ../music-app.qml:161313#: ../music-app.qml:162
314msgid "Pause"314msgid "Pause"
315msgstr ""315msgstr ""
316316
317#: ../music-app.qml:161317#: ../music-app.qml:162
318msgid "Play"318msgid "Play"
319msgstr ""319msgstr ""
320320
321#: ../music-app.qml:163321#: ../music-app.qml:164
322msgid "Pause Playback"322msgid "Pause Playback"
323msgstr ""323msgstr ""
324324
325#: ../music-app.qml:163325#: ../music-app.qml:164
326msgid "Continue or start playback"326msgid "Continue or start playback"
327msgstr ""327msgstr ""
328328
329#: ../music-app.qml:168329#: ../music-app.qml:169
330msgid "Back"330msgid "Back"
331msgstr ""331msgstr ""
332332
333#: ../music-app.qml:169333#: ../music-app.qml:170
334msgid "Go back to last page"334msgid "Go back to last page"
335msgstr ""335msgstr ""
336336
337#: ../music-app.qml:177337#: ../music-app.qml:178
338msgid "Previous"338msgid "Previous"
339msgstr ""339msgstr ""
340340
341#: ../music-app.qml:178341#: ../music-app.qml:179
342msgid "Previous Track"342msgid "Previous Track"
343msgstr ""343msgstr ""
344344
345#: ../music-app.qml:183345#: ../music-app.qml:184
346msgid "Stop"346msgid "Stop"
347msgstr ""347msgstr ""
348348
349#: ../music-app.qml:184349#: ../music-app.qml:185
350msgid "Stop Playback"350msgid "Stop Playback"
351msgstr ""351msgstr ""
352352
353#: ../music-app.qml:192353#: ../music-app.qml:193
354msgid "Music Settings"354msgid "Music Settings"
355msgstr ""355msgstr ""
356356
357#. 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)357#. 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)
358#: ../music-app.qml:294358#: ../music-app.qml:288
359msgid "songs played today"359msgid "songs played today"
360msgstr ""360msgstr ""
361361
362#: ../music-app.qml:295362#: ../music-app.qml:289
363msgid "No songs played today"363msgid "No songs played today"
364msgstr ""364msgstr ""
365365
366#: ../music-app.qml:392366#: ../music-app.qml:396
367msgid "Debug: "367msgid "Debug: "
368msgstr ""368msgstr ""
369369
370#: ../music-app.qml:701370#: ../music-app.qml:712
371msgid "Unknown Year"
372msgstr ""
373
374#: ../music-app.qml:702
375msgid "Unknown Genre"
376msgstr ""
377
378#: ../music-app.qml:947
379msgid "New Playlist"371msgid "New Playlist"
380msgstr ""372msgstr ""
381373
382#: ../music-app.qml:948374#: ../music-app.qml:713
383msgid "Name your playlist."375msgid "Name your playlist."
384msgstr ""376msgstr ""
385377
386#: ../music-app.qml:952378#: ../music-app.qml:717
387msgid "Name"379msgid "Name"
388msgstr ""380msgstr ""
389381
390#: ../music-app.qml:960382#: ../music-app.qml:725
391msgid "Create"383msgid "Create"
392msgstr ""384msgstr ""
393385
394#: ../music-app.qml:976386#: ../music-app.qml:741
395msgid "Error: "387msgid "Error: "
396msgstr ""388msgstr ""
397389
398#: ../music-app.qml:981390#: ../music-app.qml:746
399msgid "Error: You didn't type a name."391msgid "Error: You didn't type a name."
400msgstr ""392msgstr ""
401393
402394
=== modified file 'po/lv.po'
--- po/lv.po 2014-06-07 06:34:52 +0000
+++ po/lv.po 2014-06-19 01:49:30 +0000
@@ -6,11 +6,12 @@
6msgid ""6msgid ""
7msgstr ""7msgstr ""
8"Project-Id-Version: music-app\n"8"Project-Id-Version: music-app\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2014-06-06 12:31+0100\n"10"POT-Creation-Date: 2014-06-06 12:31+0100\n"
11"PO-Revision-Date: 2014-06-06 17:55+0000\n"11"PO-Revision-Date: 2014-06-06 17:55+0000\n"
12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
13"Language-Team: Latvian <lv@li.org>\n"13"Language-Team: Latvian <lv@li.org>\n"
14"Language: lv\n"
14"MIME-Version: 1.0\n"15"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"17"Content-Transfer-Encoding: 8bit\n"
1718
=== modified file 'tests/autopilot/music_app/__init__.py'
--- tests/autopilot/music_app/__init__.py 2013-06-21 23:06:10 +0000
+++ tests/autopilot/music_app/__init__.py 2014-06-19 01:49:30 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical2# Copyright 2013, 2014 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
66
=== modified file 'tests/autopilot/music_app/content/1.ogg'
7Binary 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 differ7Binary 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
=== removed directory 'tests/autopilot/music_app/content/mediascanner'
=== added directory 'tests/autopilot/music_app/content/mediascanner-2.0'
=== added file 'tests/autopilot/music_app/content/mediascanner-2.0/mediastore.db'
8Binary 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 differ8Binary 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
=== added file 'tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql'
--- tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql 1970-01-01 00:00:00 +0000
+++ tests/autopilot/music_app/content/mediascanner-2.0/mediastore.sql 2014-06-19 01:49:30 +0000
@@ -0,0 +1,39 @@
1BEGIN TRANSACTION;
2DROP TABLE media;
3CREATE TABLE media (
4 filename TEXT PRIMARY KEY NOT NULL,
5 content_type TEXT,
6 etag TEXT,
7 title TEXT,
8 date TEXT,
9 artist TEXT, -- Only relevant to audio
10 album TEXT, -- Only relevant to audio
11 album_artist TEXT, -- Only relevant to audio
12 genre TEXT, -- Only relevant to audio
13 disc_number INTEGER, -- Only relevant to audio
14 track_number INTEGER, -- Only relevant to audio
15 duration INTEGER,
16 width INTEGER, -- Only relevant to video/images
17 height INTEGER, -- Only relevant to video/images
18 latitude DOUBLE,
19 longitude DOUBLE,
20 type INTEGER -- 0=Audio, 1=Video
21);
22INSERT 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);
23INSERT 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);
24INSERT 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);
25
26CREATE INDEX media_album_album_artist_idx ON media(album, album_artist);
27CREATE TRIGGER media_ai AFTER INSERT ON media BEGIN
28 INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album);
29END;
30CREATE TRIGGER media_au AFTER UPDATE ON media BEGIN
31 INSERT INTO media_fts(docid, title, artist, album) VALUES (new.rowid, new.title, new.artist, new.album);
32END;
33CREATE TRIGGER media_bd BEFORE DELETE ON media BEGIN
34 DELETE FROM media_fts WHERE docid=old.rowid;
35END;
36CREATE TRIGGER media_bu BEFORE UPDATE ON media BEGIN
37 DELETE FROM media_fts WHERE docid=old.rowid;
38END;
39COMMIT;
040
=== removed directory 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab'
=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments.gen'
1Binary 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 differ41Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/segments_1'
2Binary 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 differ42Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/7d246d8c-5e6d-4fe8-8271-1472eb02c3ab/write.lock'
=== removed directory 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7'
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_0.cfs'
3Binary 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 differ43Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1.cfs'
4Binary 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 differ44Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_1_1.del'
5Binary 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 differ45Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_2.cfs'
6Binary 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 differ46Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3.cfs'
7Binary 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 differ47Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_3_1.del'
8Binary 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 differ48Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/_4.cfs'
9Binary 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 differ49Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments.gen'
10Binary 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 differ50Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/segments_6'
11Binary 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 differ51Binary 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
=== removed file 'tests/autopilot/music_app/content/mediascanner/d15682c3-89f1-4e41-abfc-531e4740e5a7/write.lock'
=== removed file 'tests/autopilot/music_app/content/mediascanner/mediaindex'
--- tests/autopilot/music_app/content/mediascanner/mediaindex 2013-10-15 18:19:36 +0000
+++ tests/autopilot/music_app/content/mediascanner/mediaindex 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1[global]
2format=Ubuntu Media Scanner Meta Index 1.0
3
4[media:home/autopilot-music-app/Music]
5segments=d15682c3-89f1-4e41-abfc-531e4740e5a7
6relative-path=home/autopilot-music-app/Music
7
8[media:]
9segments=7d246d8c-5e6d-4fe8-8271-1472eb02c3ab
10relative-path=
110
=== modified file 'tests/autopilot/music_app/emulators.py'
--- tests/autopilot/music_app/emulators.py 2014-05-03 15:54:38 +0000
+++ tests/autopilot/music_app/emulators.py 2014-06-19 01:49:30 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical2# Copyright 2013, 2014 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -51,6 +51,23 @@
5151
52 self.pointing_device.drag(x1, y1, x1, y1 - toolbar.fullHeight)52 self.pointing_device.drag(x1, y1, x1, y1 - toolbar.fullHeight)
5353
54 def add_to_queue_from_albums_tab_album_sheet(self, artistName, trackTitle):
55 # switch to albums tab
56 self.switch_to_tab("albumstab")
57
58 #select album
59 albumartist = self.get_albums_albumartist(artistName)
60 self.pointing_device.click_object(albumartist)
61
62 #get track item to add to queue
63 trackicon = self.get_album_sheet_listview_trackicon(
64 trackTitle)
65 self.pointing_device.click_object(trackicon)
66
67 #click on Add to queue
68 queueTrackLabel = self.get_album_sheet_queuetrack_label()
69 self.pointing_device.click_object(queueTrackLabel)
70
54 def get_player(self):71 def get_player(self):
55 return self.select_single("*", objectName="player")72 return self.select_single("*", objectName="player")
5673
@@ -83,7 +100,9 @@
83 return self.wait_select_single("*", objectName="genreItemObject")100 return self.wait_select_single("*", objectName="genreItemObject")
84101
85 def get_back_button(self):102 def get_back_button(self):
86 return self.select_single("*", objectName="nowPlayingBackButtonObject")103 backButton = self.select_single("AbstractButton",
104 objectName="backButton")
105 return backButton
87106
88 def get_albumstab(self):107 def get_albumstab(self):
89 return self.select_single("Tab", objectName="albumstab")108 return self.select_single("Tab", objectName="albumstab")
90109
=== modified file 'tests/autopilot/music_app/tests/__init__.py'
--- tests/autopilot/music_app/tests/__init__.py 2014-04-30 15:24:44 +0000
+++ tests/autopilot/music_app/tests/__init__.py 2014-06-19 01:49:30 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical2# Copyright 2013, 2014 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -7,28 +7,26 @@
77
8"""Music app autopilot tests."""8"""Music app autopilot tests."""
99
10import tempfile
11try:
12 from unittest import mock
13except ImportError:
14 import mock
15import os10import os
11import subprocess
16import os.path12import os.path
17import shutil13import shutil
18#import subprocess14import sqlite3
19import logging15import logging
20import music_app16import music_app
2117
18import fixtures
19from music_app import emulators
20
21from autopilot import logging as autopilot_logging
22from autopilot.input import Mouse, Touch, Pointer22from autopilot.input import Mouse, Touch, Pointer
23from autopilot.platform import model23from autopilot.platform import model
24from autopilot.testcase import AutopilotTestCase24from autopilot.testcase import AutopilotTestCase
2525
26from music_app import emulators
27
28from ubuntuuitoolkit import (26from ubuntuuitoolkit import (
29 base,27 base,
30 emulators as toolkit_emulators,28 emulators as toolkit_emulators,
31 environment29 fixture_setup as toolkit_fixtures
32)30)
3331
3432
@@ -50,6 +48,8 @@
50 local_location_dir = os.path.dirname(os.path.dirname(working_dir))48 local_location_dir = os.path.dirname(os.path.dirname(working_dir))
51 local_location = local_location_dir + "/music-app.qml"49 local_location = local_location_dir + "/music-app.qml"
52 installed_location = "/usr/share/music-app/music-app.qml"50 installed_location = "/usr/share/music-app/music-app.qml"
51 backup_root = os.path.join(
52 os.path.expanduser('~'), '.local/share/com.ubuntu.music/backups')
5353
54 def setup_environment(self):54 def setup_environment(self):
55 if os.path.exists(self.local_location):55 if os.path.exists(self.local_location):
@@ -64,67 +64,199 @@
64 return launch, test_type64 return launch, test_type
6565
66 def setUp(self):66 def setUp(self):
67 subprocess.call(["stop", "mediascanner-2.0"])
68
69 try:
70 pid = subprocess.check_output(["pidof", "mediascanner-dbus-2.0"])
71 except subprocess.CalledProcessError:
72 logger.debug("mediascanner-dbus-2.0 not running")
73 else:
74 pid = pid.decode("utf-8")
75 pid = pid.split(None, 1)[0]
76 subprocess.call(["kill", "-9", pid])
77
78 # Stop any mediascanner-dbus and restart mediascanner on exit
79 self.addCleanup(subprocess.call,
80 'kill -9 \
81 `pidof \
82 /usr/lib/*/mediascanner-2.0/mediascanner-dbus-2.0`',
83 shell=True)
84 self.addCleanup(subprocess.call, ["start", "mediascanner-2.0"])
85
67 launch, self.test_type = self.setup_environment()86 launch, self.test_type = self.setup_environment()
68 self.home_dir = self._patch_home()87
88 #Use backup and restore to setup test environment
89 #################################################
90 #for now, we will use real /home
91 logger.debug("Backup root folder %s" % self.backup_root)
92
93 #backup and wipe before testing
94 sqlite_dir = os.path.join(
95 os.environ.get('HOME'), '.local/share/com.ubuntu.music/Databases')
96 self.backup_folder(sqlite_dir)
97 self.addCleanup(lambda: self.restore_folder(sqlite_dir))
98
99 #backup Music folder and restore it after testing
100 self.backup_folder(os.path.join(os.environ.get('HOME'), 'Music'))
101 self.addCleanup(lambda: self.restore_folder(
102 os.path.join(os.environ.get('HOME'), 'Music')))
103
104 #backup mediascanner folder and restore it after testing
105 self.backup_folder(os.path.join(os.environ.get('HOME'),
106 '.cache/mediascanner-2.0'))
107 self.addCleanup(lambda: self.restore_folder(os.path.join(
108 os.environ.get('HOME'),
109 '.cache/mediascanner-2.0')))
110
111 self.home_dir = os.environ['HOME']
69 self._create_music_library()112 self._create_music_library()
113 #################################################
114 #Use backup and restore to setup test environment
115
116 #Use mocking fakehome
117 #####################
118 #self.home_dir = self._patch_home()
119
120 #self._create_music_library()
121
122 ##we need to also tell upstart about our fake home
123 ##and we need to do this all in one shell,
124 ##also passing along our fake env (env=env)
125 #logger.debug("Launching mediascanner")
126 #env = os.environ.copy()
127 #sethome = "initctl set-env HOME=" + self.home_dir
128 #retcode = subprocess.check_output(sethome + "; \
129 #start mediascanner-2.0",
130 #env=env,
131 #stderr=subprocess.STDOUT,
132 #shell=True)
133 #logger.debug("mediascanner launched %s" % retcode)
134 #time.sleep(10)
135
136 #logger.debug("Launching mediascanner-dbus")
137 #retcode = subprocess.call(
138 #"/usr/lib/*/mediascanner-2.0/mediascanner-dbus-2.0 &",
139 #env=env, stderr=subprocess.STDOUT, shell=True)
140 #logger.debug("mediascanner-dbus launched %s" % retcode)
141
142 ##we attempt to reset home for future upstart jobs
143 #retcode = subprocess.check_output("initctl reset-env",
144 #env=env, shell=True)
145 #retcode = subprocess.check_output("initctl get-env HOME",
146 #env=env, shell=True)
147 #logger.debug("reset initctl home %s" % retcode)
148 #####################
149 #Use mocking fakehome
150
70 self.pointing_device = Pointer(self.input_device_class.create())151 self.pointing_device = Pointer(self.input_device_class.create())
71 super(MusicTestCase, self).setUp()152 super(MusicTestCase, self).setUp()
72 launch()153 launch()
73154
155 @autopilot_logging.log_action(logger.info)
74 def launch_test_local(self):156 def launch_test_local(self):
75 logger.debug("Running via local installation")
76 self.app = self.launch_test_application(157 self.app = self.launch_test_application(
77 base.get_qmlscene_launch_command(),158 base.get_qmlscene_launch_command(),
78 self.local_location,159 self.local_location,
160 "debug",
79 app_type='qt',161 app_type='qt',
80 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)162 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
81163
164 @autopilot_logging.log_action(logger.info)
82 def launch_test_installed(self):165 def launch_test_installed(self):
83 logger.debug("Running via installed debian package")
84 self.app = self.launch_test_application(166 self.app = self.launch_test_application(
85 base.get_qmlscene_launch_command(),167 base.get_qmlscene_launch_command(),
86 self.installed_location,168 self.installed_location,
169 "debug",
87 app_type='qt',170 app_type='qt',
88 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)171 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
89172
173 @autopilot_logging.log_action(logger.info)
90 def launch_test_click(self):174 def launch_test_click(self):
91 logger.debug("Running via click package")
92 self.app = self.launch_click_package(175 self.app = self.launch_click_package(
93 "com.ubuntu.music",176 "com.ubuntu.music",
94 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)177 emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
95178
96 def _patch_home(self):179 def _copy_xauthority_file(self, directory):
97 #make a temp dir180 """ Copy .Xauthority file to directory, if it exists in /home
98 temp_dir = tempfile.mkdtemp()181 """
99182 #If running under xvfb, as jenkins does,
100 #delete it, and recreate it to the length183 #xsession will fail to start without xauthority file
101 #required so our patching the db works184 #Thus if the Xauthority file is in the home directory
102 #require a length of 25185 #make sure we copy it to our temp home directory
103 shutil.rmtree(temp_dir)186
104 temp_dir = temp_dir.ljust(25, 'X')187 xauth = os.path.expanduser(os.path.join(os.environ.get('HOME'),
105 os.mkdir(temp_dir)188 '.Xauthority'))
106 logger.debug("Created fake home directory " + temp_dir)
107 self.addCleanup(shutil.rmtree, temp_dir)
108
109 #if the Xauthority file is in home directory
110 #make sure we copy it to temp home, otherwise do nothing
111 xauth = os.path.expanduser(os.path.join('~', '.Xauthority'))
112 if os.path.isfile(xauth):189 if os.path.isfile(xauth):
113 logger.debug("Copying .Xauthority to fake home " + temp_dir)190 logger.debug("Copying .Xauthority to %s" % directory)
114 shutil.copyfile(191 shutil.copyfile(
115 os.path.expanduser(os.path.join('~', '.Xauthority')),192 os.path.expanduser(os.path.join(os.environ.get('HOME'),
116 os.path.join(temp_dir, '.Xauthority'))193 '.Xauthority')),
194 os.path.join(directory, '.Xauthority'))
117195
118 #click can use initctl env (upstart), but desktop still requires mock196 def _patch_home(self):
197 """ mock /home for testing purposes to preserve user data
198 """
199 #click requires apparmor profile, and writing to special dir
200 #but the desktop can write to a traditional /tmp directory
119 if self.test_type == 'click':201 if self.test_type == 'click':
120 environment.set_initctl_env_var('HOME', temp_dir)202 env_dir = os.path.join(os.environ.get('HOME'), 'autopilot',
121 self.addCleanup(environment.unset_initctl_env_var, 'HOME')203 'fakeenv')
204
205 if not os.path.exists(env_dir):
206 os.makedirs(env_dir)
207
208 temp_dir_fixture = fixtures.TempDir(env_dir)
209 self.useFixture(temp_dir_fixture)
210
211 #apparmor doesn't allow the app to create needed directories,
212 #so we create them now
213 temp_dir = temp_dir_fixture.path
214 temp_dir_cache = os.path.join(temp_dir, '.cache')
215 temp_dir_cache_font = os.path.join(temp_dir_cache, 'fontconfig')
216 temp_dir_cache_media = os.path.join(temp_dir_cache, 'media-art')
217 temp_dir_cache_write = os.path.join(temp_dir_cache,
218 'tncache-write-text.null')
219 temp_dir_config = os.path.join(temp_dir, '.config')
220 temp_dir_toolkit = os.path.join(temp_dir_config,
221 'ubuntu-ui-toolkit')
222 temp_dir_font = os.path.join(temp_dir_cache, '.fontconfig')
223 temp_dir_local = os.path.join(temp_dir, '.local', 'share')
224 temp_dir_confined = os.path.join(temp_dir, 'confined')
225
226 if not os.path.exists(temp_dir_cache):
227 os.makedirs(temp_dir_cache)
228 if not os.path.exists(temp_dir_cache_font):
229 os.makedirs(temp_dir_cache_font)
230 if not os.path.exists(temp_dir_cache_media):
231 os.makedirs(temp_dir_cache_media)
232 if not os.path.exists(temp_dir_cache_write):
233 os.makedirs(temp_dir_cache_write)
234 if not os.path.exists(temp_dir_config):
235 os.makedirs(temp_dir_config)
236 if not os.path.exists(temp_dir_toolkit):
237 os.makedirs(temp_dir_toolkit)
238 if not os.path.exists(temp_dir_font):
239 os.makedirs(temp_dir_font)
240 if not os.path.exists(temp_dir_local):
241 os.makedirs(temp_dir_local)
242 if not os.path.exists(temp_dir_confined):
243 os.makedirs(temp_dir_confined)
244
245 #before we set fixture, copy xauthority if needed
246 self._copy_xauthority_file(temp_dir)
247 self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
248 HOME=temp_dir))
122 else:249 else:
123 patcher = mock.patch.dict('os.environ', {'HOME': temp_dir})250 temp_dir_fixture = fixtures.TempDir()
124 patcher.start()251 self.useFixture(temp_dir_fixture)
125 self.addCleanup(patcher.stop)252 temp_dir = temp_dir_fixture.path
126253
127 logger.debug("Patched home to fake home directory " + temp_dir)254 #before we set fixture, copy xauthority if needed
255 self._copy_xauthority_file(temp_dir)
256 self.useFixture(fixtures.EnvironmentVariable('HOME',
257 newvalue=temp_dir))
258
259 logger.debug("Patched home to fake home directory %s" % temp_dir)
128 return temp_dir260 return temp_dir
129261
130 def _create_music_library(self):262 def _create_music_library(self):
@@ -132,8 +264,10 @@
132 logger.debug("Home set to %s" % self.home_dir)264 logger.debug("Home set to %s" % self.home_dir)
133 musicpath = os.path.join(self.home_dir, 'Music')265 musicpath = os.path.join(self.home_dir, 'Music')
134 logger.debug("Music path set to %s" % musicpath)266 logger.debug("Music path set to %s" % musicpath)
135 mediascannerpath = os.path.join(self.home_dir, '.cache/mediascanner')267 mediascannerpath = os.path.join(self.home_dir,
136 os.mkdir(musicpath)268 '.cache/mediascanner-2.0')
269 if not os.path.exists(musicpath):
270 os.makedirs(musicpath)
137 logger.debug("Mediascanner path set to %s" % mediascannerpath)271 logger.debug("Mediascanner path set to %s" % mediascannerpath)
138272
139 #set content path273 #set content path
@@ -147,11 +281,12 @@
147 shutil.copy(os.path.join(content_dir, '2.ogg'), musicpath)281 shutil.copy(os.path.join(content_dir, '2.ogg'), musicpath)
148 shutil.copy(os.path.join(content_dir, '3.mp3'), musicpath)282 shutil.copy(os.path.join(content_dir, '3.mp3'), musicpath)
149 shutil.copytree(283 shutil.copytree(
150 os.path.join(content_dir, 'mediascanner'), mediascannerpath)284 os.path.join(content_dir, 'mediascanner-2.0'), mediascannerpath)
151285
152 logger.debug("Music copied, files " + str(os.listdir(musicpath)))286 logger.debug("Music copied, files " + str(os.listdir(musicpath)))
153287
154 self._patch_mediascanner_home(mediascannerpath)288 self._patch_mediascanner_home(mediascannerpath)
289
155 logger.debug(290 logger.debug(
156 "Mediascanner database copied, files " +291 "Mediascanner database copied, files " +
157 str(os.listdir(mediascannerpath)))292 str(os.listdir(mediascannerpath)))
@@ -160,18 +295,24 @@
160 #do some inline db patching295 #do some inline db patching
161 #patch mediaindex to proper home296 #patch mediaindex to proper home
162 #these values are dependent upon our sampled db297 #these values are dependent upon our sampled db
163 logger.debug("Patching fake mediascanner database")298 logger.debug("Patching fake mediascanner database in %s" %
299 mediascannerpath)
300 logger.debug(
301 "Mediascanner database files " +
302 str(os.listdir(mediascannerpath)))
303
164 relhome = self.home_dir[1:]304 relhome = self.home_dir[1:]
165 dblocation = "home/autopilot-music-app"305 dblocation = "home/phablet"
166 dbfoldername = "d15682c3-89f1-4e41-abfc-531e4740e5a7"
167 #patch mediaindex306 #patch mediaindex
168 self._file_find_replace(mediascannerpath +307 self._file_find_replace(mediascannerpath +
169 "/mediaindex", dblocation, relhome)308 "/mediastore.sql", dblocation, relhome)
170309
171 #patch file indexes310 con = sqlite3.connect(mediascannerpath + "/mediastore.db")
172 index_template = '%s/%s/_%%s.cfs' % (mediascannerpath, dbfoldername)311 f = open(mediascannerpath + "/mediastore.sql", 'r')
173 for i in range(5):312 sql = f.read()
174 self._file_find_replace(index_template % i, dblocation, relhome)313 cur = con.cursor()
314 cur.executescript(sql)
315 con.close()
175316
176 def _file_find_replace(self, in_filename, find, replace):317 def _file_find_replace(self, in_filename, find, replace):
177 #replace all occurences of string find with string replace318 #replace all occurences of string find with string replace
@@ -188,6 +329,55 @@
188 os.remove(in_filename)329 os.remove(in_filename)
189 os.rename(out_filename, in_filename)330 os.rename(out_filename, in_filename)
190331
332 def backup_folder(self, folder):
333 backup_dir = os.path.join(self.backup_root, os.path.basename(folder))
334 logger.debug('Backup dir set to %s' % backup_dir)
335 try:
336 shutil.rmtree(backup_dir)
337 except:
338 pass
339 else:
340 logger.warning("Prexisting backup found and removed")
341
342 try:
343 shutil.move(folder, backup_dir)
344 except shutil.Error as e:
345 logger.error('Backup error for %s: %s' % (folder, e))
346 except IOError as e:
347 logger.error('Backup error for %s: %s' % (folder, e.strerror))
348 except:
349 logger.error("Unknown error backing up %s" % folder)
350 else:
351 logger.debug('Backed up %s to %s' % (folder, backup_dir))
352
353 def restore_folder(self, folder):
354 backup_dir = os.path.join(self.backup_root, os.path.basename(folder))
355 logger.debug('Backup dir set to %s' % backup_dir)
356 if os.path.exists(backup_dir):
357 if os.path.exists(folder):
358 try:
359 shutil.rmtree(folder)
360 except shutil.Error as e:
361 logger.error('Restore error for %s: %s' % (folder, e))
362 except IOError as e:
363 logger.error('Restore error for %s: %s' %
364 (folder, e.strerror))
365 except:
366 logger.error("Failed to remove test data for %s" % folder)
367 return
368 try:
369 shutil.move(backup_dir, folder)
370 except shutil.Error as e:
371 logger.error('Restore error for %s: %s' % (folder, e))
372 except IOError as e:
373 logger.error('Restore error for %s: %s' % (folder, e.strerror))
374 except:
375 logger.error('Unknown error restoring %s' % folder)
376 else:
377 logger.debug('Restored %s from %s' % (folder, backup_dir))
378 else:
379 logger.warn('No backup found to restore for %s' % folder)
380
191 @property381 @property
192 def player(self):382 def player(self):
193 return self.main_view.get_player()383 return self.main_view.get_player()
194384
=== modified file 'tests/autopilot/music_app/tests/test_music.py'
--- tests/autopilot/music_app/tests/test_music.py 2014-05-21 07:23:35 +0000
+++ tests/autopilot/music_app/tests/test_music.py 2014-06-19 01:49:30 +0000
@@ -1,5 +1,5 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2013 Canonical2# Copyright 2013, 2014 Canonical
3#3#
4# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -21,25 +21,35 @@
2121
2222
23class TestMainWindow(MusicTestCase):23class TestMainWindow(MusicTestCase):
24 FIRST_TITLE = "Foss Yeaaaah! (Radio Edit)"
25 LAST_TITLE = "TestMP3Title"
2624
27 def setUp(self):25 def setUp(self):
28 super(TestMainWindow, self).setUp()26 super(TestMainWindow, self).setUp()
29 self.assertThat(27 self.assertThat(
30 self.main_view.visible, Eventually(Equals(True)))28 self.main_view.visible, Eventually(Equals(True)))
29
31 #wait for activity indicator to stop spinning30 #wait for activity indicator to stop spinning
32 spinner = lambda: self.main_view.get_spinner().running31 spinner = lambda: self.main_view.get_spinner().running
33 self.assertThat(spinner, Eventually(Equals(False)))32 self.assertThat(spinner, Eventually(Equals(False)))
33 self.trackTitle = u"Gran Vals"
34 self.artistName = u"Francisco Tárrega"
35 self.lastTrackTitle = u"TestMP3Title"
3436
35 def populate_and_play_queue(self):37 def populate_and_play_queue(self):
36 first_genre_item = self.main_view.get_first_genre_item()38 first_genre_item = self.main_view.get_first_genre_item()
37 self.pointing_device.click_object(first_genre_item)39 self.pointing_device.click_object(first_genre_item)
3840
39 title = self.FIRST_TITLE41 song = self.main_view.get_album_sheet_listview_tracktitle(
40 song = self.main_view.get_album_sheet_listview_tracktitle(title)42 self.trackTitle)
41 self.pointing_device.click_object(song)43 self.pointing_device.click_object(song)
4244
45 def populate_and_play_queue_from_songs_tab(self):
46 # switch to songs tab
47 self.main_view.switch_to_tab("trackstab")
48
49 # get track item to add to queue
50 trackitem = self.main_view.get_songs_tab_tracktitle(self.trackTitle)
51 self.pointing_device.click_object(trackitem)
52
43 def turn_shuffle_off(self):53 def turn_shuffle_off(self):
44 if self.player.shuffle:54 if self.player.shuffle:
45 shufflebutton = self.main_view.get_shuffle_button()55 shufflebutton = self.main_view.get_shuffle_button()
@@ -81,55 +91,63 @@
81 fake mediascanner database"""91 fake mediascanner database"""
8292
83 # populate queue93 # populate queue
84 first_genre_item = self.main_view.get_first_genre_item()94 self.populate_and_play_queue_from_songs_tab()
85 self.pointing_device.click_object(first_genre_item)
86 trackTitle = "Foss Yeaaaah! (Radio Edit)"
87 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
88 self.pointing_device.click_object(song)
8995
90 title = lambda: self.player.currentMetaTitle96 title = lambda: self.player.currentMetaTitle
91 artist = lambda: self.player.currentMetaArtist97 artist = lambda: self.player.currentMetaArtist
92 self.assertThat(title,98 self.assertThat(title, Eventually(Equals(self.trackTitle)))
93 Eventually(Equals("Foss Yeaaaah! (Radio Edit)")))99 self.assertThat(artist, Eventually(Equals(self.artistName)))
94 self.assertThat(artist, Eventually(Equals("Benjamin Kerensa")))
95100
96 def test_play_pause_library(self):101 def test_play_pause_library(self):
97 """ Test playing and pausing a track (Music Library must exist) """102 """ Test playing and pausing a track (Music Library must exist) """
98103
99 # populate queue104 # get number of tracks in queue before queuing a track
100 first_genre_item = self.main_view.get_first_genre_item()105 initialtracksCount = self.main_view.get_queue_track_count()
101 self.pointing_device.click_object(first_genre_item)106
102 button = self.main_view.get_add_to_queue_button()107 self.main_view.add_to_queue_from_albums_tab_album_sheet(
103 self.pointing_device.click_object(button)108 self.artistName, self.trackTitle)
104109
105 # click on close button to close genre sheet110 # verify track queue has added one to initial value
111 endtracksCount = self.main_view.get_queue_track_count()
112 self.assertThat(endtracksCount, Equals(initialtracksCount + 1))
113
114 #Assert that the song added to the list is not playing
115 self.assertThat(self.player.currentIndex,
116 Eventually(NotEquals(endtracksCount)))
117 self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
118
119 #verify song's metadata matches the item added to the Now Playing view
120 queueArtistName = self.main_view.get_queue_now_playing_artist(
121 self.artistName)
122 self.assertThat(queueArtistName.text, Equals(self.artistName))
123 queueTrackTitle = self.main_view.get_queue_now_playing_title(
124 self.trackTitle)
125 self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
126
127 # click on close button to close album sheet
106 closebutton = self.main_view.get_album_sheet_close_button()128 closebutton = self.main_view.get_album_sheet_close_button()
107 self.pointing_device.click_object(closebutton)129 self.pointing_device.click_object(closebutton)
130 self.assertThat(self.main_view.get_albumstab(), Not(Is(None)))
108131
109 if self.main_view.wideAspect:132 if self.main_view.wideAspect:
110 playbutton = self.main_view.get_now_playing_play_button()133 play_button = self.main_view.get_now_playing_play_button()
111 else:134 else:
112 playbutton = self.main_view.get_play_button()135 play_button = self.main_view.get_play_button()
113136 self.main_view.show_toolbar()
114 self.main_view.show_toolbar()
115137
116 """ Track is playing"""138 """ Track is playing"""
117 self.pointing_device.click_object(playbutton)139 self.pointing_device.click_object(play_button)
118 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))140 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
119141
120 """ Track is not playing"""142 """ Track is not playing"""
121 self.pointing_device.click_object(playbutton)143 self.pointing_device.click_object(play_button)
122 self.assertThat(self.player.isPlaying, Eventually(Equals(False)))144 self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
123145
124 def test_play_pause_now_playing(self):146 def test_play_pause_now_playing(self):
125 """ Test playing and pausing a track (Music Library must exist) """147 """ Test playing and pausing a track (Music Library must exist) """
126148
127 # populate queue149 # populate queue
128 first_genre_item = self.main_view.get_first_genre_item()150 self.populate_and_play_queue_from_songs_tab()
129 self.pointing_device.click_object(first_genre_item)
130 trackTitle = "Foss Yeaaaah! (Radio Edit)"
131 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
132 self.pointing_device.click_object(song)
133151
134 playbutton = self.main_view.get_now_playing_play_button()152 playbutton = self.main_view.get_now_playing_play_button()
135153
@@ -148,11 +166,7 @@
148 """ Test going to next track (Music Library must exist) """166 """ Test going to next track (Music Library must exist) """
149167
150 # populate queue168 # populate queue
151 first_genre_item = self.main_view.get_first_genre_item()169 self.populate_and_play_queue_from_songs_tab()
152 self.pointing_device.click_object(first_genre_item)
153 trackTitle = "Foss Yeaaaah! (Radio Edit)"
154 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
155 self.pointing_device.click_object(song)
156170
157 playbutton = self.main_view.get_now_playing_play_button()171 playbutton = self.main_view.get_now_playing_play_button()
158172
@@ -206,11 +220,7 @@
206 """ Test that mp3 "plays" or at least doesn't crash on load """220 """ Test that mp3 "plays" or at least doesn't crash on load """
207221
208 # populate queue222 # populate queue
209 first_genre_item = self.main_view.get_first_genre_item()223 self.populate_and_play_queue_from_songs_tab()
210 self.pointing_device.click_object(first_genre_item)
211 trackTitle = "Foss Yeaaaah! (Radio Edit)"
212 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
213 self.pointing_device.click_object(song)
214224
215 playbutton = self.main_view.get_now_playing_play_button()225 playbutton = self.main_view.get_now_playing_play_button()
216226
@@ -253,11 +263,7 @@
253 """ Test shuffle (Music Library must exist) """263 """ Test shuffle (Music Library must exist) """
254264
255 # populate queue265 # populate queue
256 first_genre_item = self.main_view.get_first_genre_item()266 self.populate_and_play_queue_from_songs_tab()
257 self.pointing_device.click_object(first_genre_item)
258 trackTitle = "Foss Yeaaaah! (Radio Edit)"
259 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
260 self.pointing_device.click_object(song)
261267
262 """ Track is playing, shuffle is turned on"""268 """ Track is playing, shuffle is turned on"""
263 forwardbutton = self.main_view.get_forward_button()269 forwardbutton = self.main_view.get_forward_button()
@@ -326,18 +332,16 @@
326 def test_show_albums_sheet(self):332 def test_show_albums_sheet(self):
327 """tests navigating to the Albums tab and displaying the album sheet"""333 """tests navigating to the Albums tab and displaying the album sheet"""
328334
329 artistName = "Benjamin Kerensa"
330
331 # switch to albums tab335 # switch to albums tab
332 self.main_view.switch_to_tab("albumstab")336 self.main_view.switch_to_tab("albumstab")
333337
334 #select album338 #select album
335 albumartist = self.main_view.get_albums_albumartist(artistName)339 albumartist = self.main_view.get_albums_albumartist(self.artistName)
336 self.pointing_device.click_object(albumartist)340 self.pointing_device.click_object(albumartist)
337341
338 #get album sheet album artist342 #get album sheet album artist
339 sheet_albumartist = self.main_view.get_album_sheet_artist()343 sheet_albumartist = self.main_view.get_album_sheet_artist()
340 self.assertThat(sheet_albumartist.text, Equals(artistName))344 self.assertThat(sheet_albumartist.text, Equals(self.artistName))
341345
342 # click on close button to close album sheet346 # click on close button to close album sheet
343 closebutton = self.main_view.get_album_sheet_close_button()347 closebutton = self.main_view.get_album_sheet_close_button()
@@ -347,31 +351,11 @@
347 def test_add_song_to_queue_from_albums_sheet(self):351 def test_add_song_to_queue_from_albums_sheet(self):
348 """tests navigating to the Albums tab and adding a song to queue"""352 """tests navigating to the Albums tab and adding a song to queue"""
349353
350 trackTitle = "Foss Yeaaaah! (Radio Edit)"
351 artistName = "Benjamin Kerensa"
352
353 # get number of tracks in queue before queuing a track354 # get number of tracks in queue before queuing a track
354 initialtracksCount = self.main_view.get_queue_track_count()355 initialtracksCount = self.main_view.get_queue_track_count()
355356
356 # switch to albums tab357 self.main_view.add_to_queue_from_albums_tab_album_sheet(
357 self.main_view.switch_to_tab("albumstab")358 self.artistName, self.trackTitle)
358
359 #select album
360 albumartist = self.main_view.get_albums_albumartist(artistName)
361 self.pointing_device.click_object(albumartist)
362
363 #get album sheet album artist
364 sheet_albumartist = self.main_view.get_album_sheet_artist()
365 self.assertThat(sheet_albumartist.text, Equals(artistName))
366
367 #get track item to add to queue
368 trackicon = self.main_view.get_album_sheet_listview_trackicon(
369 trackTitle)
370 self.pointing_device.click_object(trackicon)
371
372 #click on Add to queue
373 queueTrackLabel = self.main_view.get_album_sheet_queuetrack_label()
374 self.pointing_device.click_object(queueTrackLabel)
375359
376 # verify track queue has added one to initial value360 # verify track queue has added one to initial value
377 endtracksCount = self.main_view.get_queue_track_count()361 endtracksCount = self.main_view.get_queue_track_count()
@@ -384,11 +368,11 @@
384368
385 #verify song's metadata matches the item added to the Now Playing view369 #verify song's metadata matches the item added to the Now Playing view
386 queueArtistName = self.main_view.get_queue_now_playing_artist(370 queueArtistName = self.main_view.get_queue_now_playing_artist(
387 artistName)371 self.artistName)
388 self.assertThat(str(queueArtistName.text), Equals(artistName))372 self.assertThat(queueArtistName.text, Equals(self.artistName))
389 queueTrackTitle = self.main_view.get_queue_now_playing_title(373 queueTrackTitle = self.main_view.get_queue_now_playing_title(
390 trackTitle)374 self.trackTitle)
391 self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))375 self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
392376
393 # click on close button to close album sheet377 # click on close button to close album sheet
394 closebutton = self.main_view.get_album_sheet_close_button()378 closebutton = self.main_view.get_album_sheet_close_button()
@@ -399,18 +383,11 @@
399 """tests navigating to the Songs tab and adding the library to the383 """tests navigating to the Songs tab and adding the library to the
400 queue with the selected item being played. """384 queue with the selected item being played. """
401385
402 trackTitle = "Foss Yeaaaah! (Radio Edit)"
403 artistName = "Benjamin Kerensa"
404
405 # get number of tracks in queue before queuing a track386 # get number of tracks in queue before queuing a track
406 initialtracksCount = self.main_view.get_queue_track_count()387 initialtracksCount = self.main_view.get_queue_track_count()
407388
408 # switch to songs tab389 # populate queue
409 self.main_view.switch_to_tab("trackstab")390 self.populate_and_play_queue_from_songs_tab()
410
411 # get track item to add to queue
412 trackitem = self.main_view.get_songs_tab_tracktitle(trackTitle)
413 self.pointing_device.click_object(trackitem)
414391
415 # verify track queue has added all songs to initial value392 # verify track queue has added all songs to initial value
416 endtracksCount = self.main_view.get_queue_track_count()393 endtracksCount = self.main_view.get_queue_track_count()
@@ -423,19 +400,16 @@
423400
424 # verify song's metadata matches the item added to the Now Playing view401 # verify song's metadata matches the item added to the Now Playing view
425 queueArtistName = self.main_view.get_queue_now_playing_artist(402 queueArtistName = self.main_view.get_queue_now_playing_artist(
426 artistName)403 self.artistName)
427 self.assertThat(str(queueArtistName.text), Equals(artistName))404 self.assertThat(queueArtistName.text, Equals(self.artistName))
428 queueTrackTitle = self.main_view.get_queue_now_playing_title(405 queueTrackTitle = self.main_view.get_queue_now_playing_title(
429 trackTitle)406 self.trackTitle)
430 self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))407 self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
431408
432 def test_add_song_to_queue_from_songs_tab(self):409 def test_add_song_to_queue_from_songs_tab(self):
433 """tests navigating to the Songs tab and adding a song from the library410 """tests navigating to the Songs tab and adding a song from the library
434 to the queue via the expandable list view item. """411 to the queue via the expandable list view item. """
435412
436 trackTitle = "Foss Yeaaaah! (Radio Edit)"
437 artistName = "Benjamin Kerensa"
438
439 # get number of tracks in queue before queuing a track413 # get number of tracks in queue before queuing a track
440 initialtracksCount = self.main_view.get_queue_track_count()414 initialtracksCount = self.main_view.get_queue_track_count()
441415
@@ -443,7 +417,7 @@
443 self.main_view.switch_to_tab("trackstab")417 self.main_view.switch_to_tab("trackstab")
444418
445 # get track item to add to queue419 # get track item to add to queue
446 trackitem = self.main_view.get_songs_tab_trackimage(trackTitle)420 trackitem = self.main_view.get_songs_tab_trackimage(self.trackTitle)
447 self.pointing_device.click_object(trackitem)421 self.pointing_device.click_object(trackitem)
448 addtoqueueLabel = self.main_view.get_songs_tab_add_to_queue_label()422 addtoqueueLabel = self.main_view.get_songs_tab_add_to_queue_label()
449 self.pointing_device.click_object(addtoqueueLabel)423 self.pointing_device.click_object(addtoqueueLabel)
@@ -459,23 +433,21 @@
459433
460 # verify song's metadata matches the item added to the Now Playing view434 # verify song's metadata matches the item added to the Now Playing view
461 queueArtistName = self.main_view.get_queue_now_playing_artist(435 queueArtistName = self.main_view.get_queue_now_playing_artist(
462 artistName)436 self.artistName)
463 self.assertThat(str(queueArtistName.text), Equals(artistName))437 self.assertThat(queueArtistName.text, Equals(self.artistName))
464 queueTrackTitle = self.main_view.get_queue_now_playing_title(438 queueTrackTitle = self.main_view.get_queue_now_playing_title(
465 trackTitle)439 self.trackTitle)
466 self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))440 self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
467441
468 def test_create_playlist_from_songs_tab(self):442 def test_create_playlist_from_songs_tab(self):
469 """tests navigating to the Songs tab and creating a playlist by443 """tests navigating to the Songs tab and creating a playlist by
470 selecting a song to add it to a new playlist. """444 selecting a song to add it to a new playlist. """
471445
472 trackTitle = "Foss Yeaaaah! (Radio Edit)"
473
474 # switch to songs tab446 # switch to songs tab
475 self.main_view.switch_to_tab("trackstab")447 self.main_view.switch_to_tab("trackstab")
476448
477 # get track item to add to queue449 # get track item to add to queue
478 trackitem = self.main_view.get_songs_tab_trackimage(trackTitle)450 trackitem = self.main_view.get_songs_tab_trackimage(self.trackTitle)
479 self.pointing_device.click_object(trackitem)451 self.pointing_device.click_object(trackitem)
480 addtoplaylistLbl = self.main_view.get_songs_tab_add_to_playlist_label()452 addtoplaylistLbl = self.main_view.get_songs_tab_add_to_playlist_label()
481 self.pointing_device.click_object(addtoplaylistLbl)453 self.pointing_device.click_object(addtoplaylistLbl)
@@ -513,9 +485,6 @@
513 def test_artists_tab_album(self):485 def test_artists_tab_album(self):
514 """tests navigating to the Artists tab and playing an album"""486 """tests navigating to the Artists tab and playing an album"""
515487
516 artistName = "Benjamin Kerensa"
517 trackTitle = "Foss Yeaaaah! (Radio Edit)"
518
519 # get number of tracks in queue before queuing a track488 # get number of tracks in queue before queuing a track
520 initialtracksCount = self.main_view.get_queue_track_count()489 initialtracksCount = self.main_view.get_queue_track_count()
521490
@@ -523,12 +492,12 @@
523 self.main_view.switch_to_tab("artiststab")492 self.main_view.switch_to_tab("artiststab")
524493
525 #select artist494 #select artist
526 artist = self.main_view.get_artists_artist(artistName)495 artist = self.main_view.get_artists_artist(self.artistName)
527 self.pointing_device.click_object(artist)496 self.pointing_device.click_object(artist)
528497
529 #get album sheet album artist498 #get album sheet album artist
530 sheet_albumartist = self.main_view.get_artist_sheet_artist()499 sheet_albumartist = self.main_view.get_artist_sheet_artist()
531 self.assertThat(sheet_albumartist.text, Equals(artistName))500 self.assertThat(sheet_albumartist.text, Equals(self.artistName))
532501
533 # click on album to shows the artists502 # click on album to shows the artists
534 sheet_albumartist = self.main_view.get_artist_sheet_artist_cover()503 sheet_albumartist = self.main_view.get_artist_sheet_artist_cover()
@@ -536,13 +505,14 @@
536505
537 #get song sheet album artist506 #get song sheet album artist
538 sheet_albumartist = self.main_view.get_album_sheet_artist()507 sheet_albumartist = self.main_view.get_album_sheet_artist()
539 self.assertThat(sheet_albumartist.text, Equals(artistName))508 self.assertThat(sheet_albumartist.text, Equals(self.artistName))
540509
541 # click on song to populate queue and start playing510 # click on song to populate queue and start playing
542 self.pointing_device.click_object(sheet_albumartist)511 self.pointing_device.click_object(sheet_albumartist)
543512
544 #select artist513 #select artist
545 track = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)514 track = self.main_view.get_album_sheet_listview_tracktitle(
515 self.trackTitle)
546 self.pointing_device.click_object(track)516 self.pointing_device.click_object(track)
547517
548 # verify track queue has added all songs to initial value518 # verify track queue has added all songs to initial value
@@ -556,31 +526,25 @@
556526
557 # verify song's metadata matches the item added to the Now Playing view527 # verify song's metadata matches the item added to the Now Playing view
558 queueArtistName = self.main_view.get_queue_now_playing_artist(528 queueArtistName = self.main_view.get_queue_now_playing_artist(
559 artistName)529 self.artistName)
560 self.assertThat(str(queueArtistName.text), Equals(artistName))530 self.assertThat(queueArtistName.text, Equals(self.artistName))
561 queueTrackTitle = self.main_view.get_queue_now_playing_title(531 queueTrackTitle = self.main_view.get_queue_now_playing_title(
562 trackTitle)532 self.trackTitle)
563 self.assertThat(str(queueTrackTitle.text), Equals(trackTitle))533 self.assertThat(queueTrackTitle.text, Equals(self.trackTitle))
564534
565 def test_swipe_to_delete_song(self):535 def test_swipe_to_delete_song(self):
566 """tests navigating to the Now Playing queue, swiping to delete a536 """tests navigating to the Now Playing queue, swiping to delete a
567 track, and confirming the delete action. """537 track, and confirming the delete action. """
568538
569 artistName = "Benjamin Kerensa"
570
571 # populate queue539 # populate queue
572 first_genre_item = self.main_view.get_first_genre_item()540 self.populate_and_play_queue_from_songs_tab()
573 self.pointing_device.click_object(first_genre_item)
574 trackTitle = "Foss Yeaaaah! (Radio Edit)"
575 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
576 self.pointing_device.click_object(song)
577541
578 # get initial queue count542 # get initial queue count
579 initialqueueCount = self.main_view.get_queue_track_count()543 initialqueueCount = self.main_view.get_queue_track_count()
580544
581 # get song to delete545 # get song to delete
582 artistToDelete = self.main_view.get_queue_now_playing_artist(546 artistToDelete = self.main_view.get_queue_now_playing_artist(
583 artistName)547 self.artistName)
584 musicnowplayingpage = self.main_view.get_MusicNowPlaying_page()548 musicnowplayingpage = self.main_view.get_MusicNowPlaying_page()
585549
586 # get coordinates to delete song550 # get coordinates to delete song
@@ -604,7 +568,10 @@
604568
605 def test_playback_stops_when_last_song_ends_and_repeat_off(self):569 def test_playback_stops_when_last_song_ends_and_repeat_off(self):
606 """Check that playback stops when the last song in the queue ends"""570 """Check that playback stops when the last song in the queue ends"""
607 self.populate_and_play_queue()571
572 # populate queue
573 self.populate_and_play_queue_from_songs_tab()
574
608 self.turn_shuffle_off()575 self.turn_shuffle_off()
609 self.turn_repeat_off()576 self.turn_repeat_off()
610577
@@ -620,7 +587,10 @@
620587
621 def test_playback_repeats_when_last_song_ends_and_repeat_on(self):588 def test_playback_repeats_when_last_song_ends_and_repeat_on(self):
622 """With repeat on, the 1st song should play after the last one ends"""589 """With repeat on, the 1st song should play after the last one ends"""
623 self.populate_and_play_queue()590
591 # populate queue
592 self.populate_and_play_queue_from_songs_tab()
593
624 self.turn_shuffle_off()594 self.turn_shuffle_off()
625 self.turn_repeat_on()595 self.turn_repeat_on()
626596
@@ -632,12 +602,15 @@
632602
633 #Make sure we loop back to first song after last song ends603 #Make sure we loop back to first song after last song ends
634 actual_title = lambda: self.player.currentMetaTitle604 actual_title = lambda: self.player.currentMetaTitle
635 self.assertThat(actual_title, Eventually(Equals(self.FIRST_TITLE)))605 self.assertThat(actual_title, Eventually(Equals(self.trackTitle)))
636 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))606 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
637607
638 def test_pressing_next_from_last_song_plays_first_when_repeat_on(self):608 def test_pressing_next_from_last_song_plays_first_when_repeat_on(self):
639 """With repeat on, skipping the last song jumps to the first track"""609 """With repeat on, skipping the last song jumps to the first track"""
640 self.populate_and_play_queue()610
611 # populate queue
612 self.populate_and_play_queue_from_songs_tab()
613
641 self.turn_shuffle_off()614 self.turn_shuffle_off()
642 self.turn_repeat_on()615 self.turn_repeat_on()
643616
@@ -648,12 +621,15 @@
648 self.pointing_device.click_object(forward_button)621 self.pointing_device.click_object(forward_button)
649622
650 actual_title = lambda: self.player.currentMetaTitle623 actual_title = lambda: self.player.currentMetaTitle
651 self.assertThat(actual_title, Eventually(Equals(self.FIRST_TITLE)))624 self.assertThat(actual_title, Eventually(Equals(self.trackTitle)))
652 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))625 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
653626
654 def test_pressing_prev_from_first_song_plays_last_when_repeat_on(self):627 def test_pressing_prev_from_first_song_plays_last_when_repeat_on(self):
655 """With repeat on, 'previous' from the 1st song plays the last one."""628 """With repeat on, 'previous' from the 1st song plays the last one."""
656 self.populate_and_play_queue()629
630 # populate queue
631 self.populate_and_play_queue_from_songs_tab()
632
657 self.turn_shuffle_off()633 self.turn_shuffle_off()
658 self.turn_repeat_on()634 self.turn_repeat_on()
659635
@@ -667,5 +643,5 @@
667 self.pointing_device.click_object(prev_button)643 self.pointing_device.click_object(prev_button)
668644
669 actual_title = lambda: self.player.currentMetaTitle645 actual_title = lambda: self.player.currentMetaTitle
670 self.assertThat(actual_title, Eventually(Equals(self.LAST_TITLE)))646 self.assertThat(actual_title, Eventually(Equals(self.lastTrackTitle)))
671 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))647 self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
672648
=== modified file 'worker-library-loader.js'
--- worker-library-loader.js 2013-12-05 00:23:08 +0000
+++ worker-library-loader.js 2014-06-19 01:49:30 +0000
@@ -1,7 +1,8 @@
1/*1/*
2 * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>2 * Copyright (C) 2013, 2014
3 * Daniel Holm <d.holmen@gmail.com>3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
5 *6 *
6 * This program is free software; you can redistribute it and/or modify7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by8 * 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: