Bug 136759 - Synchronise Play Count and Last Played to iPod
Summary: Synchronise Play Count and Last Played to iPod
Alias: None
Product: amarok
Classification: Applications
Component: general (show other bugs)
Version: 1.4.4
Platform: Ubuntu Linux
: NOR wishlist
Target Milestone: ---
Assignee: Amarok Developers
Depends on:
Reported: 2006-11-03 08:58 UTC by Michael Gorven
Modified: 2006-11-29 14:27 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:

Synchronise Play Count and Last Played to iPod, and set Date Added and Date Modified on transfer (3.93 KB, patch)
2006-11-03 09:07 UTC, Michael Gorven

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Gorven 2006-11-03 08:58:41 UTC
Version:           1.4.4 (using KDE KDE 3.5.5)
Installed from:    Ubuntu Packages
OS:                Linux

I discovered that I could create live updating smart playlists on my iPod using gtkpod. However, my "Recently Unplayed" smart playlist didn't work as expected, and after some investigation I found that Amarok doesn't synchronise the Last Played time to the iPod. So I fixed it :-)

My patch synchronises Play Counts and Last Played timestamps to the iPod. This is done in the same place where Ratings are synchronised, and so is enabled/disabled with the "Synchronize with Amarok statistics" option. It also sets the "Date Added" and "Date Modified" timestamps when transferring files to the iPod. There are a few issues however, which I will describe with the patch.
Comment 1 Michael Gorven 2006-11-03 09:07:27 UTC
Created attachment 18374 [details]
Synchronise Play Count and Last Played to iPod, and set Date Added and Date Modified on transfer

There are two issues with my patch. The first is that I was unsure about this
code, found in IpodMediaDevice::setRating().

if( dynamic_cast<IpodMediaDevice *>(device()) )
    static_cast<IpodMediaDevice *>(device())->m_dbChanged = true;

It seems to mark the database as changed so that it gets written when the iPod
is disconnected. This happens regardless though, because
IpodMediaDevice::writeITunesDB() always sets dbChanged to true. In case it does
something I don't understand, I've added it to my setPlayCount() and
setLastPlayed() methods.

The second issue is that I can't find a nice way to get "modifydate" from the
database. I've used QueryBuilder to get the "createdate" for tracks, but it
doesn't have a field for "modifydate". So I just set both "time_added" and
"time_modified" to the "createdate".
Comment 2 Martin Aumueller 2006-11-28 23:18:31 UTC
SVN commit 608923 by aumuell:

synchronize play count, last played time and date of modification to ipods
- thanks to Michael <michael003@gmail.com> for the patch
BUG: 136759

 M  +2 -0      ChangeLog  
 M  +3 -1      src/mediabrowser.cpp  
 M  +2 -0      src/mediabrowser.h  
 M  +29 -0     src/mediadevice/ipod/ipodmediadevice.cpp  

--- trunk/extragear/multimedia/amarok/ChangeLog #608922:608923
@@ -5,6 +5,8 @@
 VERSION 1.4.5
+    * Synchronize play count, last played time and date of modification to
+      iPods. Patch by Michael <michael003@gmail.com>. (BR 136759)
     * Propose list of composers in collection when editing the composer tag
       from the playlist. (BR 137775)
     * Greatly improved sound quality for the xine equalizer. Patch by Tobias
--- trunk/extragear/multimedia/amarok/src/mediabrowser.cpp #608922:608923
@@ -2823,9 +2823,11 @@
                 if( url != QString::null )
-                    // copy Amarok rating to device
+                    // copy Amarok rating, play count and last played time to device
                     int rating = CollectionDB::instance()->getSongRating( url )*10;
                     it->setRating( rating );
+                    it->setPlayCount( CollectionDB::instance()->getPlayCount( url ) );
+                    it->setLastPlayed( CollectionDB::instance()->getLastPlay( url ).toTime_t() );
--- trunk/extragear/multimedia/amarok/src/mediabrowser.h #608922:608923
@@ -76,9 +76,11 @@
         virtual QDateTime playTime()  const { return QDateTime(); }
         virtual int  played()         const { return 0; }
         virtual int  recentlyPlayed() const { return 0; } // no of times played on device since last sync
+        virtual void setPlayCount( int ) {}
         virtual int  rating()         const { return 0; } // rating on device, normalized to 100
         virtual void setRating( int /*rating*/ ) {}
         virtual bool ratingChanged()  const { return false; }
+        virtual void setLastPlayed( uint ) {}
         virtual long size()           const;
         virtual MediaDevice *device() const { return m_device; }
         virtual bool listened()       const { return m_listened; }
--- trunk/extragear/multimedia/amarok/src/mediadevice/ipod/ipodmediadevice.cpp #608922:608923
@@ -164,6 +164,22 @@
                 static_cast<IpodMediaDevice *>(device())->m_dbChanged = true;
+        void setPlayCount( int playcount )
+        {
+            if ( m_track )
+                m_track->playcount = playcount;
+            if( dynamic_cast<IpodMediaDevice *>(device()) )
+                static_cast<IpodMediaDevice *>(device())->m_dbChanged = true;
+        }
+        void setLastPlayed( uint lastplay )
+        {
+            if ( m_track )
+                m_track->time_played = itdb_time_host_to_mac( lastplay );
+            if( dynamic_cast<IpodMediaDevice *>(device()) )
+                static_cast<IpodMediaDevice *>(device())->m_dbChanged = true;
+        }
         bool ratingChanged() const { return m_track ? m_track->rating != m_track->app_rating : false; }
         void setListened( bool l )
@@ -387,6 +403,19 @@
     track->samplerate = propertiesBundle.sampleRate();
     track->tracklen = propertiesBundle.length()*1000;
+    //Get the createdate from database
+    QueryBuilder qb;
+    qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valCreateDate );
+    qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valURL, metaBundle.url().path() );
+    QStringList values = qb.run();
+    //Add to track info if present
+    if ( values.count() ) {
+        uint createdate = values.first().toUInt();
+        track->time_added = itdb_time_host_to_mac( createdate );
+        track->time_modified = itdb_time_host_to_mac( createdate );
+    }
Comment 3 Tobias Knieper 2006-11-29 10:13:28 UTC
Does this mean that the changed statistics are always copied to the iPod even if the play count on the iPod is higher?
Comment 4 Martin Aumueller 2006-11-29 10:49:51 UTC
This will happen: but if you always sync your ipod to the same computer, then all plays have been synced to your computer and the play count on your computer is at least as high as on your ipod.
Comment 5 Michael Gorven 2006-11-29 14:27:00 UTC
There are actually two play count values on the iPod. The first is the total play count, which is stored in the "iTunesDB" file. This value is not modified by the iPod (the iPod does not change the "iTunesDB" file at all). The second value is the play count since the last sync. This value is stored in the "Play Counts" file and is reset to 0 when the iPod is synchronised.

When the iPod is synchronised Amarok adds the play count from the "Play Counts" file (i.e. the number of times the track was played on the iPod since the last sync) to it's own play count. My patch makes Amarok write the play count (the updated total play count) to the "iTunesDB" file.