Index: mythplugins/mythmusic/mythmusic/playbackbox.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/playbackbox.cpp	(working copy)
@@ -1214,12 +1214,12 @@
     gPlayer->getOutput()->ToggleUpmix();
     gPlayer->getOutput()->SetTimecode(currentTime * 1000);
 }
-    
 
+
 void PlaybackBoxMusic::showProgressBar()
 {
     if (progress_bar && visualizer_status != 2)
-    {       
+    {
         int percentplayed = 1;
         if (maxTime)
             percentplayed = (int)(((double)currentTime / (double)maxTime)*100);
@@ -1562,7 +1562,7 @@
             }
 
         }
-        
+
         gPlayer->getOutput()->SetTimecode(pos*1000);
 
         if (!gPlayer->isPlaying())
@@ -1774,7 +1774,7 @@
             // sanity check - if we are not in 'show whole tree' mode we
             // should only restore the position if it points to a track
             // in the active play queue
-            if (branches_to_current_node.size() >= 3 && 
+            if (branches_to_current_node.size() >= 3 &&
                 branches_to_current_node[0] == 0 &&
                 branches_to_current_node[1] == 1 &&
                 branches_to_current_node[2] == 0)
@@ -1946,7 +1946,7 @@
         {
             if (class LCD *lcd = LCD::Get())
             {
-                float percent_heard = (maxTime<=0) ? 
+                float percent_heard = (maxTime<=0) ?
                     0.0:((float)rs / (float)curMeta->Length()) * 1000.0;
 
                 QString lcd_time_string = time_string;
Index: mythplugins/mythmusic/mythmusic/decoder.h
===================================================================
--- mythplugins/mythmusic/mythmusic/decoder.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/decoder.h	(working copy)
@@ -93,6 +93,7 @@
     virtual Metadata *getMetadata(void);
     virtual MetaIO *doCreateTagger (void);
     virtual void commitMetadata(Metadata *mdata);
+    virtual void commitVolatileMetadata(const Metadata *mdata);
 
     // static methods
     static QStringList all();
Index: mythplugins/mythmusic/mythmusic/metadata.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/metadata.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metadata.cpp	(working copy)
@@ -87,8 +87,11 @@
     Metadata::m_startdir = dir;
 }
 
-void Metadata::persist()
+void Metadata::persist() const
 {
+    if (m_id < 1)
+        return;
+
     MSqlQuery query(MSqlQuery::InitCon());
     query.prepare("UPDATE music_songs set rating = :RATING , "
                   "numplays = :PLAYCOUNT , lastplay = :LASTPLAY "
@@ -102,6 +105,25 @@
         MythDB::DBError("music persist", query);
 }
 
+
+void Metadata::UpdateModTime() const
+{
+    if (m_id < 1)
+        return;
+
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare("UPDATE music_songs SET date_modified = :DATE_MOD "
+                  "WHERE song_id= :ID ;");
+
+    query.bindValue(":DATE_MOD", QDateTime::currentDateTime());
+    query.bindValue(":ID", m_id);
+
+    if (!query.exec())
+        MythDB::DBError("Metadata::UpdateModTime",
+                        query);
+}
+
 int Metadata::compare(const Metadata *other) const
 {
     if (m_format == "cast")
Index: mythplugins/mythmusic/mythmusic/metaio.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metaio.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metaio.h	(working copy)
@@ -16,7 +16,7 @@
     virtual ~MetaIO(void);
 
     /*!
-    * \brief Writes metadata back to a file
+    * \brief Writes all metadata back to a file
     *
     * \param mdata A pointer to a Metadata object
     * \returns Boolean to indicate success/failure.
@@ -24,6 +24,19 @@
     virtual bool write(Metadata* mdata) = 0;
 
     /*!
+    * \brief Writes rating and playcount back to a file
+    *
+    * \param rating Integer between 0 and 10 representing the rating given to
+    *               this file by the user
+    * \returns Boolean to indicate success/failure.
+    */
+    virtual bool writeVolatileMetadata(const Metadata* mdata)
+    {
+        (void)mdata;
+        return false;
+    }
+
+    /*!
     * \brief Reads Metadata from a file.
     *
     * \param filename The filename to read metadata from.
@@ -31,13 +44,13 @@
     */
     virtual Metadata* read(QString filename) = 0;
 
-    void readFromFilename(QString filename, QString &artist, QString &album, 
+    void readFromFilename(QString filename, QString &artist, QString &album,
                           QString &title, QString &genre, int &tracknum);
 
     Metadata* readFromFilename(QString filename, bool blnLength = false);
 
     void readFromFilename(Metadata *metadata);
-    
+
   protected:
 
   private:
Index: mythplugins/mythmusic/mythmusic/decoder.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/decoder.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/decoder.cpp	(working copy)
@@ -156,6 +156,31 @@
     }
 }
 
+/**
+ *  \brief Write the changable metadata, e.g. ratings, playcounts; to the
+ *         \p filename if the tag format supports it.
+ *
+ *  Creates a \p MetaIO object using \p Decoder::doCreateTagger and
+ *  asks the MetaIO object to write changes to a specific subset of metadata
+ *  to \p filename.
+ *
+ *  \params mdata the metadata to write to the disk
+ */
+void Decoder::commitVolatileMetadata(const Metadata *mdata)
+{
+    if (!mdata)
+        return;
+
+    MetaIO* p_tagger = doCreateTagger();
+    if (p_tagger)
+    {
+        p_tagger->writeVolatileMetadata(mdata);
+        delete p_tagger;
+    }
+
+    mdata->UpdateModTime();
+}
+
 // static methods
 
 int Decoder::ignore_id3 = 0;
Index: mythplugins/mythmusic/mythmusic/metadata.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metadata.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metadata.h	(working copy)
@@ -181,7 +181,8 @@
     void getField(const QString& field, QString *data);
     void toMap(MetadataMap &metadataMap);
 
-    void persist();
+    void persist(void) const;
+    void UpdateModTime(void) const;
     bool hasChanged() {return m_changed;}
     int compare(const Metadata *other) const;
     static void setArtistAndTrackFormats();
Index: mythplugins/mythmusic/mythmusic/editmetadata.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/editmetadata.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/editmetadata.cpp	(working copy)
@@ -660,6 +660,7 @@
 
     m_metadata->dumpToDatabase();
     *m_sourceMetadata = m_metadata;
+
     accept();
 }
 
Index: mythplugins/mythmusic/mythmusic/metaiotaglib.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metaiotaglib.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metaiotaglib.h	(working copy)
@@ -26,7 +26,7 @@
 
     virtual bool write(Metadata* mdata) = 0;
     virtual Metadata* read(QString filename) = 0;
-    
+
   protected:
     int getTrackLength(TagLib::FileRef *file);
     int getTrackLength(QString filename);
Index: mythplugins/mythmusic/mythmusic/metaioid3.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/metaioid3.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metaioid3.cpp	(working copy)
@@ -4,7 +4,10 @@
 
 // Libmyth
 #include <mythverbose.h>
+#include <set>
 
+const String email = "music@mythtv.org";  // TODO username/ip/hostname?
+
 MetaIOID3::MetaIOID3(void)
     : MetaIOTagLib()
 {
@@ -24,7 +27,7 @@
 {
     QByteArray fname = filename.toLocal8Bit();
     TagLib::MPEG::File *mpegfile = new TagLib::MPEG::File(fname.constData());
-    
+
     if (!mpegfile->isOpen())
     {
         delete mpegfile;
@@ -43,7 +46,7 @@
 
     if (!mpegfile)
         return false;
-    
+
     TagLib::ID3v2::Tag *tag = mpegfile->ID3v2Tag();
 
     if (!tag)
@@ -51,29 +54,27 @@
         delete mpegfile;
         return false;
     }
-    
+
     WriteGenericMetadata(tag, mdata);
-    
-    if (mdata->Rating() > 0 || mdata->PlayCount() > 0)
-    {
-        // Needs to be implemented for taglib by subclassing ID3v2::Frames
-        // with one to handle POPM frames
-    }
-    
+
+    // MythTV rating and playcount, stored in POPM frame
+    writeRating(tag, mdata->Rating());
+    writePlayCount(tag, mdata->PlayCount());
+
     // MusicBrainz ID
     UserTextIdentificationFrame *musicbrainz = NULL;
     musicbrainz = find(tag, "MusicBrainz Album Artist Id");
 
     if (mdata->Compilation())
     {
-        
+
         if (!musicbrainz)
         {
             musicbrainz = new UserTextIdentificationFrame(TagLib::String::UTF8);
             tag->addFrame(musicbrainz);
             musicbrainz->setDescription("MusicBrainz Album Artist Id");
         }
-        
+
         musicbrainz->setText(MYTH_MUSICBRAINZ_ALBUMARTIST_UUID);
     }
     else if (musicbrainz)
@@ -86,7 +87,7 @@
         TagLib::ID3v2::FrameList tpelist = tag->frameListMap()["TPE4"];
         if (!tpelist.isEmpty())
             tpe4frame = (TextIdentificationFrame *)tpelist.front();
-        
+
         if (!tpe4frame)
         {
             tpe4frame = new TextIdentificationFrame(TagLib::ByteVector("TPE4"),
@@ -95,7 +96,7 @@
         }
         tpe4frame->setText(QStringToTString(mdata->CompilationArtist()));
 
-        
+
         TextIdentificationFrame *tpe2frame = NULL;
         tpelist = tag->frameListMap()["TPE2"];
         if (!tpelist.isEmpty())
@@ -109,11 +110,11 @@
         }
         tpe2frame->setText(QStringToTString(mdata->CompilationArtist()));
     }
-    
+
     bool result = mpegfile->save();
 
     delete mpegfile;
-    
+
     return result;
 }
 
@@ -123,7 +124,7 @@
 Metadata *MetaIOID3::read(QString filename)
 {
     TagLib::MPEG::File *mpegfile = OpenFile(filename);
-    
+
     if (!mpegfile)
         return NULL;
 
@@ -134,9 +135,9 @@
         delete mpegfile;
         return NULL;
     }
-    
+
     Metadata *metadata = new Metadata(filename);
-    
+
     ReadGenericMetadata(tag, metadata);
 
     bool compilation = false;
@@ -151,7 +152,7 @@
         tpelist = tag->frameListMap()["TPE2"];
     if (!tpelist.isEmpty())
         tpeframe = (TextIdentificationFrame *)tpelist.front();
-    
+
     if (tpeframe && !tpeframe->toString().isEmpty())
     {
         QString compilation_artist = TStringToQString(tpeframe->toString())
@@ -159,6 +160,25 @@
         metadata->setCompilationArtist(compilation_artist);
     }
 
+    // MythTV rating and playcount, stored in POPM frame
+    PopularimeterFrame *popm = findPOPM(tag, email);
+
+    if (!popm)
+    {
+        if (!tag->frameListMap()["POPM"].isEmpty())
+            popm = dynamic_cast<PopularimeterFrame *>
+                                        (tag->frameListMap()["POPM"].front());
+    }
+
+    if (popm)
+    {
+        int rating = popm->rating();
+        rating = static_cast<int>(((static_cast<float>(rating)/255.0)
+                                                                * 10.0) + 0.5);
+        metadata->setRating(rating);
+        metadata->setPlaycount(popm->counter());
+    }
+
     // Look for MusicBrainz Album+Artist ID in TXXX Frame
     UserTextIdentificationFrame *musicbrainz = find(tag,
                                             "MusicBrainz Album Artist Id");
@@ -173,7 +193,7 @@
     }
 
     // Length
-    if (!mpegfile->ID3v2Tag()->frameListMap()["TLEN"].isEmpty())
+    if (!tag->frameListMap()["TLEN"].isEmpty())
     {
         int length = tag->frameListMap()["TLEN"].front()->toString().toInt();
         metadata->setLength(length);
@@ -199,7 +219,7 @@
     }
     else
         delete mpegfile;
-    
+
     return metadata;
 }
 
@@ -237,7 +257,7 @@
         default:
             return picture;
     }
