Bug 171213

Summary: Dynamic Collection broken in Amarok 2
Product: [Applications] amarok Reporter: Roman Zimmermann <torotil>
Component: Collections/LocalAssignee: Amarok Developers <amarok-bugs-dist>
Status: RESOLVED FIXED    
Severity: major CC: cp_caverna, dariopnc, gabrimonfa, gladhorn, heri+kde, hydrogen, jaba, jmjohn, k.dummann, kfunk, kronos, krux, macbeth8, mail, matt, mikko.cal, mitchell, pickscrape, rafal.przemyslaw.malinowski, rinse, roman.karlstetter, spraff, stefan.neupert, stuffcorpse, sven.burmeister, thomas, tobias, vdboor
Priority: VHI    
Version: 2.3.1-GIT   
Target Milestone: 2.3.2   
Platform: Gentoo Packages   
OS: Linux   
Latest Commit: Version Fixed In: 2.3.2
Sentry Crash Report:

Description Roman Zimmermann 2008-09-17 15:42:35 UTC
Version:           recent svn (using KDE 4.1.1)
Compiler:          gcc-4.3.1 
OS:                Linux
Installed from:    Gentoo Packages

It seems that currently removable disks are not handled properly. This leads (among other things) to parts of the collection being removed when the disk is not mounted.

How to reproduce:
1. rm .kde4/share/apps/collection*
2. start amarok and add directory on usb-disk to the collection
3. shutdown amarok
4. look at the collection.db

Expected result:
the device table should contain the usb-disk;
directories should have a correct device id

Actual result:
the device table is empty. all directories have deviceid=-1.
Comment 1 Mikko C. 2008-09-20 10:51:59 UTC
Yes, I can confirm this.
Comment 2 Mark Kretschmann 2008-10-25 14:05:39 UTC
What's the deal with this, is it broken, or simply not implemented?
Comment 3 Roman K. 2008-12-04 11:41:59 UTC
Hi, I don't know if this is the right bug, but I think I have the same Problem.
Some of my music is on my internal HDD, an other part is on an external usb HDD. When I open amarok, add the two directories containing the music, everything is in the database, I close amarok, open it again, and still everything is in the database. Then I close amarok, umount the external drive, restart amarok, the music of the external drive is not there. OK, this makes sense. But when I mount this drive again, nothing happens, unless I rescan my entire collection.

