MythTV  0.26-pre
metadata.cpp
Go to the documentation of this file.
00001 // qt
00002 #include <QApplication>
00003 #include <QRegExp>
00004 #include <QDateTime>
00005 #include <QDir>
00006 
00007 // mythtv
00008 #include <mythcontext.h>
00009 #include <mythwidgets.h>
00010 #include <mythdb.h>
00011 #include <mythdirs.h>
00012 #include <mythprogressdialog.h>
00013 #include <mythdownloadmanager.h>
00014 #include <mythlogging.h>
00015 #include <mythmiscutil.h>
00016 
00017 // mythmusic
00018 #include "metadata.h"
00019 #include "metaio.h"
00020 #include "metaioid3.h"
00021 #include "metaiomp4.h"
00022 #include "metaioavfcomment.h"
00023 #include "metaiooggvorbis.h"
00024 #include "metaioflacvorbis.h"
00025 #include "metaiowavpack.h"
00026 #include "playlist.h"
00027 #include "playlistcontainer.h"
00028 #include "musicutils.h"
00029 
00030 
00031 
00032 // this is the global MusicData object shared thoughout MythMusic
00033 MusicData  *gMusicData = NULL;
00034 
00035 static QString thePrefix = "the ";
00036 
00037 bool operator==(const Metadata& a, const Metadata& b)
00038 {
00039     if (a.Filename() == b.Filename())
00040         return true;
00041     return false;
00042 }
00043 
00044 bool operator!=(const Metadata& a, const Metadata& b)
00045 {
00046     if (a.Filename() != b.Filename())
00047         return true;
00048     return false;
00049 }
00050 
00051 Metadata::~Metadata()
00052 {
00053     if (m_albumArt)
00054     {
00055         delete m_albumArt;
00056         m_albumArt = NULL;
00057     }
00058 }
00059 
00060 
00061 Metadata& Metadata::operator=(const Metadata &rhs)
00062 {
00063     m_artist = rhs.m_artist;
00064     m_compilation_artist = rhs.m_compilation_artist;
00065     m_album = rhs.m_album;
00066     m_title = rhs.m_title;
00067     m_formattedartist = rhs.m_formattedartist;
00068     m_formattedtitle = rhs.m_formattedtitle;
00069     m_genre = rhs.m_genre;
00070     m_year = rhs.m_year;
00071     m_tracknum = rhs.m_tracknum;
00072     m_trackCount = rhs.m_trackCount;
00073     m_length = rhs.m_length;
00074     m_rating = rhs.m_rating;
00075     m_lastplay = rhs.m_lastplay;
00076     m_templastplay = rhs.m_templastplay;
00077     m_dateadded = rhs.m_dateadded;
00078     m_playcount = rhs.m_playcount;
00079     m_tempplaycount = rhs.m_tempplaycount;
00080     m_compilation = rhs.m_compilation;
00081     m_id = rhs.m_id;
00082     m_filename = rhs.m_filename;
00083     m_directoryid = rhs.m_directoryid;
00084     m_artistid = rhs.m_artistid;
00085     m_compartistid = rhs.m_compartistid;
00086     m_albumid = rhs.m_albumid;
00087     m_genreid = rhs.m_genreid;
00088     m_albumArt = NULL;
00089     m_format = rhs.m_format;
00090     m_changed = rhs.m_changed;
00091 
00092     return *this;
00093 }
00094 
00095 void Metadata::persist()
00096 {
00097     if (m_id < 1)
00098         return;
00099 
00100     if (m_templastplay.isValid())
00101     {
00102         m_lastplay = m_templastplay;
00103         m_playcount = m_tempplaycount;
00104 
00105         m_templastplay = QDateTime();
00106     }
00107 
00108     MSqlQuery query(MSqlQuery::InitCon());
00109     query.prepare("UPDATE music_songs set rating = :RATING , "
00110                   "numplays = :PLAYCOUNT , lastplay = :LASTPLAY "
00111                   "where song_id = :ID ;");
00112     query.bindValue(":RATING", m_rating);
00113     query.bindValue(":PLAYCOUNT", m_playcount);
00114     query.bindValue(":LASTPLAY", m_lastplay);
00115     query.bindValue(":ID", m_id);
00116 
00117     if (!query.exec())
00118         MythDB::DBError("music persist", query);
00119 
00120     gPlayer->sendTrackStatsChangedEvent(ID());
00121 
00122     m_changed = false;
00123 }
00124 
00125 
00126 void Metadata::UpdateModTime() const
00127 {
00128     if (m_id < 1)
00129         return;
00130 
00131     MSqlQuery query(MSqlQuery::InitCon());
00132 
00133     query.prepare("UPDATE music_songs SET date_modified = :DATE_MOD "
00134                   "WHERE song_id= :ID ;");
00135 
00136     query.bindValue(":DATE_MOD", QDateTime::currentDateTime());
00137     query.bindValue(":ID", m_id);
00138 
00139     if (!query.exec())
00140         MythDB::DBError("Metadata::UpdateModTime",
00141                         query);
00142 }
00143 
00144 int Metadata::compare(const Metadata *other) const
00145 {
00146     if (m_format == "cast")
00147     {
00148         int artist_cmp = Artist().toLower().localeAwareCompare(
00149             other->Artist().toLower());
00150 
00151         if (artist_cmp == 0)
00152             return Title().toLower().localeAwareCompare(
00153                 other->Title().toLower());
00154 
00155         return artist_cmp;
00156     }
00157     else
00158     {
00159         int track_cmp = Track() - other->Track();
00160 
00161         if (track_cmp == 0)
00162             return Title().toLower().localeAwareCompare(
00163                 other->Title().toLower());
00164 
00165         return track_cmp;
00166     }
00167 }
00168 
00169 bool Metadata::isInDatabase()
00170 {
00171     bool retval = false;
00172 
00173     QString sqldir = m_filename.section('/', 0, -2);
00174     QString sqlfilename = m_filename.section('/', -1);
00175 
00176     MSqlQuery query(MSqlQuery::InitCon());
00177     query.prepare("SELECT music_artists.artist_name, "
00178     "music_comp_artists.artist_name AS compilation_artist, "
00179     "music_albums.album_name, music_songs.name, music_genres.genre, "
00180     "music_songs.year, music_songs.track, music_songs.length, "
00181     "music_songs.song_id, music_songs.rating, music_songs.numplays, "
00182     "music_songs.lastplay, music_albums.compilation, music_songs.format, "
00183     "music_songs.track_count "
00184     "FROM music_songs "
00185     "LEFT JOIN music_directories "
00186     "ON music_songs.directory_id=music_directories.directory_id "
00187     "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
00188     "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
00189     "LEFT JOIN music_artists AS music_comp_artists "
00190     "ON music_albums.artist_id=music_comp_artists.artist_id "
00191     "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id "
00192     "WHERE music_songs.filename = :FILENAME "
00193     "AND music_directories.path = :DIRECTORY ;");
00194     query.bindValue(":FILENAME", sqlfilename);
00195     query.bindValue(":DIRECTORY", sqldir);
00196 
00197     if (query.exec() && query.next())
00198     {
00199 
00200         m_artist = query.value(0).toString();
00201         m_compilation_artist = query.value(1).toString();
00202         m_album = query.value(2).toString();
00203         m_title = query.value(3).toString();
00204         m_genre = query.value(4).toString();
00205         m_year = query.value(5).toInt();
00206         m_tracknum = query.value(6).toInt();
00207         m_length = query.value(7).toInt();
00208         m_id = query.value(8).toUInt();
00209         m_rating = query.value(9).toInt();
00210         m_playcount = query.value(10).toInt();
00211         m_lastplay = query.value(11).toDateTime();
00212         m_compilation = (query.value(12).toInt() > 0);
00213         m_format = query.value(13).toString();
00214         m_trackCount = query.value(14).toInt();
00215 
00216         retval = true;
00217     }
00218 
00219     return retval;
00220 }
00221 
00222 void Metadata::dumpToDatabase()
00223 {
00224     QString sqldir = m_filename.section('/', 0, -2);
00225     QString sqlfilename = m_filename.section('/', -1);
00226 
00227     checkEmptyFields();
00228 
00229     MSqlQuery query(MSqlQuery::InitCon());
00230 
00231     if (sqldir.isEmpty())
00232     {
00233         m_directoryid = 0;
00234     }
00235     else if (m_directoryid < 0)
00236     {
00237         // Load the directory id
00238         query.prepare("SELECT directory_id FROM music_directories "
00239                     "WHERE path = :DIRECTORY ;");
00240         query.bindValue(":DIRECTORY", sqldir);
00241 
00242         if (!query.exec() || !query.isActive())
00243         {
00244             MythDB::DBError("music select directory id", query);
00245             return;
00246         }
00247         if (query.next())
00248         {
00249             m_directoryid = query.value(0).toInt();
00250         }
00251         else
00252         {
00253             query.prepare("INSERT INTO music_directories (path) VALUES (:DIRECTORY);");
00254             query.bindValue(":DIRECTORY", sqldir);
00255 
00256             if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
00257             {
00258                 MythDB::DBError("music insert directory", query);
00259                 return;
00260             }
00261             m_directoryid = query.lastInsertId().toInt();
00262         }
00263     }
00264 
00265     if (m_artistid < 0)
00266     {
00267         // Load the artist id
00268         query.prepare("SELECT artist_id FROM music_artists "
00269                     "WHERE artist_name = :ARTIST ;");
00270         query.bindValue(":ARTIST", m_artist);
00271 
00272         if (!query.exec() || !query.isActive())
00273         {
00274             MythDB::DBError("music select artist id", query);
00275             return;
00276         }
00277         if (query.next())
00278         {
00279             m_artistid = query.value(0).toInt();
00280         }
00281         else
00282         {
00283             query.prepare("INSERT INTO music_artists (artist_name) VALUES (:ARTIST);");
00284             query.bindValue(":ARTIST", m_artist);
00285 
00286             if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
00287             {
00288                 MythDB::DBError("music insert artist", query);
00289                 return;
00290             }
00291             m_artistid = query.lastInsertId().toInt();
00292         }
00293     }
00294 
00295     // Compilation Artist
00296     if (m_artist == m_compilation_artist)
00297     {
00298         m_compartistid = m_artistid;
00299     }
00300     else
00301     {
00302         query.prepare("SELECT artist_id FROM music_artists "
00303                     "WHERE artist_name = :ARTIST ;");
00304         query.bindValue(":ARTIST", m_compilation_artist);
00305         if (!query.exec() || !query.isActive())
00306         {
00307             MythDB::DBError("music select compilation artist id", query);
00308             return;
00309         }
00310         if (query.next())
00311         {
00312             m_compartistid = query.value(0).toInt();
00313         }
00314         else
00315         {
00316             query.prepare("INSERT INTO music_artists (artist_name) VALUES (:ARTIST);");
00317             query.bindValue(":ARTIST", m_compilation_artist);
00318 
00319             if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
00320             {
00321                 MythDB::DBError("music insert compilation artist", query);
00322                 return;
00323             }
00324             m_compartistid = query.lastInsertId().toInt();
00325         }
00326     }
00327 
00328     // Album
00329     if (m_albumid < 0)
00330     {
00331         query.prepare("SELECT album_id FROM music_albums "
00332                     "WHERE artist_id = :COMP_ARTIST_ID "
00333                     " AND album_name = :ALBUM ;");
00334         query.bindValue(":COMP_ARTIST_ID", m_compartistid);
00335         query.bindValue(":ALBUM", m_album);
00336         if (!query.exec() || !query.isActive())
00337         {
00338             MythDB::DBError("music select album id", query);
00339             return;
00340         }
00341         if (query.next())
00342         {
00343             m_albumid = query.value(0).toInt();
00344         }
00345         else
00346         {
00347             query.prepare("INSERT INTO music_albums (artist_id, album_name, compilation, year) VALUES (:COMP_ARTIST_ID, :ALBUM, :COMPILATION, :YEAR);");
00348             query.bindValue(":COMP_ARTIST_ID", m_compartistid);
00349             query.bindValue(":ALBUM", m_album);
00350             query.bindValue(":COMPILATION", m_compilation);
00351             query.bindValue(":YEAR", m_year);
00352 
00353             if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
00354             {
00355                 MythDB::DBError("music insert album", query);
00356                 return;
00357             }
00358             m_albumid = query.lastInsertId().toInt();
00359         }
00360     }
00361 
00362     if (m_genreid < 0)
00363     {
00364         // Genres
00365         query.prepare("SELECT genre_id FROM music_genres "
00366                     "WHERE genre = :GENRE ;");
00367         query.bindValue(":GENRE", m_genre);
00368         if (!query.exec() || !query.isActive())
00369         {
00370             MythDB::DBError("music select genre id", query);
00371             return;
00372         }
00373         if (query.next())
00374         {
00375             m_genreid = query.value(0).toInt();
00376         }
00377         else
00378         {
00379             query.prepare("INSERT INTO music_genres (genre) VALUES (:GENRE);");
00380             query.bindValue(":GENRE", m_genre);
00381 
00382             if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
00383             {
00384                 MythDB::DBError("music insert genre", query);
00385                 return;
00386             }
00387             m_genreid = query.lastInsertId().toInt();
00388         }
00389     }
00390 
00391     // We have all the id's now. We can insert it.
00392     QString strQuery;
00393     if (m_id < 1)
00394     {
00395         strQuery = "INSERT INTO music_songs ( directory_id,"
00396                    " artist_id, album_id,  name,         genre_id,"
00397                    " year,      track,     length,       filename,"
00398                    " rating,    format,    date_entered, date_modified,"
00399                    " numplays,  track_count) "
00400                    "VALUES ( "
00401                    " :DIRECTORY, "
00402                    " :ARTIST,   :ALBUM,    :TITLE,       :GENRE,"
00403                    " :YEAR,     :TRACKNUM, :LENGTH,      :FILENAME,"
00404                    " :RATING,   :FORMAT,   :DATE_ADD,    :DATE_MOD,"
00405                    " :PLAYCOUNT,:TRACKCOUNT );";
00406     }
00407     else
00408     {
00409         strQuery = "UPDATE music_songs SET"
00410                    " directory_id = :DIRECTORY"
00411                    ", artist_id = :ARTIST"
00412                    ", album_id = :ALBUM"
00413                    ", name = :TITLE"
00414                    ", genre_id = :GENRE"
00415                    ", year = :YEAR"
00416                    ", track = :TRACKNUM"
00417                    ", length = :LENGTH"
00418                    ", filename = :FILENAME"
00419                    ", rating = :RATING"
00420                    ", format = :FORMAT"
00421                    ", date_modified = :DATE_MOD "
00422                    ", numplays = :PLAYCOUNT "
00423                    ", track_count = :TRACKCOUNT "
00424                    "WHERE song_id= :ID ;";
00425     }
00426 
00427     query.prepare(strQuery);
00428 
00429     query.bindValue(":DIRECTORY", m_directoryid);
00430     query.bindValue(":ARTIST", m_artistid);
00431     query.bindValue(":ALBUM", m_albumid);
00432     query.bindValue(":TITLE", m_title);
00433     query.bindValue(":GENRE", m_genreid);
00434     query.bindValue(":YEAR", m_year);
00435     query.bindValue(":TRACKNUM", m_tracknum);
00436     query.bindValue(":LENGTH", m_length);
00437     query.bindValue(":FILENAME", sqlfilename);
00438     query.bindValue(":RATING", m_rating);
00439     query.bindValue(":FORMAT", m_format);
00440     query.bindValue(":DATE_MOD", QDateTime::currentDateTime());
00441     query.bindValue(":PLAYCOUNT", m_playcount);
00442 
00443     if (m_id < 1)
00444         query.bindValue(":DATE_ADD",  QDateTime::currentDateTime());
00445     else
00446         query.bindValue(":ID", m_id);
00447 
00448     query.bindValue(":TRACKCOUNT", m_trackCount);
00449 
00450     if (!query.exec())
00451         MythDB::DBError("Metadata::dumpToDatabase - updating music_songs",
00452                         query);
00453 
00454     if (m_id < 1 && query.isActive() && 1 == query.numRowsAffected())
00455         m_id = query.lastInsertId().toInt();
00456 
00457     // save the albumart to the db
00458     if (m_albumArt)
00459         m_albumArt->dumpToDatabase();
00460 
00461     // make sure the compilation flag is updated
00462     query.prepare("UPDATE music_albums SET compilation = :COMPILATION, year = :YEAR "
00463                   "WHERE music_albums.album_id = :ALBUMID");
00464     query.bindValue(":ALBUMID", m_albumid);
00465     query.bindValue(":COMPILATION", m_compilation);
00466     query.bindValue(":YEAR", m_year);
00467 
00468     if (!query.exec() || !query.isActive())
00469     {
00470         MythDB::DBError("music compilation update", query);
00471         return;
00472     }
00473 }
00474 
00475 // Default values for formats
00476 // NB These will eventually be customizable....
00477 QString Metadata::m_formatnormalfileartist      = "ARTIST";
00478 QString Metadata::m_formatnormalfiletrack       = "TITLE";
00479 QString Metadata::m_formatnormalcdartist        = "ARTIST";
00480 QString Metadata::m_formatnormalcdtrack         = "TITLE";
00481 QString Metadata::m_formatcompilationfileartist = "COMPARTIST";
00482 QString Metadata::m_formatcompilationfiletrack  = "TITLE (ARTIST)";
00483 QString Metadata::m_formatcompilationcdartist   = "COMPARTIST";
00484 QString Metadata::m_formatcompilationcdtrack    = "TITLE (ARTIST)";
00485 
00486 void Metadata::setArtistAndTrackFormats()
00487 {
00488     QString tmp;
00489 
00490     tmp = gCoreContext->GetSetting("MusicFormatNormalFileArtist");
00491     if (!tmp.isEmpty())
00492         m_formatnormalfileartist = tmp;
00493 
00494     tmp = gCoreContext->GetSetting("MusicFormatNormalFileTrack");
00495     if (!tmp.isEmpty())
00496         m_formatnormalfiletrack = tmp;
00497 
00498     tmp = gCoreContext->GetSetting("MusicFormatNormalCDArtist");
00499     if (!tmp.isEmpty())
00500         m_formatnormalcdartist = tmp;
00501 
00502     tmp = gCoreContext->GetSetting("MusicFormatNormalCDTrack");
00503     if (!tmp.isEmpty())
00504         m_formatnormalcdtrack = tmp;
00505 
00506     tmp = gCoreContext->GetSetting("MusicFormatCompilationFileArtist");
00507     if (!tmp.isEmpty())
00508         m_formatcompilationfileartist = tmp;
00509 
00510     tmp = gCoreContext->GetSetting("MusicFormatCompilationFileTrack");
00511     if (!tmp.isEmpty())
00512         m_formatcompilationfiletrack = tmp;
00513 
00514     tmp = gCoreContext->GetSetting("MusicFormatCompilationCDArtist");
00515     if (!tmp.isEmpty())
00516         m_formatcompilationcdartist = tmp;
00517 
00518     tmp = gCoreContext->GetSetting("MusicFormatCompilationCDTrack");
00519     if (!tmp.isEmpty())
00520         m_formatcompilationcdtrack = tmp;
00521 }
00522 
00523 
00524 bool Metadata::determineIfCompilation(bool cd)
00525 {
00526     m_compilation = (!m_compilation_artist.isEmpty()
00527                    && m_artist != m_compilation_artist);
00528     setCompilationFormatting(cd);
00529     return m_compilation;
00530 }
00531 
00532 
00533 inline QString Metadata::formatReplaceSymbols(const QString &format)
00534 {
00535   QString rv = format;
00536   rv.replace("COMPARTIST", m_compilation_artist);
00537   rv.replace("ARTIST", m_artist);
00538   rv.replace("TITLE", m_title);
00539   rv.replace("TRACK", QString("%1").arg(m_tracknum, 2));
00540   return rv;
00541 }
00542 
00543 void Metadata::checkEmptyFields()
00544 {
00545     if (m_artist.isEmpty())
00546         m_artist = QObject::tr("Unknown Artist");
00547     // This should be the same as Artist if it's a compilation track or blank
00548     if (!m_compilation || m_compilation_artist.isEmpty())
00549         m_compilation_artist = m_artist;
00550     if (m_album.isEmpty())
00551         m_album = QObject::tr("Unknown Album");
00552     if (m_title.isEmpty())
00553         m_title = m_filename;
00554     if (m_genre.isEmpty())
00555         m_genre = QObject::tr("Unknown Genre");
00556 
00557 }
00558 
00559 inline void Metadata::setCompilationFormatting(bool cd)
00560 {
00561     QString format_artist, format_title;
00562 
00563     if (!m_compilation
00564         || "" == m_compilation_artist
00565         || m_artist == m_compilation_artist)
00566     {
00567         if (!cd)
00568         {
00569           format_artist = m_formatnormalfileartist;
00570           format_title  = m_formatnormalfiletrack;
00571         }
00572         else
00573         {
00574           format_artist = m_formatnormalcdartist;
00575           format_title  = m_formatnormalcdtrack;
00576         }
00577     }
00578     else
00579     {
00580         if (!cd)
00581         {
00582           format_artist = m_formatcompilationfileartist;
00583           format_title  = m_formatcompilationfiletrack;
00584         }
00585         else
00586         {
00587           format_artist = m_formatcompilationcdartist;
00588           format_title  = m_formatcompilationcdtrack;
00589         }
00590     }
00591 
00592     // NB Could do some comparisons here to save memory with shallow copies...
00593     m_formattedartist = formatReplaceSymbols(format_artist);
00594     m_formattedtitle = formatReplaceSymbols(format_title);
00595 }
00596 
00597 
00598 QString Metadata::FormatArtist()
00599 {
00600     if (m_formattedartist.isEmpty())
00601         setCompilationFormatting();
00602 
00603     return m_formattedartist;
00604 }
00605 
00606 
00607 QString Metadata::FormatTitle()
00608 {
00609     if (m_formattedtitle.isEmpty())
00610         setCompilationFormatting();
00611 
00612     return m_formattedtitle;
00613 }
00614 
00615 QString Metadata::Filename(bool find) const
00616 {
00617     // if not asked to find the file just return the raw filename from the DB
00618     if (find == false)
00619         return m_filename;
00620 
00621     // check for a cd track
00622     if (m_filename.endsWith(".cda"))
00623         return m_filename;
00624 
00625     // check for http urls etc
00626     if (m_filename.contains("://"))
00627         return m_filename;
00628 
00629     // first check to see if the filename is complete
00630     if (QFile::exists(m_filename))
00631         return m_filename;
00632 
00633     // next try appending the start directory
00634     if (QFile::exists(gMusicData->musicDir + m_filename))
00635         return gMusicData->musicDir + m_filename;
00636 
00637     // maybe it's in our 'Music' storage group
00638     //TODO add storage group lookup here
00639 
00640     // not found
00641     LOG(VB_GENERAL, LOG_ERR, QString("Metadata: Asked to get the filename for a track but no file found: %1")
00642                                      .arg(m_filename));
00643     return QString();
00644 }
00645 
00646 void Metadata::setField(const QString &field, const QString &data)
00647 {
00648     if (field == "artist")
00649         m_artist = data;
00650     else if (field == "compilation_artist")
00651       m_compilation_artist = data;
00652     else if (field == "album")
00653         m_album = data;
00654     else if (field == "title")
00655         m_title = data;
00656     else if (field == "genre")
00657         m_genre = data;
00658     else if (field == "filename")
00659         m_filename = data;
00660     else if (field == "year")
00661         m_year = data.toInt();
00662     else if (field == "tracknum")
00663         m_tracknum = data.toInt();
00664     else if (field == "trackcount")
00665         m_trackCount = data.toInt();
00666     else if (field == "length")
00667         m_length = data.toInt();
00668     else if (field == "compilation")
00669         m_compilation = (data.toInt() > 0);
00670 
00671     else
00672     {
00673         LOG(VB_GENERAL, LOG_ERR, QString("Something asked me to set data "
00674                                          "for a field called %1").arg(field));
00675     }
00676 }
00677 
00678 void Metadata::getField(const QString &field, QString *data)
00679 {
00680     if (field == "artist")
00681         *data = FormatArtist();
00682     else if (field == "album")
00683         *data = m_album;
00684     else if (field == "title")
00685         *data = FormatTitle();
00686     else if (field == "genre")
00687         *data = m_genre;
00688     else
00689     {
00690         LOG(VB_GENERAL, LOG_ERR, QString("Something asked me to return data "
00691                                          "about a field called %1").arg(field));
00692         *data = "I Dunno";
00693     }
00694 }
00695 
00696 void Metadata::toMap(MetadataMap &metadataMap, const QString &prefix)
00697 {
00698     metadataMap[prefix + "artist"] = m_artist;
00699     metadataMap[prefix + "formatartist"] = FormatArtist();
00700     metadataMap[prefix + "compilationartist"] = m_compilation_artist;
00701     metadataMap[prefix + "album"] = m_album;
00702     metadataMap[prefix + "title"] = m_title;
00703     metadataMap[prefix + "formattitle"] = FormatTitle();
00704     metadataMap[prefix + "tracknum"] = (m_tracknum > 0 ? QString("%1").arg(m_tracknum) : "");
00705     metadataMap[prefix + "trackcount"] = (m_trackCount > 0 ? QString("%1").arg(m_trackCount) : "");
00706     metadataMap[prefix + "genre"] = m_genre;
00707     metadataMap[prefix + "year"] = (m_year > 0 ? QString("%1").arg(m_year) : "");
00708 
00709     int len = m_length / 1000;
00710     int eh = len / 3600;
00711     int em = (len / 60) % 60;
00712     int es = len % 60;
00713     if (eh > 0)
00714         metadataMap[prefix + "length"] = QString().sprintf("%d:%02d:%02d", eh, em, es);
00715     else
00716         metadataMap[prefix + "length"] = QString().sprintf("%02d:%02d", em, es);
00717 
00718     if (m_lastplay.isValid())
00719         metadataMap[prefix + "lastplayed"] = MythDateTimeToString(m_lastplay,
00720                                                 kDateFull | kSimplify | kAddYear);
00721     else
00722         metadataMap[prefix + "lastplayed"] = QObject::tr("Never Played");
00723 
00724     metadataMap[prefix + "dateadded"] = MythDateTimeToString(m_dateadded,
00725                                               kDateFull | kSimplify | kAddYear);
00726 
00727     metadataMap[prefix + "playcount"] = QString::number(m_playcount);
00728     // FIXME we should use Filename() here but that will slow things down because of the hunt for the file
00729     metadataMap[prefix + "filename"] = gMusicData->musicDir + m_filename;
00730 }
00731 
00732 void Metadata::decRating()
00733 {
00734     if (m_rating > 0)
00735     {
00736         m_rating--;
00737     }
00738     m_changed = true;
00739 }
00740 
00741 void Metadata::incRating()
00742 {
00743     if (m_rating < 10)
00744     {
00745         m_rating++;
00746     }
00747     m_changed = true;
00748 }
00749 
00750 void Metadata::setLastPlay()
00751 {
00752     m_templastplay = QDateTime::currentDateTime();
00753     m_changed = true;
00754 }
00755 
00756 void Metadata::incPlayCount()
00757 {
00758     m_tempplaycount = m_playcount + 1;
00759     m_changed = true;
00760 }
00761 
00762 void Metadata::setEmbeddedAlbumArt(AlbumArtList &albumart)
00763 {
00764     // add the images found in the tag to the ones we got from the DB
00765 
00766     if (!m_albumArt)
00767         m_albumArt = new AlbumArtImages(this);
00768 
00769     for (int x = 0; x < albumart.size(); x++)
00770     {
00771         m_albumArt->addImage(albumart.at(x));
00772     }
00773 
00774     m_changed = true;
00775 }
00776 
00777 QStringList Metadata::fillFieldList(QString field)
00778 {
00779     QStringList searchList;
00780     searchList.clear();
00781 
00782     MSqlQuery query(MSqlQuery::InitCon());
00783     if ("artist" == field)
00784     {
00785         query.prepare("SELECT artist_name FROM music_artists ORDER BY artist_name;");
00786     }
00787     else if ("compilation_artist" == field)
00788     {
00789         query.prepare("SELECT DISTINCT artist_name FROM music_artists, music_albums where "
00790                 "music_albums.artist_id=music_artists.artist_id ORDER BY artist_name");
00791     }
00792     else if ("album" == field)
00793     {
00794         query.prepare("SELECT album_name FROM music_albums ORDER BY album_name;");
00795     }
00796     else if ("title" == field)
00797     {
00798         query.prepare("SELECT name FROM music_songs ORDER BY name;");
00799     }
00800     else if ("genre" == field)
00801     {
00802         query.prepare("SELECT genre FROM music_genres ORDER BY genre;");
00803     }
00804     else
00805     {
00806         return searchList;
00807     }
00808 
00809     if (query.exec() && query.isActive())
00810     {
00811         while (query.next())
00812         {
00813             searchList << query.value(0).toString();
00814         }
00815     }
00816     return searchList;
00817 }
00818 
00819 QString Metadata::getAlbumArtFile(void)
00820 {
00821     if (!m_albumArt)
00822         m_albumArt = new AlbumArtImages(this);
00823 
00824     AlbumArtImage *albumart_image = NULL;
00825     QString res;
00826 
00827     if ((albumart_image = m_albumArt->getImage(IT_FRONTCOVER)))
00828         res = albumart_image->filename;
00829     else if ((albumart_image = m_albumArt->getImage(IT_UNKNOWN)))
00830         res = albumart_image->filename;
00831     else if ((albumart_image = m_albumArt->getImage(IT_BACKCOVER)))
00832         res = albumart_image->filename;
00833     else if ((albumart_image = m_albumArt->getImage(IT_INLAY)))
00834         res = albumart_image->filename;
00835     else if ((albumart_image = m_albumArt->getImage(IT_CD)))
00836         res = albumart_image->filename;
00837 
00838     // check file exists
00839     if (!res.isEmpty())
00840     {
00841         int repo = ID_TO_REPO(m_id);
00842         if (repo == RT_Radio)
00843         {
00844             // image is a radio station icon, check if we have already downloaded and cached it
00845             QString path = GetConfDir() + "/MythMusic/AlbumArt/";
00846             QFileInfo fi(res);
00847             QString filename = QString("%1-%2.%3").arg(m_id).arg("front").arg(fi.suffix());
00848 
00849             albumart_image->filename = path + filename;
00850 
00851             if (!QFile::exists(albumart_image->filename))
00852             {
00853                 // file does not exist so try to download and cache it
00854                 if (!GetMythDownloadManager()->download(res, albumart_image->filename))
00855                 {
00856                     m_albumArt->getImageList()->removeAll(albumart_image);
00857                     return QString("");
00858                 }
00859             }
00860 
00861             res = albumart_image->filename;
00862         }
00863         else
00864         {
00865             if (!QFile::exists(res))
00866             {
00867                 if (albumart_image->embedded && getTagger()->supportsEmbeddedImages())
00868                 {
00869                     // image is embedded try to extract it from the tag and cache it for latter
00870                     QImage *image = getTagger()->getAlbumArt(Filename(),
00871                                                              albumart_image->imageType);
00872                     if (image)
00873                     {
00874                         image->save(res);
00875                         delete image;
00876                         return res;
00877                     }
00878                 }
00879                 else
00880                 {
00881                     // image is in a file but couldn't be found!
00882                     m_albumArt->getImageList()->removeAll(albumart_image);
00883                     return QString("");
00884                 }
00885             }
00886         }
00887 
00888         return res;
00889     }
00890 
00891     return QString("");
00892 }
00893 
00894 QString Metadata::getAlbumArtFile(ImageType type)
00895 {
00896     if (!m_albumArt)
00897         m_albumArt = new AlbumArtImages(this);
00898 
00899     AlbumArtImage *albumart_image = m_albumArt->getImage(type);
00900     if (albumart_image)
00901         return albumart_image->filename;
00902 
00903     return QString("");
00904 }
00905 
00906 AlbumArtImages *Metadata::getAlbumArtImages(void)
00907 {
00908     if (!m_albumArt)
00909         m_albumArt = new AlbumArtImages(this);
00910 
00911     return m_albumArt;
00912 }
00913 
00914 void Metadata::reloadAlbumArtImages(void)
00915 {
00916     delete m_albumArt;
00917     m_albumArt = NULL; //new AlbumArtImages(this);
00918 }
00919 
00920 
00922 MetaIO* Metadata::getTagger(void)
00923 {
00924     static MetaIOID3 metaIOID3;
00925     static MetaIOOggVorbis metaIOOggVorbis;
00926     static MetaIOFLACVorbis metaIOFLACVorbis;
00927     static MetaIOMP4 metaIOMP4;
00928     static MetaIOWavPack metaIOWavPack;
00929     static MetaIOAVFComment metaIOAVFComment;
00930 
00931     QFileInfo fi(m_filename);
00932     QString extension = fi.suffix();
00933 
00934     if (extension == "mp3" || extension == "mp2")
00935         return &metaIOID3;
00936     else if (extension == "ogg" || extension == "oga")
00937         return &metaIOOggVorbis;
00938     else if (extension == "flac")
00939     {
00940         if (metaIOID3.TagExists(Filename(true)))
00941             return &metaIOID3;
00942         else
00943             return &metaIOFLACVorbis;
00944     }
00945     else if (extension == "m4a")
00946         return &metaIOMP4;
00947     else if (extension == "wv")
00948         return &metaIOWavPack;
00949     else
00950         return &metaIOAVFComment;
00951 }
00952 
00953 //--------------------------------------------------------------------------
00954 
00955 MetadataLoadingThread::MetadataLoadingThread(AllMusic *parent_ptr) :
00956     MThread("MetadataLoading"), parent(parent_ptr)
00957 {
00958 }
00959 
00960 void MetadataLoadingThread::run()
00961 {
00962     RunProlog();
00963     //if you want to simulate a big music collection load
00964     //sleep(3);
00965     parent->resync();
00966     RunEpilog();
00967 }
00968 
00969 AllMusic::AllMusic(void) :
00970     m_numPcs(0),
00971     m_numLoaded(0),
00972     m_metadata_loader(NULL),
00973     m_done_loading(false),
00974     m_last_listed(-1),
00975 
00976     m_playcountMin(0),
00977     m_playcountMax(0),
00978     m_lastplayMin(0.0),
00979     m_lastplayMax(0.0)
00980 {
00981     //  Start a thread to do data loading and sorting
00982     startLoading();
00983 }
00984 
00985 AllMusic::~AllMusic()
00986 {
00987     while (!m_all_music.empty())
00988     {
00989         delete m_all_music.back();
00990         m_all_music.pop_back();
00991     }
00992 
00993     while (!m_cdData.empty())
00994     {
00995         delete m_cdData.back();
00996         m_cdData.pop_back();
00997     }
00998 
00999     m_metadata_loader->wait();
01000     delete m_metadata_loader;
01001 }
01002 
01003 bool AllMusic::cleanOutThreads()
01004 {
01005     //  If this is still running, the user
01006     //  probably selected mythmusic and then
01007     //  escaped out right away
01008 
01009     if (m_metadata_loader->isFinished())
01010     {
01011         return true;
01012     }
01013 
01014     m_metadata_loader->wait();
01015     return false;
01016 }
01017 
01029 bool AllMusic::startLoading(void)
01030 {
01031     // Set this to false early rather than letting it be
01032     // delayed till the thread calls resync.
01033     m_done_loading = false;
01034 
01035     if (m_metadata_loader)
01036     {
01037         cleanOutThreads();
01038         delete m_metadata_loader;
01039     }
01040 
01041     m_metadata_loader = new MetadataLoadingThread(this);
01042     m_metadata_loader->start();
01043 
01044     return true;
01045 }
01046 
01047 // NOTE we don't clear the existing tracks just load any new ones found in the DB
01048 // maybe we should also check for any removed tracks?
01049 void AllMusic::resync()
01050 {
01051     m_done_loading = false;
01052 
01053     QString aquery = "SELECT music_songs.song_id, music_artists.artist_id, music_artists.artist_name, "
01054                      "music_comp_artists.artist_name AS compilation_artist, "
01055                      "music_albums.album_id, music_albums.album_name, music_songs.name, music_genres.genre, music_songs.year, "
01056                      "music_songs.track, music_songs.length, music_songs.directory_id, "
01057                      "CONCAT_WS('/', music_directories.path, music_songs.filename) AS filename, "
01058                      "music_songs.rating, music_songs.numplays, music_songs.lastplay, music_songs.date_entered, "
01059                      "music_albums.compilation, music_songs.format, music_songs.track_count "
01060                      "FROM music_songs "
01061                      "LEFT JOIN music_directories ON music_songs.directory_id=music_directories.directory_id "
01062                      "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
01063                      "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
01064                      "LEFT JOIN music_artists AS music_comp_artists ON music_albums.artist_id=music_comp_artists.artist_id "
01065                      "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id "
01066                      "ORDER BY music_songs.song_id;";
01067 
01068     QString filename, artist, album, title, compartist;
01069 
01070     MSqlQuery query(MSqlQuery::InitCon());
01071     if (!query.exec(aquery))
01072         MythDB::DBError("AllMusic::resync", query);
01073 
01074     m_numPcs = query.size() * 2;
01075     m_numLoaded = 0;
01076 
01077     if (query.isActive() && query.size() > 0)
01078     {
01079         while (query.next())
01080         {
01081             int id = query.value(0).toInt();
01082 
01083             if (!music_map.contains(id))
01084             {
01085                 filename = query.value(12).toString();
01086 
01087                 Metadata *mdata = new Metadata(
01088                     filename,
01089                     query.value(2).toString(),     // artist
01090                     query.value(3).toString(),     // compilation artist
01091                     query.value(5).toString(),     // album
01092                     query.value(6).toString(),     // title
01093                     query.value(7).toString(),     // genre
01094                     query.value(8).toInt(),        // year
01095                     query.value(9).toInt(),        // track no.
01096                     query.value(10).toInt(),       // length
01097                     query.value(0).toInt(),        // id
01098                     query.value(13).toInt(),       // rating
01099                     query.value(14).toInt(),       // playcount
01100                     query.value(15).toDateTime(),  // lastplay
01101                     query.value(16).toDateTime(),  // date_entered
01102                     (query.value(17).toInt() > 0), // compilation
01103                     query.value(18).toString());   // format
01104 
01105                 mdata->setDirectoryId(query.value(11).toInt());
01106                 mdata->setArtistId(query.value(1).toInt());
01107                 mdata->setAlbumId(query.value(4).toInt());
01108                 mdata->setTrackCount(query.value(19).toInt());
01109 
01110                 //  Don't delete mdata, as PtrList now owns it
01111                 m_all_music.append(mdata);
01112 
01113                 music_map[id] = mdata;
01114             }
01115 
01116             // compute max/min playcount,lastplay for all music
01117             if (query.at() == 0)
01118             {
01119                 // first song
01120                 m_playcountMin = m_playcountMax = query.value(13).toInt();
01121                 m_lastplayMin  = m_lastplayMax  = query.value(14).toDateTime().toTime_t();
01122             }
01123             else
01124             {
01125                 int playCount = query.value(13).toInt();
01126                 double lastPlay = query.value(14).toDateTime().toTime_t();
01127 
01128                 m_playcountMin = min(playCount, m_playcountMin);
01129                 m_playcountMax = max(playCount, m_playcountMax);
01130                 m_lastplayMin  = min(lastPlay,  m_lastplayMin);
01131                 m_lastplayMax  = max(lastPlay,  m_lastplayMax);
01132             }
01133             m_numLoaded++;
01134         }
01135     }
01136     else
01137     {
01138          LOG(VB_GENERAL, LOG_ERR, "MythMusic hasn't found any tracks! "
01139                                   "That's ok with me if it's ok with you.");
01140     }
01141 
01142     m_done_loading = true;
01143 }
01144 
01145 Metadata* AllMusic::getMetadata(int an_id)
01146 {
01147     if (music_map.contains(an_id))
01148         return music_map[an_id];
01149 
01150     return NULL;
01151 }
01152 
01153 bool AllMusic::isValidID(int an_id)
01154 {
01155     return music_map.contains(an_id);
01156 }
01157 
01158 bool AllMusic::updateMetadata(int an_id, Metadata *the_track)
01159 {
01160     if (an_id > 0)
01161     {
01162         Metadata *mdata = getMetadata(an_id);
01163         if (mdata)
01164         {
01165             *mdata = *the_track;
01166             return true;
01167         }
01168     }
01169     return false;
01170 }
01171 
01173 void AllMusic::save(void)
01174 {
01175     MetadataPtrList::iterator it = m_all_music.begin();
01176     for (; it != m_all_music.end(); ++it)
01177     {
01178         if ((*it)->hasChanged())
01179             (*it)->persist();
01180     }
01181 }
01182 
01183 // cd stuff
01184 void AllMusic::clearCDData(void)
01185 {
01186     while (!m_cdData.empty())
01187     {
01188         delete m_cdData.back();
01189         m_cdData.pop_back();
01190     }
01191 
01192     m_cdTitle = QObject::tr("CD -- none");
01193 }
01194 
01195 void AllMusic::addCDTrack(const Metadata &the_track)
01196 {
01197     Metadata *mdata = new Metadata(the_track);
01198     mdata->setID(m_cdData.count() + 1);
01199     mdata->setRepo(RT_CD);
01200     m_cdData.append(mdata);
01201     music_map[mdata->ID()] = mdata;
01202 }
01203 
01204 bool AllMusic::checkCDTrack(Metadata *the_track)
01205 {
01206     if (m_cdData.count() < 1)
01207         return false;
01208 
01209     if (m_cdData.last()->FormatTitle() == the_track->FormatTitle())
01210         return true;
01211 
01212     return false;
01213 }
01214 
01215 Metadata* AllMusic::getCDMetadata(int the_track)
01216 {
01217     MetadataPtrList::iterator anit;
01218     for (anit = m_cdData.begin(); anit != m_cdData.end(); ++anit)
01219     {
01220         if ((*anit)->Track() == the_track)
01221         {
01222             return (*anit);
01223         }
01224     }
01225 
01226     return NULL;
01227 }
01228 
01229 
01230 /**************************************************************************/
01231 
01232 AlbumArtImages::AlbumArtImages(Metadata *metadata)
01233     : m_parent(metadata)
01234 {
01235     findImages();
01236 }
01237 
01238 AlbumArtImages::~AlbumArtImages()
01239 {
01240     while (!m_imageList.empty())
01241     {
01242         delete m_imageList.back();
01243         m_imageList.pop_back();
01244     }
01245 }
01246 
01247 void AlbumArtImages::findImages(void)
01248 {
01249     while (!m_imageList.empty())
01250     {
01251         delete m_imageList.back();
01252         m_imageList.pop_back();
01253     }
01254 
01255     if (m_parent == NULL)
01256         return;
01257 
01258     int trackid = ID_TO_ID(m_parent->ID());
01259     int repo = ID_TO_REPO(m_parent->ID());
01260 
01261     if (repo == RT_Radio)
01262     {
01263         MSqlQuery query(MSqlQuery::InitCon());
01264         query.prepare("SELECT logourl FROM music_radios WHERE intid = :ID;");
01265         query.bindValue(":ID", trackid);
01266         if (query.exec())
01267         {
01268             while (query.next())
01269             {
01270                 QString logoUrl = query.value(0).toString();
01271                 AlbumArtImage *image = new AlbumArtImage();
01272                 image->id = -1;
01273                 image->filename = logoUrl;
01274                 image->imageType = IT_FRONTCOVER;
01275                 image->embedded = false;
01276 
01277                 m_imageList.push_back(image);
01278             }
01279         }
01280     }
01281     else
01282     {
01283         if (trackid == 0)
01284             return;
01285 
01286         QFileInfo fi(m_parent->Filename(false));
01287         QString dir = fi.path();
01288 
01289         MSqlQuery query(MSqlQuery::InitCon());
01290         query.prepare("SELECT albumart_id, CONCAT_WS('/', music_directories.path, "
01291                 "music_albumart.filename), music_albumart.filename, music_albumart.imagetype, "
01292                 "music_albumart.embedded "
01293                 "FROM music_albumart "
01294                 "LEFT JOIN music_directories ON "
01295                 "music_directories.directory_id = music_albumart.directory_id "
01296                 "WHERE music_directories.path = :DIR "
01297                 "OR song_id = :SONGID "
01298                 "ORDER BY music_albumart.imagetype;");
01299         query.bindValue(":DIR", dir);
01300         query.bindValue(":SONGID", trackid);
01301         if (query.exec())
01302         {
01303             while (query.next())
01304             {
01305                 AlbumArtImage *image = new AlbumArtImage();
01306                 bool embedded = (query.value(4).toInt() == 1);
01307                 image->id = query.value(0).toInt();
01308 
01309                 if (embedded)
01310                     image->filename = GetConfDir() + "/MythMusic/AlbumArt/" + query.value(1).toString();
01311                 else
01312                     image->filename = gMusicData->musicDir + query.value(1).toString();
01313 
01314                 image->imageType = (ImageType) query.value(3).toInt();
01315                 image->embedded = embedded;
01316 
01317                 m_imageList.push_back(image);
01318             }
01319         }
01320 
01321         // add any artist images
01322         QString artist = m_parent->Artist().toLower();
01323         if (findIcon("artist", artist) != QString())
01324         {
01325             AlbumArtImage *image = new AlbumArtImage();
01326             image->id = -1;
01327             image->filename = findIcon("artist", artist);
01328             image->imageType = IT_ARTIST;
01329             image->embedded = false;
01330 
01331             m_imageList.push_back(image);
01332         }
01333     }
01334 }
01335 
01336 AlbumArtImage *AlbumArtImages::getImage(ImageType type)
01337 {
01338     AlbumArtList::iterator it = m_imageList.begin();
01339     for (; it != m_imageList.end(); ++it)
01340     {
01341         if ((*it)->imageType == type)
01342             return *it;
01343     }
01344 
01345     return NULL;
01346 }
01347 
01348 QStringList AlbumArtImages::getImageFilenames(void) const
01349 {
01350     QStringList paths;
01351 
01352     AlbumArtList::const_iterator it = m_imageList.begin();
01353     for (; it != m_imageList.end(); ++it)
01354         paths += (*it)->filename;
01355 
01356     return paths;
01357 }
01358 
01359 AlbumArtImage *AlbumArtImages::getImageAt(uint index)
01360 {
01361     if (index < (uint)m_imageList.size())
01362         return m_imageList[index];
01363 
01364     return NULL;
01365 }
01366 
01367 // static method to get a translated type name from an ImageType
01368 QString AlbumArtImages::getTypeName(ImageType type)
01369 {
01370     // these const's should match the ImageType enum's
01371     static const char* type_strings[] = {
01372         QT_TR_NOOP("Unknown"),            // IT_UNKNOWN
01373         QT_TR_NOOP("Front Cover"),        // IT_FRONTCOVER
01374         QT_TR_NOOP("Back Cover"),         // IT_BACKCOVER
01375         QT_TR_NOOP("CD"),                 // IT_CD
01376         QT_TR_NOOP("Inlay"),              // IT_INLAY
01377         QT_TR_NOOP("Artist"),             // IT_ARTIST
01378     };
01379 
01380     return QCoreApplication::translate("AlbumArtImages",
01381                                        type_strings[type]);
01382 }
01383 
01384 // static method to get a filename from an ImageType
01385 QString AlbumArtImages::getTypeFilename(ImageType type)
01386 {
01387     // these const's should match the ImageType enum's
01388     static const char* filename_strings[] = {
01389         QT_TR_NOOP("unknown"),      // IT_UNKNOWN
01390         QT_TR_NOOP("front"),        // IT_FRONTCOVER
01391         QT_TR_NOOP("back"),         // IT_BACKCOVER
01392         QT_TR_NOOP("cd"),           // IT_CD
01393         QT_TR_NOOP("inlay"),        // IT_INLAY
01394         QT_TR_NOOP("artist")        // IT_ARTIST
01395     };
01396 
01397     return QCoreApplication::translate("AlbumArtImages",
01398                                        filename_strings[type]);
01399 }
01400 
01401 // static method to guess the image type from the filename
01402 ImageType AlbumArtImages::guessImageType(const QString &filename)
01403 {
01404     ImageType type = IT_FRONTCOVER;
01405 
01406     if (filename.contains("front", Qt::CaseInsensitive) ||
01407              filename.contains(QObject::tr("front"), Qt::CaseInsensitive))
01408         type = IT_FRONTCOVER;
01409     else if (filename.contains("back", Qt::CaseInsensitive) ||
01410              filename.contains(QObject::tr("back"),  Qt::CaseInsensitive))
01411         type = IT_BACKCOVER;
01412     else if (filename.contains("inlay", Qt::CaseInsensitive) ||
01413              filename.contains(QObject::tr("inlay"), Qt::CaseInsensitive))
01414         type = IT_INLAY;
01415     else if (filename.contains("cd", Qt::CaseInsensitive) ||
01416              filename.contains(QObject::tr("cd"), Qt::CaseInsensitive))
01417         type = IT_CD;
01418     else if (filename.contains("cover", Qt::CaseInsensitive) ||
01419              filename.contains(QObject::tr("cover"), Qt::CaseInsensitive))
01420         type = IT_FRONTCOVER;
01421 
01422     return type;
01423 }
01424 
01425 void AlbumArtImages::addImage(const AlbumArtImage &newImage)
01426 {
01427     // do we already have an image of this type?
01428     AlbumArtImage *image = NULL;
01429 
01430     AlbumArtList::iterator it = m_imageList.begin();
01431     for (; it != m_imageList.end(); ++it)
01432     {
01433         if ((*it)->imageType == newImage.imageType && (*it)->embedded == newImage.embedded)
01434         {
01435             image = *it;
01436             break;
01437         }
01438     }
01439 
01440     if (!image)
01441     {
01442         // not found so just add it to the list
01443         image = new AlbumArtImage(newImage);
01444         m_imageList.push_back(image);
01445     }
01446     else
01447     {
01448         // we already have an image of this type so just update it with the new info
01449         image->filename = newImage.filename;
01450         image->imageType = newImage.imageType;
01451         image->embedded = newImage.embedded;
01452         image->description = newImage.description;
01453     }
01454 
01455     // if this is an embedded image copy it to disc to speed up its display
01456     if (image->embedded && m_parent->getTagger()->supportsEmbeddedImages())
01457     {
01458         QString path = GetConfDir() + "/MythMusic/AlbumArt/";
01459         QDir dir(path);
01460 
01461         QString filename = QString("%1-%2.jpg").arg(m_parent->ID()).arg(AlbumArtImages::getTypeFilename(image->imageType));
01462         if (!QFile::exists(path + filename))
01463         {
01464             if (!dir.exists())
01465                 dir.mkpath(path);
01466 
01467             QImage *saveImage = m_parent->getTagger()->getAlbumArt(m_parent->Filename(), image->imageType);
01468             if (saveImage)
01469             {
01470                 saveImage->save(path + filename, "JPEG");
01471                 delete saveImage;
01472             }
01473         }
01474 
01475         image->filename = path + filename;
01476     }
01477 }
01478 
01480 void AlbumArtImages::dumpToDatabase(void)
01481 {
01482     Metadata::IdType trackID = ID_TO_ID(m_parent->ID());
01483     int directoryID = m_parent->getDirectoryId();
01484 
01485     // sanity check we have a valid songid and directoryid
01486     if (trackID == 0 || directoryID == -1)
01487     {
01488         LOG(VB_GENERAL, LOG_ERR, "AlbumArtImages: Asked to save to the DB but "
01489                                  "have invalid songid or directoryid");
01490         return;
01491     }
01492 
01493     MSqlQuery query(MSqlQuery::InitCon());
01494 
01495     // remove all albumart for this track from the db
01496     query.prepare("DELETE FROM music_albumart "
01497                   "WHERE song_id = :SONGID "
01498                   "OR (embedded = 0 AND directory_id = :DIRECTORYID)");
01499 
01500     query.bindValue(":SONGID", trackID);
01501     query.bindValue(":DIRECTORYID", directoryID);
01502 
01503     query.exec();
01504 
01505     // now add the albumart to the db
01506     AlbumArtList::iterator it = m_imageList.begin();
01507     for (; it != m_imageList.end(); ++it)
01508     {
01509         AlbumArtImage *image = (*it);
01510 
01511         //TODO: for the moment just ignore artist images
01512         if (image->imageType == IT_ARTIST)
01513             continue;
01514 
01515         if (image->id > 0)
01516         {
01517             // re-use the same id this image had before
01518             query.prepare("INSERT INTO music_albumart ( albumart_id, "
01519                           "filename, imagetype, song_id, directory_id, embedded ) "
01520                           "VALUES ( :ID, :FILENAME, :TYPE, :SONGID, :DIRECTORYID, :EMBED );");
01521             query.bindValue(":ID", image->id);
01522         }
01523         else
01524         {
01525             query.prepare("INSERT INTO music_albumart ( filename, "
01526                         "imagetype, song_id, directory_id, embedded ) VALUES ( "
01527                         ":FILENAME, :TYPE, :SONGID, :DIRECTORYID, :EMBED );");
01528         }
01529 
01530         QFileInfo fi(image->filename);
01531         query.bindValue(":FILENAME", fi.fileName());
01532 
01533         query.bindValue(":TYPE", image->imageType);
01534         query.bindValue(":SONGID", image->embedded ? trackID : 0);
01535         query.bindValue(":DIRECTORYID", image->embedded ? 0 : directoryID);
01536         query.bindValue(":EMBED", image->embedded);
01537 
01538         if (!query.exec())
01539             MythDB::DBError("AlbumArtImages::dumpToDatabase - "
01540                             "add/update music_albumart", query);
01541     }
01542 }
01543 
01544 
01545 /**************************************************************************/
01546 
01547 MusicData::MusicData(void)
01548 {
01549     all_playlists = NULL;
01550     all_music = NULL;
01551     initialized = false;
01552 }
01553 
01554 MusicData::~MusicData(void)
01555 {
01556     if (all_playlists)
01557     {
01558         delete all_playlists;
01559         all_playlists = NULL;
01560     }
01561 
01562     if (all_music)
01563     {
01564         delete all_music;
01565         all_music = NULL;
01566     }
01567 }
01568 
01570 void MusicData::reloadMusic(void)
01571 {
01572     if (!all_music || !all_playlists)
01573         return;
01574 
01575     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
01576     QString message = QObject::tr("Rebuilding music tree");
01577 
01578     MythUIBusyDialog *busy = new MythUIBusyDialog(message, popupStack,
01579                                                   "musicscanbusydialog");
01580 
01581     if (busy->Create())
01582         popupStack->AddScreen(busy, false);
01583     else
01584         busy = NULL;
01585 
01586     all_music->startLoading();
01587     while (!all_music->doneLoading())
01588     {
01589         qApp->processEvents();
01590         usleep(50000);
01591     }
01592     all_playlists->postLoad();
01593 
01594     if (busy)
01595         busy->Close();
01596 }
01597 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends