[mythtv] MythMusic Feature Attempt

m0j0.j0j0 m0j0 at foofus.net
Wed Feb 26 09:10:07 EST 2003


> Looks fun, if you'd like to make more changes to it, I'd be glad to include it 
> whenever you think it's ready.
> 
> Isaac
> 

OK, I think I have my "intelligent playlist" in a more usable form. I
figured out my previous attempt didn't do anything like what I thought
it did. This one seems to work a bit better. Again, I make no claims to
be a programmer, so any comments/critiques/suggestions/changes are
welcome. This was mainly a learning exercise for me.

This patch is against CVS MythMusic from Monday 1/24 (~2:30PM).

What it does:

-Adds Thumbs UP/Thumbs Down icons for rating the currently playing song
-Adds rating column which displays the user assigned rating for each
song in the playlist
-Stores the song rating/last play time/number of times played to the DB
-Generates a playlist based on the above values plus a little randomness

Current issues:

-If the user starts hitting next or previous quickly through the list,
it seems that the "last play" SQL update doesn't finish and a value of 0
gets put in the DB. This causes issues when the value is read back in.
Is there an easy way to lock the interface until the SQL update
finishes?

Hope this is useful.
-j 


-------------- next part --------------
--- mythmusic/playbackbox.cpp.orig	Mon Feb 24 14:48:50 2003
+++ mythmusic/playbackbox.cpp	Tue Feb 25 23:12:56 2003
@@ -38,6 +38,8 @@
 #include "res/prevfile.xpm"
 #include "res/prev.xpm"
 #include "res/stop.xpm"
+#include "res/rateup.xpm"
+#include "res/ratedn.xpm"
 
 PlaybackBox::PlaybackBox(QSqlDatabase *ldb, QValueList<Metadata> *playlist,
                          QWidget *parent, const char *name)
@@ -125,6 +127,16 @@
     nextfileb->setIconSet(scalePixmap((const char **)nextfile_pix));
     connect(nextfileb, SIGNAL(clicked()), this, SLOT(next()));    
 
+    MythToolButton *rateup = new MythToolButton(this);
+    rateup->setAutoRaise(true);
+    rateup->setIconSet(scalePixmap((const char **)rateup_pix));
+    connect(rateup, SIGNAL(clicked()), this, SLOT(toggleRatingUp()));
+
+    MythToolButton *ratedn = new MythToolButton(this);
+    ratedn->setAutoRaise(true);
+    ratedn->setIconSet(scalePixmap((const char **)ratedn_pix));
+    connect(ratedn, SIGNAL(clicked()), this, SLOT(toggleRatingDn()));
+	 
     controlbox->addWidget(prevfileb);
     controlbox->addWidget(prevb);
     controlbox->addWidget(pauseb);
@@ -132,6 +144,8 @@
     controlbox->addWidget(stopb);
     controlbox->addWidget(nextb);
     controlbox->addWidget(nextfileb);
+    controlbox->addWidget(rateup);
+    controlbox->addWidget(ratedn);
 
     QHBoxLayout *secondcontrol = new QHBoxLayout(vbox2, (int)(2 * wmult));
 
@@ -188,6 +202,10 @@
         nextb->setFocusPolicy( QWidget::NoFocus);
         nextfileb->setAccel(Key_Down);
         nextfileb->setFocusPolicy( QWidget::NoFocus);
+        rateup->setAccel(Key_U);
+        rateup->setFocusPolicy( QWidget::NoFocus);
+        ratedn->setAccel(Key_D);
+        ratedn->setFocusPolicy( QWidget::NoFocus);
 		
         randomize->setAccel(Key_1);
         randomize->setFocusPolicy( QWidget::NoFocus);
@@ -201,6 +219,7 @@
 
     playview = new MythListView(this);
     playview->addColumn("#");
+    playview->addColumn("Rating");  
     playview->addColumn("Artist");  
     playview->addColumn("Title");
     playview->addColumn("Length");
@@ -209,15 +228,17 @@
     playview->setFocusPolicy(NoFocus);
 
     playview->setColumnWidth(0, (int)(50 * wmult));
-    playview->setColumnWidth(1, (int)(210 * wmult));
-    playview->setColumnWidth(2, (int)(385 * wmult));
-    playview->setColumnWidth(3, (int)(90 * wmult));
+    playview->setColumnWidth(1, (int)(60 * wmult));
+    playview->setColumnWidth(2, (int)(195 * wmult));
+    playview->setColumnWidth(3, (int)(355 * wmult));
+    playview->setColumnWidth(4, (int)(80 * wmult));
     playview->setColumnWidthMode(0, QListView::Manual);
     playview->setColumnWidthMode(1, QListView::Manual);
     playview->setColumnWidthMode(2, QListView::Manual);
     playview->setColumnWidthMode(3, QListView::Manual);
+    playview->setColumnWidthMode(4, QListView::Manual);
 
-    playview->setColumnAlignment(3, Qt::AlignRight);
+    playview->setColumnAlignment(4, Qt::AlignRight);
 
     playview->setSorting(-1);
     playview->setAllColumnsShowFocus(true);
@@ -237,7 +258,7 @@
     input = 0; decoder = 0; seeking = false; remainingTime = false;
     output = 0; outputBufferSize = 256;
 
-    shufflemode = false;
+    shufflemode = 0;
     repeatmode = false;  
 
     curMeta = Metadata("dummy.music");
@@ -245,6 +266,11 @@
     QString playmode = gContext->GetSetting("PlayMode");
     if (playmode.lower() == "random")
         toggleShuffle();
+    else if (playmode.lower() == "intelligent")
+    {
+        shufflemode++; 
+        toggleShuffle();
+    }					 
     else
         setupPlaylist();
 
@@ -291,6 +317,8 @@
         connect(repeat, SIGNAL(clicked()), this, SLOT(resetTimer()));
         connect(pledit, SIGNAL(clicked()), this, SLOT(resetTimer()));
         connect(vis, SIGNAL(clicked()), this, SLOT(resetTimer()));
+        connect(rateup, SIGNAL(clicked()), this, SLOT(resetTimer()));
+        connect(ratedn, SIGNAL(clicked()), this, SLOT(resetTimer()));
     }
 
     visualizer_is_active = false;
@@ -353,7 +381,11 @@
         QString timestr;
         timestr.sprintf("%2d:%02d", min, secs);
         
-        litem = new QListViewItem(playview, position, (*it).Artist(),
+        QString rating;
+        for (int i = 0; i < (*it).Rating(); i++)
+            rating.append("|");
+		  
+        litem = new QListViewItem(playview, position, rating, (*it).Artist(),
                                   (*it).Title(), timestr);
         listlist.prepend(litem);
         it--; count--;
@@ -401,7 +433,7 @@
         return;
     }
 
-    if (!shufflemode)
+    if (shufflemode == 0)
     {
         for (int i = 0; i < (int)plist->size(); i++)
         {
@@ -415,32 +447,57 @@
         int max = plist->size();
         srand((unsigned int)time(NULL));
 
-        int i;
-        bool usedList[max];
-        for (i = 0; i < max; i++)
-            usedList[i] = false;
+        int i, j, temp;
+		  double tempd;
+        int rating;
+        int playcount;
+        double lastplay;
+        QDateTime cTime = QDateTime::currentDateTime();
+        double currentDateTime = atof(cTime.toString("yyyyMMddhhmmss").ascii());
+        double ratingValue = 0;
+        double playcountValue = 0;
+        double lastplayValue = 0;
+        double weight;
 
-        int index = 0; 
-        int lastindex = 0;
+        for (i = 0; i < max; i++)
+            playlistweight.push_back(0);
 
         for (i = 0; i < max; i++)
         {
-            while (1)
+            if (shufflemode == 2) {
+                curMeta = (*plist)[i];
+                rating = curMeta.Rating();
+                playcount = curMeta.PlayCount();
+                lastplay = curMeta.LastPlay();
+                ratingValue = (double)rating / 10;
+                playcountValue =  (double)playcount / 50;
+                lastplayValue = (currentDateTime - lastplay) / currentDateTime * 2000;
+
+                weight = ( 35 * ratingValue - 25 * playcountValue + 25 * lastplayValue + 
+                           15 * (double)rand() / (RAND_MAX + 1.0));
+            }
+            else
+                weight = (int)((double)rand() / (RAND_MAX + 1.0) * max);
+
+            playlistweight[i] = weight;
+            playlistorder.push_back(i);
+        }
+
+        for (i = 0; i < (max - 1); i++)
+        {
+            for (j = i + 1; j < max; j++)
+            {
+                if (playlistweight[j] > playlistweight[i])
             {
-                index = (int)((double)rand() / (RAND_MAX + 1.0) * max);
-                if (max - i > 50 && abs(index - lastindex) < 10)
-                    continue;
-                if (usedList[index] == false)
-                    break;
-            }
-            usedList[index] = true;
-            playlistorder.push_back(index);
-            lastindex = index;
+                    tempd = playlistweight[i];
+                    playlistweight[i] = playlistweight[j];
+                    playlistweight[j] = tempd;
 
-            if (curMeta == (*plist)[i])
-                playlistindex = i;
-            if (curMeta == (*plist)[index])
-                shuffleindex = i;
+                    temp = playlistorder[i];
+                    playlistorder[i] = playlistorder[j];
+                    playlistorder[j] = temp;
+                }
+            }
         }
     }
 
@@ -544,6 +601,8 @@
         decoder->start();
 
         isplaying = true;
+        curMeta.setLastPlay(db);
+        curMeta.incPlayCount(db);
        
         playlist_timer->start(1, true);
  
@@ -689,6 +748,9 @@
 {
     listlock.lock();
 
+    if (isplaying == true)
+        curMeta.decRating(db);
+
     shuffleindex++;
     if (shuffleindex >= (int)plist->size())
         shuffleindex = 0;
@@ -765,11 +827,13 @@
 
 void PlaybackBox::toggleShuffle()
 {
-    shufflemode = !shufflemode;
+    shufflemode = (shufflemode + 1) % 3;
 
     setupPlaylist();
 
-    if (shufflemode)
+    if (shufflemode == 2)
+        randomize->setText("Shuffle: Intelligent");
+	 else if (shufflemode == 1)
         randomize->setText("Shuffle: Random");
     else
         randomize->setText("Shuffle: Normal"); 
@@ -778,6 +842,28 @@
         randomize->setAccel(Key_1);
 }
 
+void PlaybackBox::toggleRatingUp()
+{
+    curMeta.incRating(db);
+
+    QString rating;
+    for (int i = 0; i < curMeta.Rating(); i++)
+        rating.append("|");
+    QListViewItem *curItem = listlist.at(playlistindex);
+    curItem->setText(1, rating);
+}
+
+void PlaybackBox::toggleRatingDn()
+{
+    curMeta.decRating(db);
+
+    QString rating;
+    for (int i = 0; i < curMeta.Rating(); i++)
+        rating.append("|");
+    QListViewItem *curItem = listlist.at(playlistindex);
+    curItem->setText(1, rating);
+}
+
 void PlaybackBox::toggleRepeat()
 {
     repeatmode = !repeatmode;
--- mythmusic/playbackbox.h.orig	Mon Feb 24 14:48:58 2003
+++ mythmusic/playbackbox.h	Tue Feb 25 17:08:48 2003
@@ -52,6 +52,8 @@
     void seek(int);
     void stopAll();
     void toggleShuffle();
+    void toggleRatingUp();
+    void toggleRatingDn();
     void toggleRepeat();
     void editPlaylist();
     void nextAuto();
@@ -87,6 +89,7 @@
 
     QValueList<Metadata> *plist;
     QValueList<int> playlistorder;
+    QValueList<double> playlistweight;
     QMutex listlock;
 
     int playlistindex;
@@ -107,7 +110,7 @@
     MythToolButton *vis;
     MythToolButton *pauseb;
 
-    bool shufflemode;
+    int shufflemode;
     bool repeatmode;
 
     bool isplaying;
--- mythmusic/metadata.cpp.orig	Mon Feb 24 14:48:35 2003
+++ mythmusic/metadata.cpp	Tue Feb 25 17:16:53 2003
@@ -1,5 +1,6 @@
 #include <qsqldatabase.h>
 #include <qregexp.h>
+#include <qdatetime.h>
 
 #include "metadata.h"
 
@@ -25,8 +26,8 @@
     sqlfilename.replace(QRegExp("\""), QString("\\\""));
 
     QString thequery = QString("SELECT artist,album,title,genre,year,tracknum,"
-                               "length,intid FROM musicmetadata WHERE "
-                               "filename = \"%1\";").arg(sqlfilename);
+                               "length,intid,rating,playcount,lastplay FROM "
+                               "musicmetadata WHERE filename = \"%1\";").arg(filename);
 
     QSqlQuery query = db->exec(thequery);
 
@@ -42,7 +43,9 @@
         tracknum = query.value(5).toInt();
         length = query.value(6).toInt();
         id = query.value(7).toUInt();
-
+        rating = query.value(8).toInt();
+        playcount = query.value(9).toInt();
+        lastplay = query.value(10).toString();
         retval = true;
     }
 
@@ -107,8 +110,8 @@
     sqltitle.replace(QRegExp("\""), QString("\\\""));
 
     QString thequery = "SELECT artist,album,title,genre,year,tracknum,length,"