-    
+
     QByteArray fname = filename.toLocal8Bit();
     TagLib::MPEG::File *mpegfile = new TagLib::MPEG::File(fname.constData());
 
@@ -364,5 +384,93 @@
     if (f && f->description() == description)
       return f;
   }
-  return 0;
+  return NULL;
 }
+
+/*!
+ * \brief Find the POPM tag associated with MythTV
+ *        This is a copy of the same function in the
+ *        TagLib::ID3v2::UserTextIdentificationFrame Class with a static
+ *        instead of dynamic cast.
+ *
+ * \param tag Pointer to TagLib::ID3v2::Tag object
+ * \param email Email address associated with this POPM frame
+ * \returns Pointer to frame
+ */
+PopularimeterFrame* MetaIOID3::findPOPM(TagLib::ID3v2::Tag *tag,
+                                        const String &email)
+{
+  TagLib::ID3v2::FrameList l = tag->frameList("POPM");
+  for(TagLib::ID3v2::FrameList::Iterator it = l.begin(); it != l.end(); ++it)
+  {
+    PopularimeterFrame *f = static_cast<PopularimeterFrame *>(*it);
+    if (f && f->email() == email)
+      return f;
+  }
+  return NULL;
+}
+
+bool MetaIOID3::writePlayCount(TagLib::ID3v2::Tag *tag, int playcount)
+{
+    if (!tag)
+        return false;
+
+    PopularimeterFrame *popm = findPOPM(tag, email);
+
+    if (!popm)
+    {
+        popm = new PopularimeterFrame();
+        tag->addFrame(popm);
+        popm->setEmail(email);
+    }
+
+    popm->setCounter(playcount);
+
+    return true;
+}
+
+bool MetaIOID3::writeVolatileMetadata(const Metadata* mdata)
+{
+    QString filename = mdata->Filename();
+    int rating = mdata->Rating();
+    int playcount = mdata->PlayCount();
+    TagLib::MPEG::File *mpegfile = OpenFile(filename);
+
+    if (!mpegfile)
+        return false;
+
+    TagLib::ID3v2::Tag *tag = mpegfile->ID3v2Tag();
+
+    if (!tag)
+    {
+        delete mpegfile;
+        return false;
+    }
+
+    bool result = (writeRating(tag, rating) && writePlayCount(tag, playcount));
+
+    mpegfile->save();
+    delete mpegfile;
+
+    return result;
+}
+
+bool MetaIOID3::writeRating(TagLib::ID3v2::Tag *tag, int rating)
+{
+    if (!tag)
+        return false;
+
+    PopularimeterFrame *popm = findPOPM(tag, email);
+
+    if (!popm)
+    {
+        popm = new PopularimeterFrame();
+        tag->addFrame(popm);
+        popm->setEmail(email);
+    }
+    int popmrating = static_cast<int>(((static_cast<float>(rating) / 10.0)
+                                                               * 255.0) + 0.5);
+    popm->setRating(popmrating);
+
+    return true;
+}
Index: mythplugins/mythmusic/mythmusic/musicplayer.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/musicplayer.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/musicplayer.cpp	(working copy)
@@ -166,7 +166,7 @@
             m_output->addListener(visual);
             m_output->addVisual(visual);
         }
- 
+
         m_visualisers.insert(visual);
     }
 }
@@ -355,7 +355,10 @@
     if (m_currentMetadata)
     {
         if (m_currentMetadata->hasChanged())
+        {
             m_currentMetadata->persist();
+            m_decoder->commitVolatileMetadata(m_currentMetadata);
+        }
         delete m_currentMetadata;
     }
     m_currentMetadata = NULL;
@@ -768,12 +771,12 @@
     {
         m_currentTrack = position;
     }
-    
+
     m_currentFile.clear();
     Track *track = m_currentPlaylist->getSongAt(m_currentTrack);
     if (track)
         m_currentFile = getFilenameFromID(track->getValue());
-    
+
     if (!m_currentFile.isEmpty())
         play();
 }
@@ -989,7 +992,7 @@
 
 void MusicPlayer::toMap(QHash<QString, QString> &map)
 {
-    map["volumemute"] = QString("%1%").arg(getVolume()) + 
+    map["volumemute"] = QString("%1%").arg(getVolume()) +
                         (isMuted() ? " (" + tr("Muted") + ")" : "");
     map["volume"] = QString("%1").arg(getVolume());
     map["volumepercent"] = QString("%1%").arg(getVolume());
Index: mythplugins/mythmusic/mythmusic/musiccommon.h
===================================================================
--- mythplugins/mythmusic/mythmusic/musiccommon.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/musiccommon.h	(working copy)
@@ -51,8 +51,7 @@
     void seekback(void);
     void seek(int);
     void stopAll(void);
-    void increaseRating(void);
-    void decreaseRating(void);
+    void changeRating(bool increase);
 
     void showViewMenu(void);
     void showPlaylistMenu(void);
Index: mythplugins/mythmusic/mythmusic/metaioavfcomment.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metaioavfcomment.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metaioavfcomment.h	(working copy)
@@ -21,10 +21,10 @@
 public:
     MetaIOAVFComment(void);
     virtual ~MetaIOAVFComment(void);
-    
+
     bool write(Metadata* mdata);
     Metadata* read(QString filename);
-    
+
 private:
     int getTrackLength(QString filename);
     int getTrackLength(AVFormatContext* p_context);
Index: mythplugins/mythmusic/mythmusic/metaioid3.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metaioid3.h	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/metaioid3.h	(working copy)
@@ -9,6 +9,7 @@
 #include <id3v2tag.h>
 #include <textidentificationframe.h>
 #include <attachedpictureframe.h>
+#include <popularimeterframe.h>
 #include <mpegproperties.h>
 #include <mpegfile.h>
 #include <tfile.h>
@@ -18,6 +19,7 @@
 
 using TagLib::ID3v2::UserTextIdentificationFrame;
 using TagLib::ID3v2::TextIdentificationFrame;
+using TagLib::ID3v2::PopularimeterFrame;
 using TagLib::ID3v2::AttachedPictureFrame;
 using TagLib::MPEG::Properties;
 
@@ -37,8 +39,13 @@
   public:
     MetaIOID3(void);
     virtual ~MetaIOID3(void);
-    
+
     bool write(Metadata* mdata);
+    bool writeVolatileMetadata(const Metadata* mdata);
+
+    bool writePlayCount(TagLib::ID3v2::Tag *tag, int playcount);
+    bool writeRating(TagLib::ID3v2::Tag *tag, int rating);
+
     Metadata* read(QString filename);
     static QImage getAlbumArt(QString filename, ImageType type);
 
@@ -47,6 +54,7 @@
 
     AlbumArtList readAlbumArt(TagLib::ID3v2::Tag *tag);
     UserTextIdentificationFrame* find(TagLib::ID3v2::Tag *tag, const String &description);
+    PopularimeterFrame* findPOPM(TagLib::ID3v2::Tag *tag, const String &email);
 };
 
 #endif
Index: mythplugins/mythmusic/mythmusic/musiccommon.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/musiccommon.cpp	(revision 25163)
+++ mythplugins/mythmusic/mythmusic/musiccommon.cpp	(working copy)
@@ -191,9 +191,9 @@
 
     if (m_currentPlaylist)
     {
-        connect(m_currentPlaylist, SIGNAL(itemClicked(MythUIButtonListItem*)), 
+        connect(m_currentPlaylist, SIGNAL(itemClicked(MythUIButtonListItem*)),
                 this, SLOT(playlistItemClicked(MythUIButtonListItem*)));
-        connect(m_currentPlaylist, SIGNAL(itemSelected(MythUIButtonListItem*)), 
+        connect(m_currentPlaylist, SIGNAL(itemSelected(MythUIButtonListItem*)),
                 this, SLOT(playlistItemSelected(MythUIButtonListItem*)));
 
         updateUIPlaylist();
@@ -413,9 +413,9 @@
             }
         }
         else if (action == "THMBUP")
-            increaseRating();
+            changeRating(true);
         else if (action == "THMBDOWN")
-            decreaseRating();
+            changeRating(false);
         else if (action == "NEXTTRACK")
         {
             if (m_nextButton)
@@ -825,48 +825,33 @@
     }
 }
 