My wish: when a part of the collection is on an removable drive, amarok should automatically detect whether this drive is accessible and according to this show the tracks in the collection or not.
Comment 4 Mikko C. 2008-12-04 11:48:04 UTC
In my case it is different: my whole collection is on an external usb drive.
No matter if the disk is mounted or not, Amarok will show the whole collection as being present.
Comment 5 Jeff Mitchell 2008-12-04 23:24:40 UTC
(In reply to comment #2)
> What's the deal with this, is it broken, or simply not implemented?
> 

Not ported.  I think the vast majority of the groundwork is there, and parts have been ported, but it hasn't been completed.
Comment 6 Frederik Gladhorn 2008-12-13 12:06:32 UTC
Amarok 2.0.0 still loses the collection when started with a hdd unmounted.
In addition I get a crash (currently with no backtrace) when mounting the hdd that contains the collection while Amarok 2 is running.
Comment 7 Frederik Gladhorn 2008-12-13 12:08:23 UTC
*** Bug 172321 has been marked as a duplicate of this bug. ***
Comment 8 Mark Kretschmann 2009-01-01 19:57:06 UTC
*** Bug 179306 has been marked as a duplicate of this bug. ***
Comment 9 Mark Kretschmann 2009-01-12 10:02:58 UTC
*** Bug 180385 has been marked as a duplicate of this bug. ***
Comment 10 Dan Meltzer 2009-03-04 21:50:45 UTC
This is mostly fixed in trunk.  NFS/smb mounts are still not dynamic enabled, and unavailable music is shown in the collection browser, but it should be polished by the time 2.1 is released.
Comment 11 Dan Meltzer 2009-03-06 00:02:17 UTC
*** Bug 186274 has been marked as a duplicate of this bug. ***
Comment 12 Mark Kretschmann 2009-04-16 13:42:44 UTC
*** Bug 175736 has been marked as a duplicate of this bug. ***
Comment 13 Myriam Schweingruber 2009-07-16 14:18:54 UTC
Any news on this? Not fixed in trunk so far, 2.2-SVN r997463
Comment 14 Mikko C. 2009-07-19 14:26:04 UTC
*** Bug 200750 has been marked as a duplicate of this bug. ***
Comment 15 Mikko C. 2009-08-22 14:59:26 UTC
*** Bug 194918 has been marked as a duplicate of this bug. ***
Comment 17 Maximilian Kossick 2009-09-05 12:26:02 UTC
Regarding comment #10:
SqlQueryMaker does not restrict queries to devices that are currently mounted.
Comment 18 Elias Probst 2009-09-06 15:57:33 UTC
Amarok 2.1.80 here.

I'm facing this problem with NFS shares too and it is really annoying, as a complete collection rescan takes here ~40 minutes.

So starting Amarok once without having mounted the NFS share before, makes me need to wait for 40 minutes until I finally can use Amarok again.

The scenario looks like this:
- NFS-share, containing ~8000 tracks, hosted on a fileserver
- NFS-share is mounted on 2 clients using autofs over a WLAN connection

The problem:
One of the clients is a laptop, so autofs is started manually because it just causes hanging connections/applications when autofs is trying to reach the NFS mountpoins when being on-the-road.
When I return home/reboot my laptop and start Amarok it shows 0 items in the collection.
Now I quit Amarok, mount the NFS share, and start Amarok again.
It does an automagic collection rescan now.
This scan takes ~40 minutes.
After the scan, all files + ratings etc. are available again.

This is IMHO a real showstopper as I don't like to have to wait for 40 minutes to use Amarok, just because I forgot mounting the NFS share.

Amarok should handle this in a more intelligent way.
Comment 19 Mikko C. 2009-09-23 16:12:24 UTC
Not going to happen for 2.2 I guess.
Comment 20 jmjohn 2009-10-11 03:42:54 UTC
I loved Amarok 1, but I really can't use a music player that rescans my collection on a mounted logical partition every time I fire it up.  That makes it impossible for me to play music.  I'm on Ubuntu 9.04 and Sidux (shared home directory)

I'll guess I'll hop over to Rythymbox or something for a little while.  

That sucks because Amarok really could be the best.
Comment 21 Jeff Mitchell 2009-10-11 17:20:18 UTC
jmjohn,

As far as I know the support is mostly there; it's just not fully finished. But the framework for using it is already in-place, and the components of A2 are all using it...it's just the backend code that's broken.

Essentially what I'm saying is that if you want to see it back, the best thing you could do would be to help out showing it some love. Us devs are all just too overloaded at the moment with other things.

I do plan on looking at it for 2.3 if no one has before then; but we have a bunch of 2.2 releases first. I'll look at it earlier if I can but no promises. So if someone wants to pick up the mantle and work on it...
Comment 22 Mark Kretschmann 2009-11-02 13:47:55 UTC
So, as Jeff wrote, support for this feature is almost there. Anybody here motivated to finish this feature?

Thanks to Git, it's now possible to develop features safely in another clone of Amarok, and then send us a Merge Request when everything is finished.

Many users have already done this. See here:

http://gitorious.org/amarok/amarok/merge_requests
Comment 23 Myriam Schweingruber 2009-12-25 13:07:05 UTC
*** Bug 218000 has been marked as a duplicate of this bug. ***
Comment 24 Mikko C. 2010-01-03 16:34:53 UTC
*** Bug 220609 has been marked as a duplicate of this bug. ***
Comment 25 Mikko C. 2010-01-03 18:38:50 UTC
*** Bug 220525 has been marked as a duplicate of this bug. ***
Comment 26 Myriam Schweingruber 2010-01-08 23:29:41 UTC
*** Bug 221858 has been marked as a duplicate of this bug. ***
Comment 27 Maximilian Kossick 2010-01-09 20:36:39 UTC
commit 51a6a9a25fd288814ddac36db554636f52f3eb73
Author: Maximilian Kossick <maximilian.kossick@googlemail.com>
Date:   Sat Jan 9 20:33:52 2010 +0100

    only query tracks on mounted devices in SqlQueryMaker.
    This is the missing bit of Dynamic Collection support
    BUG: 171213

diff --git a/ChangeLog b/ChangeLog
index 79a597b..e7d84c0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,7 @@ Amarok ChangeLog
 
 VERSION 2.2.3
   FEATURES:
+    * Dynamic Collection is back. (BR 171213)
     * Searching covers from Last.fm using custom query in interactive mode.
  
   CHANGES:
diff --git a/src/collection/sqlcollection/SqlQueryMaker.cpp b/src/collection/sqlcollection/SqlQueryMaker.cpp
index dd56af7..97a2c96 100644
--- a/src/collection/sqlcollection/SqlQueryMaker.cpp
+++ b/src/collection/sqlcollection/SqlQueryMaker.cpp
@@ -735,6 +735,8 @@ SqlQueryMaker::linkTables()
 void
 SqlQueryMaker::buildQuery()
 {
+    //URLS is always required for dynamic collection
+    d->linkedTables |= Private::URLS_TAB;
     linkTables();
     QString query = "SELECT ";
     if ( d->withoutDuplicates )
@@ -743,6 +745,22 @@ SqlQueryMaker::buildQuery()
     query += " FROM ";
     query += d->queryFrom;
     query += " WHERE 1 ";
+
+    //dynamic collection
+    Q_ASSERT( m_collection->mountPointManager() );
+    IdList list = m_collection->mountPointManager()->getMountedDeviceIds();
+    QString commaSeparatedIds;
+    foreach( int id, list )
+    {
+        if( !commaSeparatedIds.isEmpty() )
+            commaSeparatedIds += ',';
+        commaSeparatedIds += QString::number( id );
+    }
+    if( !commaSeparatedIds.isEmpty() )
+    {
+        query += QString( " AND urls.deviceid in (%1)" ).arg( commaSeparatedIds );
+    }
+
     switch( d->albumMode )
     {
         case OnlyNormalAlbums:
diff --git a/tests/collection/sqlcollection/TestSqlQueryMaker.cpp b/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
index c9273f3..4dda826 100644
--- a/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
+++ b/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
@@ -87,8 +87,15 @@ TestSqlQueryMaker::initTestCase()
     m_storage = new MySqlEmbeddedStorage( m_tmpDir->name() );
     m_collection = new SqlCollection( "testId", "testcollection" );
     m_collection->setSqlStorage( m_storage );
-    SqlMountPointManagerMock *mpm = new SqlMountPointManagerMock();
-    m_collection->setMountPointManager( mpm );
+
+    QMap<int,QString> mountPoints;
+    mountPoints.insert( 1, "/foo" );
+    mountPoints.insert( 2, "/bar" );
+
+    m_mpm = new SqlMountPointManagerMock();
+    m_mpm->mountPoints = mountPoints;
+
+    m_collection->setMountPointManager( m_mpm );
     SqlRegistry *registry = new SqlRegistry( m_collection );
     registry->setStorage( m_storage );
     m_collection->setRegistry( registry );
@@ -123,9 +130,9 @@ TestSqlQueryMaker::initTestCase()
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (1, -1, './IDoNotExist.mp3','1');" );
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (2, -1, './IDoNotExistAsWell.mp3','2');" );
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (3, -1, './MeNeither.mp3','3');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (4, -1, './NothingHere.mp3','4');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (5, -1, './GuessWhat.mp3','5');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (6, -1, './LookItsA.flac','6');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (4, 2, './NothingHere.mp3','4');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (5, 1, './GuessWhat.mp3','5');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (6, 2, './LookItsA.flac','6');" );
 
     m_storage->query( "INSERT INTO tracks(id,url,title,comment,artist,album,genre,year,composer) "
                       "VALUES(1,1,'track1','comment1',1,1,1,1,1);" );
@@ -153,6 +160,12 @@ TestSqlQueryMaker::cleanupTestCase()
 }
 
 void
+TestSqlQueryMaker::cleanup()
+{
+    m_collection->setMountPointManager( m_mpm );
+}
+
+void
 TestSqlQueryMaker::testQueryAlbums()
 {
     SqlQueryMaker qm( m_collection );
@@ -747,6 +760,63 @@ TestSqlQueryMaker::testMatch()
     QCOMPARE( qm.data( "testId" ).count(), count );
 }
 
+void
+TestSqlQueryMaker::testDynamicCollection()
+{
+    //this will not crash as we reset the correct mock in cleanup()
+    SqlMountPointManagerMock mpm;
+
+    QMap<int, QString> mountPoints;
+
+    mpm.mountPoints = mountPoints;
+
+    m_collection->setMountPointManager( &mpm );
+
+    SqlQueryMaker trackQm( m_collection );
+    trackQm.setQueryType( QueryMaker::Track );
+    trackQm.setBlocking( true );
+    trackQm.run();
+    QCOMPARE( trackQm.tracks( "testId" ).count(), 3 );
+
+    mpm.mountPoints.insert( 1, "/foo" );
+
+    trackQm.reset();
+    trackQm.setQueryType( QueryMaker::Track );
+    trackQm.setBlocking( true );
+    trackQm.run();
+    QCOMPARE( trackQm.tracks( "testId" ).count(), 4 );
+
+    SqlQueryMaker artistQm( m_collection );
+    artistQm.setQueryType( QueryMaker::Artist );
+    artistQm.setBlocking( true );
+    artistQm.run();
+    QCOMPARE( artistQm.artists( "testId" ).count(), 2 );
+
+    SqlQueryMaker albumQm( m_collection );
+    albumQm.setQueryType( QueryMaker::Album );
+    albumQm.setBlocking( true );
+    albumQm.run();
+    QCOMPARE( albumQm.albums( "testId" ).count(), 4 );
+
+    SqlQueryMaker genreQm( m_collection );
+    genreQm.setQueryType( QueryMaker::Genre );
+    genreQm.setBlocking( true );
+    genreQm.run();
+    QCOMPARE( genreQm.genres( "testId" ).count(), 2 );
+
+    SqlQueryMaker composerQm( m_collection );
+    composerQm.setQueryType( QueryMaker::Composer );
+    composerQm.setBlocking( true );
+    composerQm.run();
+    QCOMPARE( composerQm.composers( "testId" ).count(), 2 );
+
+    SqlQueryMaker yearQm( m_collection );
+    yearQm.setQueryType( QueryMaker::Year );
+    yearQm.setBlocking( true );
+    yearQm.run();
+    QCOMPARE( yearQm.years( "testId" ).count(), 2 );
+
+}
 
 
 #include "TestSqlQueryMaker.moc"
diff --git a/tests/collection/sqlcollection/TestSqlQueryMaker.h b/tests/collection/sqlcollection/TestSqlQueryMaker.h
index 04e2699..264031e 100644
--- a/tests/collection/sqlcollection/TestSqlQueryMaker.h
+++ b/tests/collection/sqlcollection/TestSqlQueryMaker.h
@@ -23,6 +23,7 @@
 
 class SqlStorage;
 class SqlCollection;
+class SqlMountPointManagerMock;
 
 class TestSqlQueryMaker : public QObject
 {
@@ -34,6 +35,8 @@ private slots:
     void initTestCase();
     void cleanupTestCase();
 
+    void cleanup();
+
     void testQueryTracks();
     void testQueryAlbums();
     void testQueryGenres();
@@ -59,8 +62,11 @@ private slots:
     void testMatch();
     void testMatch_data();
 
+    void testDynamicCollection();
+
 private:
     SqlCollection *m_collection;
+    SqlMountPointManagerMock *m_mpm;
     SqlStorage *m_storage;
     KTempDir *m_tmpDir;
 };
Comment 28 Maximilian Kossick 2010-01-09 20:37:49 UTC
commit 51a6a9a25fd288814ddac36db554636f52f3eb73
Author: Maximilian Kossick <maximilian.kossick@googlemail.com>
Date:   Sat Jan 9 20:33:52 2010 +0100

    only query tracks on mounted devices in SqlQueryMaker.
    This is the missing bit of Dynamic Collection support
    BUG: 171213

diff --git a/ChangeLog b/ChangeLog
index 79a597b..e7d84c0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,7 @@ Amarok ChangeLog
 
 VERSION 2.2.3
   FEATURES:
+    * Dynamic Collection is back. (BR 171213)
     * Searching covers from Last.fm using custom query in interactive mode.
  
   CHANGES:
diff --git a/src/collection/sqlcollection/SqlQueryMaker.cpp b/src/collection/sqlcollection/SqlQueryMaker.cpp
index dd56af7..97a2c96 100644
--- a/src/collection/sqlcollection/SqlQueryMaker.cpp
+++ b/src/collection/sqlcollection/SqlQueryMaker.cpp
@@ -735,6 +735,8 @@ SqlQueryMaker::linkTables()
 void
 SqlQueryMaker::buildQuery()
 {
+    //URLS is always required for dynamic collection
+    d->linkedTables |= Private::URLS_TAB;
     linkTables();
     QString query = "SELECT ";
     if ( d->withoutDuplicates )
@@ -743,6 +745,22 @@ SqlQueryMaker::buildQuery()
     query += " FROM ";
     query += d->queryFrom;
     query += " WHERE 1 ";
+
+    //dynamic collection
+    Q_ASSERT( m_collection->mountPointManager() );
+    IdList list = m_collection->mountPointManager()->getMountedDeviceIds();
+    QString commaSeparatedIds;
+    foreach( int id, list )
+    {
+        if( !commaSeparatedIds.isEmpty() )
+            commaSeparatedIds += ',';
+        commaSeparatedIds += QString::number( id );
+    }
+    if( !commaSeparatedIds.isEmpty() )
+    {
+        query += QString( " AND urls.deviceid in (%1)" ).arg( commaSeparatedIds );
+    }
+
     switch( d->albumMode )
     {
         case OnlyNormalAlbums:
diff --git a/tests/collection/sqlcollection/TestSqlQueryMaker.cpp b/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
index c9273f3..4dda826 100644
--- a/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
+++ b/tests/collection/sqlcollection/TestSqlQueryMaker.cpp
@@ -87,8 +87,15 @@ TestSqlQueryMaker::initTestCase()
     m_storage = new MySqlEmbeddedStorage( m_tmpDir->name() );
     m_collection = new SqlCollection( "testId", "testcollection" );
     m_collection->setSqlStorage( m_storage );
-    SqlMountPointManagerMock *mpm = new SqlMountPointManagerMock();
-    m_collection->setMountPointManager( mpm );
+
+    QMap<int,QString> mountPoints;
+    mountPoints.insert( 1, "/foo" );
+    mountPoints.insert( 2, "/bar" );
+
+    m_mpm = new SqlMountPointManagerMock();
+    m_mpm->mountPoints = mountPoints;
+
+    m_collection->setMountPointManager( m_mpm );
     SqlRegistry *registry = new SqlRegistry( m_collection );
     registry->setStorage( m_storage );
     m_collection->setRegistry( registry );
@@ -123,9 +130,9 @@ TestSqlQueryMaker::initTestCase()
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (1, -1, './IDoNotExist.mp3','1');" );
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (2, -1, './IDoNotExistAsWell.mp3','2');" );
     m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (3, -1, './MeNeither.mp3','3');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (4, -1, './NothingHere.mp3','4');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (5, -1, './GuessWhat.mp3','5');" );
-    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (6, -1, './LookItsA.flac','6');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (4, 2, './NothingHere.mp3','4');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (5, 1, './GuessWhat.mp3','5');" );
+    m_storage->query( "INSERT INTO urls(id,deviceid,rpath,uniqueid) VALUES (6, 2, './LookItsA.flac','6');" );
 
     m_storage->query( "INSERT INTO tracks(id,url,title,comment,artist,album,genre,year,composer) "
                       "VALUES(1,1,'track1','comment1',1,1,1,1,1);" );
@@ -153,6 +160,12 @@ TestSqlQueryMaker::cleanupTestCase()
 }
 
 void
+TestSqlQueryMaker::cleanup()
+{
+    m_collection->setMountPointManager( m_mpm );
+}
+
+void
 TestSqlQueryMaker::testQueryAlbums()
 {
     SqlQueryMaker qm( m_collection );
@@ -747,6 +760,63 @@ TestSqlQueryMaker::testMatch()
     QCOMPARE( qm.data( "testId" ).count(), count );
 }
 
+void
+TestSqlQueryMaker::testDynamicCollection()
+{
+    //this will not crash as we reset the correct mock in cleanup()
+    SqlMountPointManagerMock mpm;
+
+    QMap<int, QString> mountPoints;
+
+    mpm.mountPoints = mountPoints;
+
+    m_collection->setMountPointManager( &mpm );
+
+    SqlQueryMaker trackQm( m_collection );
+    trackQm.setQueryType( QueryMaker::Track );
+    trackQm.setBlocking( true );
+    trackQm.run();
+    QCOMPARE( trackQm.tracks( "testId" ).count(), 3 );
+
+    mpm.mountPoints.insert( 1, "/foo" );
+
+    trackQm.reset();
+    trackQm.setQueryType( QueryMaker::Track );
+    trackQm.setBlocking( true );
+    trackQm.run();
+    QCOMPARE( trackQm.tracks( "testId" ).count(), 4 );
+
+    SqlQueryMaker artistQm( m_collection );
+    artistQm.setQueryType( QueryMaker::Artist );
+    artistQm.setBlocking( true );
+    artistQm.run();
+    QCOMPARE( artistQm.artists( "testId" ).count(), 2 );
+
+    SqlQueryMaker albumQm( m_collection );
+    albumQm.setQueryType( QueryMaker::Album );
+    albumQm.setBlocking( true );
+    albumQm.run();
+    QCOMPARE( albumQm.albums( "testId" ).count(), 4 );
+
+    SqlQueryMaker genreQm( m_collection );
+    genreQm.setQueryType( QueryMaker::Genre );
+    genreQm.setBlocking( true );
+    genreQm.run();
+    QCOMPARE( genreQm.genres( "testId" ).count(), 2 );
+
+    SqlQueryMaker composerQm( m_collection );
+    composerQm.setQueryType( QueryMaker::Composer );
+    composerQm.setBlocking( true );
+    composerQm.run();
+    QCOMPARE( composerQm.composers( "testId" ).count(), 2 );
+
+    SqlQueryMaker yearQm( m_collection );
+    yearQm.setQueryType( QueryMaker::Year );
+    yearQm.setBlocking( true );
+    yearQm.run();
+    QCOMPARE( yearQm.years( "testId" ).count(), 2 );
+
+}
 
 
 #include "TestSqlQueryMaker.moc"
diff --git a/tests/collection/sqlcollection/TestSqlQueryMaker.h b/tests/collection/sqlcollection/TestSqlQueryMaker.h
index 04e2699..264031e 100644
--- a/tests/collection/sqlcollection/TestSqlQueryMaker.h
+++ b/tests/collection/sqlcollection/TestSqlQueryMaker.h
@@ -23,6 +23,7 @@
 
 class SqlStorage;
 class SqlCollection;
+class SqlMountPointManagerMock;
 
 class TestSqlQueryMaker : public QObject
 {
@@ -34,6 +35,8 @@ private slots:
     void initTestCase();
     void cleanupTestCase();
 
+    void cleanup();
+
     void testQueryTracks();
     void testQueryAlbums();
     void testQueryGenres();
@@ -59,8 +62,11 @@ private slots:
     void testMatch();
     void testMatch_data();
 
+    void testDynamicCollection();
+
 private:
     SqlCollection *m_collection;
+    SqlMountPointManagerMock *m_mpm;
     SqlStorage *m_storage;
     KTempDir *m_tmpDir;
 };
Comment 29 Myriam Schweingruber 2010-01-09 21:12:56 UTC
I am sorry, but this is not fixed. 
I just compiled after this fix and now my external HD is mounted on Amarok start, but my collection shows 0 tracks and all collection locations are gone from the settings.
Comment 30 Kevin Funk 2010-01-26 10:27:18 UTC
This is a major issue for people using external HDs as you are required to Rescan your external media completely. Definitely not fixed in Git as of today.
Comment 31 Myriam Schweingruber 2010-01-26 13:16:06 UTC
It used to work at some point just before we released 2.2.2, now this is broken again on current git, so call this a Regression :(
Comment 32 Mikko C. 2010-02-15 18:36:23 UTC
*** Bug 227018 has been marked as a duplicate of this bug. ***
Comment 33 Myriam Schweingruber 2010-02-27 11:55:10 UTC
Maximilian, you might not have seen that this bug was reopened...
Comment 34 kronos 2010-03-13 00:58:52 UTC
amarok doesn't react well to surprise removal of external collections. in fact when i reconnect i have to rescan said collection - not ideal especially in situations where the external collection is huge. Amarok needs to determine if the collection is on a removable device and was subject to surprise removal OR if the user removed the files from the collection. PRIOR to wiping it's info from the DB. 

in fact if amarok cant figure out what just happened, it should ask the user about it if more than $percentage-of-collection disappears in $defined-period-of-time BEFORE it does anything....
Comment 35 S. Burmeister 2010-03-13 17:23:57 UTC
https://bugs.kde.org/show_bug.cgi?id=220525 is about that issue. IMHO amarok just takes the wrong approach i.e. tries to be smart instead of relying on the user. Digikam has a very well working collection management, including local and remote collections but instead of taking a working solution amarok tries to be smarter and thus stays broken.

IMO 220525 is not a duplicate of this bug because it does not ask to fix the current collection management but burry it and introduce digikam's. Don't try to fix what's broken by design.
Comment 36 Sven Krohlas 2010-03-26 13:41:52 UTC
*** Bug 232194 has been marked as a duplicate of this bug. ***
Comment 37 Maximilian Kossick 2010-03-27 09:08:35 UTC
Looks like the port of MountPointManager is buggy, adding Dan.

Dynamic Collection code in collection and querymaker works fine.
Comment 38 Sven Krohlas 2010-04-09 13:51:13 UTC
*** Bug 227739 has been marked as a duplicate of this bug. ***
Comment 39 Sven Krohlas 2010-04-09 13:51:35 UTC
*** Bug 233431 has been marked as a duplicate of this bug. ***
Comment 40 Jeff Mitchell 2010-08-09 00:56:44 UTC
commit 57e87dc0139577120a19c0f1c39f5b7fb02b002e
Author: Jeff Mitchell <mitchell@kde.org>
Date:   Sun Aug 8 18:50:39 2010 -0400

    Fix Dynamic Collections.
    
    There were three problems I could find:
    
    1) The more minor was that devices that were already mounted when Amarok
    was first started (or the database freshly wiped) wouldn't be found
    because the database would be created after the mount point detection
    code, so the device entries couldn't be inserted.
    
    2) When a device was added, the Solid predicate that was being searched
    could not always match. Dunno why...bug in Solid, or us, but regardless,
    instead find all storage volumes and search the returned devices
    manually.
    
    3) The MountPointManager code wasn't properly watching for mount changes.
    MediaDeviceCache was, though, so now MPM gets its signals from MDC
    instead of from Solid directly.
    
    This fixes all of the problems with Dynamic Collection that I could
    find. I'd appreciate if people could test this out and see if they can
    still reproduce problems. N.B.: you *must* rescan your files after you
    compile this code, because that's the only way to get the files associated
    with the proper device IDs (unless you know how to tweak your DB
    manually). So if you don't rescan your files you'll probably still have
    problems until you do.
    
    CCMAIL: amarok-devel@kde.org
    CCBUG: 171213

diff --git a/ChangeLog b/ChangeLog
index 7a856ec..714a7c4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -42,6 +42,9 @@ VERSION 2.3.2-Beta 1
       Patch by Richard Longland <rlongland@hotmail.com>.
 
   BUGFIXES:
+    * Fix regression in Dynamic Collections. For files that were scanned when
+      Dynamic Collections wasn't working, you will need to rescan them to get
+      them associated with the proper device.
     * Fix crash on exit with newer KDE versions. Patch by Martin Blumenstingl and Felix
       Geyer. (BR 245513)
     * Fixed playlist bottom toolbar getting to tall when using "Text only"
diff --git a/src/MediaDeviceCache.h b/src/MediaDeviceCache.h
index c34ea7c..11f0c01 100644
--- a/src/MediaDeviceCache.h
+++ b/src/MediaDeviceCache.h
@@ -55,7 +55,7 @@ class AMAROK_EXPORT MediaDeviceCache : public QObject
     signals:
         void deviceAdded( const QString &udi );
         void deviceRemoved( const QString &udi );
-	void accessibilityChanged( bool accessible, const QString &udi );
+        void accessibilityChanged( bool accessible, const QString &udi );
 
     public slots:
         void slotAddSolidDevice( const QString &udi );
diff --git a/src/core-impl/collections/sqlcollection/MountPointManager.cpp b/src/core-impl/collections/sqlcollection/MountPointManager.cpp
index 013376b..a28e976 100644
--- a/src/core-impl/collections/sqlcollection/MountPointManager.cpp
+++ b/src/core-impl/collections/sqlcollection/MountPointManager.cpp
@@ -18,6 +18,8 @@
 
 #include "MountPointManager.h"
 
+#include "MediaDeviceCache.h"
+
 #include "core/support/Amarok.h"
 #include "core/support/Debug.h"
 #include "statusbar/StatusBar.h"
@@ -52,8 +54,8 @@ MountPointManager::MountPointManager( QObject *parent, SqlStorage *storage )
         return;
     }
 