-                       "filename,intid FROM musicmetadata WHERE title=\"" + 
-                       sqltitle + "\"";
+                       "filename,intid,rating,playcount,lastplay FROM "
+                       "musicmetadata WHERE title=\"" + sqltitle + "\"";
 
     if (album != "")
     {
@@ -140,6 +143,9 @@
         length = query.value(6).toInt();
         filename = query.value(7).toString();
         id = query.value(8).toUInt();
+        rating = query.value(9).toInt();
+        playcount = query.value(10).toInt();
+        lastplay = query.value(11).toString();
     }
 }
 
@@ -150,8 +156,8 @@
         
     QString thequery;
     thequery = QString("SELECT title,artist,album,title,genre,year,tracknum,"
-                       "length,filename FROM musicmetadata WHERE intid=%1;")
-                      .arg(id);
+                       "length,filename,rating,playcount,lastplay FROM "
+                       "musicmetadata WHERE intid=%1;").arg(id);
         
     QSqlQuery query = db->exec(thequery);
 
@@ -168,6 +174,47 @@
         tracknum = query.value(6).toInt();
         length = query.value(7).toInt();
         filename = query.value(8).toString();
+        rating = query.value(9).toInt();
+        playcount = query.value(10).toInt();
+        lastplay = query.value(11).toString();
     }
 }
 
+void Metadata::decRating(QSqlDatabase *db)
+{
+    if (rating > 0) 
+    setFieldDB(db, "rating", --rating);
+}
+
+void Metadata::incRating(QSqlDatabase *db)
+{
+    if (rating < 10)
+    setFieldDB(db, "rating", ++rating);
+}
+
+double Metadata::LastPlay()
+{
+    QDateTime lTime = QDateTime::fromString(lastplay, Qt::ISODate);
+    double lastDateTime = atof( lTime.toString("yyyyMMddhhmmss").ascii() );
+    return lastDateTime;
+}      
+
+void Metadata::setLastPlay(QSqlDatabase *db)
+{
+    QDateTime cTime = QDateTime::currentDateTime();
+    double currentDateTime = atof( cTime.toString("yyyyMMddhhmmss").ascii() );
+    setFieldDB(db, "lastplay", currentDateTime);
+}      
+
+void Metadata::incPlayCount(QSqlDatabase *db)
+{
+    if (playcount < 50)
+        setFieldDB(db, "playcount", ++playcount);
+}
+
+void Metadata::setFieldDB(QSqlDatabase *db, QString field, int data)
+{
+    QString thequery = QString("UPDATE musicmetadata SET %1=%2 WHERE "
+                               "intid=%3;").arg(field).arg(data).arg(id);
+    db->exec(thequery);
+}
--- mythmusic/metadata.h.orig	Mon Feb 24 14:48:41 2003
+++ mythmusic/metadata.h	Tue Feb 25 14:19:25 2003
@@ -34,6 +34,9 @@
                 tracknum = other.tracknum;
                 length = other.length;
                 id = other.id;
+                rating = other.rating;
+                lastplay = other.lastplay;
+                playcount = other.playcount;
             }
 
    ~Metadata() {}
@@ -65,9 +68,20 @@
     QString Filename() const { return filename; }
     void setFilename(QString &lfilename) { filename = lfilename; }
 
+    int Rating() { return rating; }
+    void decRating(QSqlDatabase *db);
+    void incRating(QSqlDatabase *db);
+
+    double LastPlay();
+    void setLastPlay(QSqlDatabase *db);
+
+    int PlayCount() { return playcount; }
+    void incPlayCount(QSqlDatabase *db);
+
     bool isInDatabase(QSqlDatabase *db);
     void dumpToDatabase(QSqlDatabase *db);
 
+    void setFieldDB(QSqlDatabase *db, QString field, int data);
     void setField(QString field, QString data);
     void fillData(QSqlDatabase *db);
     void fillDataFromID(QSqlDatabase *db);
@@ -80,6 +94,9 @@
     int year;
     int tracknum;
     int length;
+    int rating;
+    QString lastplay;
+    int playcount;
 
     unsigned int id;
     
-------------- next part --------------
A non-text attachment was scrubbed...
Name: music-cvs.sql
Type: text/x-sql
Size: 241 bytes
Desc: not available
Url : /pipermail/attachments/20030226/80afa6b7/music-cvs.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ratedn.xpm
Type: image/x-xpixmap
Size: 2594 bytes
Desc: not available
Url : /pipermail/attachments/20030226/80afa6b7/ratedn.xpm
-------------- next part --------------
A non-text attachment was scrubbed...
Name: rateup.xpm
Type: image/x-xpixmap
Size: 2594 bytes
Desc: not available
Url : /pipermail/attachments/20030226/80afa6b7/rateup.xpm


More information about the mythtv-dev mailing list