-void MusicCommon::increaseRating(void)
+void MusicCommon::changeRating(bool increase)
 {
-    Metadata *curMeta = gPlayer->getCurrentMetadata();
-
-    if (!curMeta)
+    // Rationale here is that if you can't get visual feedback on ratings
+    // adjustments, you probably should not be changing them
+    if (!m_ratingState)
         return;
 
-    if (m_ratingState)
-    {
-        curMeta->incRating();
-        curMeta->persist();
-        m_ratingState->DisplayState(QString("%1").arg(curMeta->Rating()));
-
-        // if all_music is still in scope we need to keep that in sync
-        if (gMusicData->all_music)
-        {
-            Metadata *mdata = gMusicData->all_music->getMetadata(curMeta->ID());
-            if (mdata)
-                mdata->incRating();
-        }
-    }
-}
-
-void MusicCommon::decreaseRating(void)
-{
     Metadata *curMeta = gPlayer->getCurrentMetadata();
 
     if (!curMeta)
         return;
 
-    if (m_ratingState)
-    {
+    if (increase)
+        curMeta->incRating();
+    else
         curMeta->decRating();
-        curMeta->persist();
-        m_ratingState->DisplayState(QString("%1").arg(curMeta->Rating()));
 
-        // if all_music is still in scope we need to keep that in sync
-        if (gMusicData->all_music)
+    curMeta->persist(); // incRating() already triggers a persist on track changes - redundant?
+    m_ratingState->DisplayState(QString("%1").arg(curMeta->Rating()));
+
+    // if all_music is still in scope we need to keep that in sync
+    if (gMusicData->all_music)
+    {
+        Metadata *mdata = gMusicData->all_music->getMetadata(curMeta->ID());
+        if (mdata)
         {
-            Metadata *mdata = gMusicData->all_music->getMetadata(curMeta->ID());
-            if (mdata)
-                mdata->decRating();
+            mdata->setRating(curMeta->Rating());
         }
     }
 }
@@ -1256,7 +1241,7 @@
     MetadataMap metadataMap;
     mdata->toMap(metadataMap);
     SetTextFromMap(metadataMap);
-    
+
     m_maxTime = mdata->Length() / 1000;
     if (m_coverartImage)
     {
@@ -1383,7 +1368,7 @@
         Metadata *mdata = gMusicData->all_music->getMetadata(trackid);
         if (mdata)
         {
-            MythUIButtonListItem *item = 
+            MythUIButtonListItem *item =
                 new MythUIButtonListItem(m_currentPlaylist, "", qVariantFromValue(mdata));
 
             MetadataMap metadataMap;