-    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceAdded( QString ) ), SLOT( deviceAdded( QString ) ) );
-    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceRemoved( QString ) ), SLOT( deviceRemoved( QString ) ) );
+    connect( MediaDeviceCache::instance(), SIGNAL( deviceAdded( QString ) ), SLOT( deviceAdded( QString ) ) );
+    connect( MediaDeviceCache::instance(), SIGNAL( deviceRemoved( QString ) ), SLOT( deviceRemoved( QString ) ) );
 
     init();
 
@@ -445,14 +447,22 @@ void
 MountPointManager::deviceAdded( const QString &udi )
 {
     DEBUG_BLOCK
-    Solid::Predicate predicate = Solid::Predicate( Solid::DeviceInterface::StorageVolume, "udi", udi );
+    Solid::Predicate predicate = Solid::Predicate( Solid::DeviceInterface::StorageVolume );
     QList<Solid::Device> devices = Solid::Device::listFromQuery( predicate );
-    //there'll be maximum one device because we are using the udi in the predicate
-    if( !devices.isEmpty() )
+    //Looking for a specific udi in predicate seems flaky/buggy; the foreach loop barely
+    //takes any time, so just be safe
+    bool found = false;
+    debug() << "looking for udi " << udi;
+    foreach( Solid::Device device, devices )
     {
-        Solid::Device device = devices[0];
-        createHandlerFromDevice( device, udi );
+        if( device.udi() == udi )
+        {
+            createHandlerFromDevice( device, udi );
+            found = true;
+        }
     }
+    if( !found )
+        debug() << "Did not find device from Solid for udi " << udi;
 }
 
 void
@@ -479,6 +489,7 @@ MountPointManager::deviceRemoved( const QString &udi )
 
 void MountPointManager::createHandlerFromDevice( const Solid::Device& device, const QString &udi )
 {
+    DEBUG_BLOCK
     if ( device.isValid() )
     {
         debug() << "Device added and mounted, checking handlers";
@@ -507,8 +518,12 @@ void MountPointManager::createHandlerFromDevice( const Solid::Device& device, co
                 emit deviceAdded( key );
                 break;  //we found the added medium and don't have to check the other device handlers
             }
+            else
+                debug() << "Factory can't handle device " << udi;
         }
     }
+    else
+        debug() << "Device not valid!";
 }
 
 
diff --git a/src/core-impl/collections/sqlcollection/SqlCollection.cpp b/src/core-impl/collections/sqlcollection/SqlCollection.cpp
index 0176fd3..f7a9ad8 100644
--- a/src/core-impl/collections/sqlcollection/SqlCollection.cpp
+++ b/src/core-impl/collections/sqlcollection/SqlCollection.cpp
@@ -73,17 +73,26 @@ SqlCollection::~SqlCollection()
 }
 
 void
-SqlCollection::init()
+SqlCollection::setUpdater( DatabaseUpdater *updater )
 {
-    QTimer::singleShot( 0, this, SLOT( initXesam() ) );
-    if( !m_updater )
+    DEBUG_BLOCK
+    if( !updater )
     {
         debug() << "Could not load updater!";
         return;
     }
-
+    m_updater = updater;
     if( m_updater->needsUpdate() )
+    {
+        debug() << "Needs update!";
         m_updater->update();
+    }
+}
+
+void
+SqlCollection::init()
+{
+    QTimer::singleShot( 0, this, SLOT( initXesam() ) );
 
     QStringList result = m_sqlStorage->query( "SELECT count(*) FROM tracks" );
     // If database version is updated, the collection needs to be rescanned.
diff --git a/src/core-impl/collections/sqlcollection/SqlCollection.h b/src/core-impl/collections/sqlcollection/SqlCollection.h
index da7e340..89fcdc0 100644
--- a/src/core-impl/collections/sqlcollection/SqlCollection.h
+++ b/src/core-impl/collections/sqlcollection/SqlCollection.h
@@ -105,7 +105,7 @@ class AMAROK_SQLCOLLECTION_EXPORT SqlCollection : public Collections::Collection
 
         void setSqlStorage( SqlStorage *storage ) { m_sqlStorage = storage; }
         void setRegistry( SqlRegistry *registry ) { m_registry = registry; }
-        void setUpdater( DatabaseUpdater *updater ) { m_updater = updater; }
+        void setUpdater( DatabaseUpdater *updater );
         void setCapabilityDelegate( Capabilities::CollectionCapabilityDelegate *delegate ) { m_capabilityDelegate = delegate; }
         void setCollectionLocationFactory( SqlCollectionLocationFactory *factory ) { m_collectionLocationFactory = factory; }
         void setQueryMakerFactory( SqlQueryMakerFactory *factory ) { m_queryMakerFactory = factory; }
diff --git a/src/core-impl/collections/sqlcollection/SqlCollectionFactory.cpp b/src/core-impl/collections/sqlcollection/SqlCollectionFactory.cpp
index c852111..a1966ac 100644
--- a/src/core-impl/collections/sqlcollection/SqlCollectionFactory.cpp
+++ b/src/core-impl/collections/sqlcollection/SqlCollectionFactory.cpp
@@ -160,13 +160,17 @@ SqlCollectionFactory::createSqlCollection( const QString &id, const QString &pre
     SqlCollection *coll = new SqlCollection( id, prettyName );
     coll->setCapabilityDelegate( new Capabilities::CollectionCapabilityDelegateImpl() );
 
-    MountPointManager *mpm = new MountPointManager( coll, storage );
-
-    coll->setMountPointManager( new SqlMountPointManagerImpl( mpm ) );
     DatabaseUpdater *updater = new DatabaseUpdater();
     updater->setStorage( storage );
     updater->setCollection( coll );
+
+    //setUpdater runs the update function; this must be run *before* MountPointManager is initialized or its handlers may try to insert
+    //into the database before it's created/updated!
     coll->setUpdater( updater );
+
+    MountPointManager *mpm = new MountPointManager( coll, storage );
+
+    coll->setMountPointManager( new SqlMountPointManagerImpl( mpm ) );
     ScanManager *scanMgr = new ScanManager( coll );
     scanMgr->setCollection( coll );
     scanMgr->setStorage( storage );
diff --git a/src/core-impl/collections/sqlcollection/device/massstorage/MassStorageDeviceHandler.cpp b/src/core-impl/collections/sqlcollection/device/massstorage/MassStorageDeviceHandler.cpp
index f0cc20a..eb4286f 100644
--- a/src/core-impl/collections/sqlcollection/device/massstorage/MassStorageDeviceHandler.cpp
+++ b/src/core-impl/collections/sqlcollection/device/massstorage/MassStorageDeviceHandler.cpp
@@ -39,6 +39,7 @@ MassStorageDeviceHandler::MassStorageDeviceHandler( int deviceId, const QString
     , m_mountPoint( mountPoint )
     , m_udi( udi )
 {
+    DEBUG_BLOCK
 }
 
 MassStorageDeviceHandler::~MassStorageDeviceHandler()
@@ -104,7 +105,19 @@ bool MassStorageDeviceHandlerFactory::canCreateFromConfig( ) const
 
 bool MassStorageDeviceHandlerFactory::canHandle( const Solid::Device &device ) const
 {
+    DEBUG_BLOCK
     const Solid::StorageVolume *volume = device.as<Solid::StorageVolume>();
+    if( !volume )
+    {
+        debug() << "found no volume";
+        return false;
+    }
+    if( volume->uuid().isEmpty() )
+        debug() << "has empty uuid";
+    if( volume->isIgnored() )
+        debug() << "volume is ignored";
+    if( excludedFilesystem( volume->fsType() ) )
+        debug() << "excluded filesystem of type " << volume->fsType();
     return volume && !volume->uuid().isEmpty()
            && !volume->isIgnored() && !excludedFilesystem( volume->fsType() );
 }
@@ -126,7 +139,10 @@ DeviceHandler * MassStorageDeviceHandlerFactory::createHandler( const Solid::Dev
 {
     DEBUG_BLOCK
     if( !s )
+    {
+        debug() << "!s, returning 0";
         return 0;
+    }
     const Solid::StorageVolume *volume = device.as<Solid::StorageVolume>();
     const Solid::StorageAccess *volumeAccess = device.as<Solid::StorageAccess>();
     if( !volume || !volumeAccess )
@@ -135,7 +151,10 @@ DeviceHandler * MassStorageDeviceHandlerFactory::createHandler( const Solid::Dev
         return 0;
     }
     if( volumeAccess->filePath().isEmpty() )
+    {
+        debug() << "not mounted, can't do anything";
         return 0; // It's not mounted, we can't do anything.
+    }
     QStringList ids = s->query( QString( "SELECT id, label, lastmountpoint "
                                          "FROM devices WHERE type = 'uuid' "
                                          "AND uuid = '%1';" ).arg( volume->uuid() ) );
Comment 41 Thomas Fjellstrom 2010-08-09 06:15:09 UTC
I've just done some limited testing, and "knock on wood" it seems to work! I tested with a SD card with the build in sd reader on my laptop (couldn't find a usb cable to test with my external HD). After adding the sd card mount path to amarok, removing and inserting the card would make the songs disappear and reappear.

Not likely related, but I did notice once I re-inserted the card amarok didn't re-scan the contents in the "removable" collection it auto populates. It only seemed to scan it the first time I inserted the card, now it just shows up with 0 tracks.
Comment 42 Jeff Mitchell 2010-08-09 13:15:18 UTC
Yeah, I noticed problems with that too. I don't know much about the mediadevice code, but that's an entirely separate issue -- Dynamic Collection pertains to the main Local Tracks collection.
Comment 43 Jeff Mitchell 2010-09-15 01:24:19 UTC
I'm going to close this since I believe all known problems are fixed in the upcoming 2.3.2. Please reopen (or preferably start a new bug) at some future point if there are issues in 2.3.2.
Comment 44 Myriam Schweingruber 2010-09-16 11:11:38 UTC
*** Bug 251423 has been marked as a duplicate of this bug. ***
Comment 45 Peter C. Ndikuwera 2011-03-03 16:23:57 UTC
Git commit d57eedc5563b3950446257f09d439aaa3b8c21ee by Peter C. Ndikuwera.
Committed on 03/03/2011 at 16:01.
Pushed by pndiku into branch 'master'.

Fix NFS & SMB/CIFS Remote collection

BUG: 249760
CCBUG: 232976
CCBUG: 171213
CCBUG: 187692

Using Max's Mass Storage Device code as a guide, this commit returns
network-based collections, shared over NFS & SMB/CIFS to the dynamic
collection architecture.

This should re-validate http://amarok.kde.org/wiki/Dynamic_Collection,
which some people have complained to longer applies.

Test it & break it!

M  +3    -3    src/core-impl/collections/db/sql/MountPointManager.cpp     
M  +2    -2    src/core-impl/collections/db/sql/device/CMakeLists.txt     
M  +14   -6    src/core-impl/collections/db/sql/device/nfs/CMakeLists.txt     
M  +77   -30   src/core-impl/collections/db/sql/device/nfs/NfsDeviceHandler.cpp     
M  +12   -8    src/core-impl/collections/db/sql/device/nfs/NfsDeviceHandler.h     
M  +14   -6    src/core-impl/collections/db/sql/device/smb/CMakeLists.txt     
M  +76   -32   src/core-impl/collections/db/sql/device/smb/SmbDeviceHandler.cpp     
M  +11   -7    src/core-impl/collections/db/sql/device/smb/SmbDeviceHandler.h     

http://commits.kde.org/amarok/d57eedc5563b3950446257f09d439aaa3b8c21ee