23 query.
prepare(
"SELECT directory_id, path FROM music_directories");
33 query.
prepare(
"SELECT genre_id, LOWER(genre) FROM music_genres");
43 query.
prepare(
"SELECT artist_id, LOWER(artist_name) FROM music_artists");
53 query.
prepare(
"SELECT album_id, artist_id, LOWER(album_name) FROM music_albums");
82 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
84 QFileInfoList list =
d.entryInfoList();
90 for (
const auto& fi : std::as_const(list))
92 QString
filename = fi.absoluteFilePath();
101 if (newparentid == 0)
112 LOG(VB_GENERAL, LOG_ERR,
113 QString(
"Failed to get directory id for path %1")
138 LOG(VB_GENERAL, LOG_INFO,
139 QString(
"Found file with unsupported extension %1")
149 QString extension = fi.suffix().toLower();
153 return !extension.isEmpty() && nameFilter.indexOf(extension.toLower()) > -1;
159 QString extension = fi.suffix().toLower();
162 return !extension.isEmpty() && nameFilter.indexOf(extension.toLower()) > -1;
177 if (directory.isEmpty())
183 query.
prepare(
"SELECT directory_id FROM music_directories "
184 "WHERE path = BINARY :DIRECTORY ;");
185 query.
bindValue(
":DIRECTORY", directory);
196 return query.
value(0).toInt();
200 query.
prepare(
"INSERT INTO music_directories (path, parent_id) "
201 "VALUES (:DIRECTORY, :PARENTID);");
202 query.
bindValue(
":DIRECTORY", directory);
223 const QString &
filename,
const QString &date_modified)
226 QDateTime dt = fi.lastModified();
230 return !old_dt.isValid() || (dt > old_dt);
232 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to stat file: %1")
254 QString extension =
filename.section(
'.', -1 ) ;
256 directory.remove(0, startDir.length());
257 directory = directory.section(
'/', 0, -2);
262 if (nameFilter.indexOf(extension.toLower()) > -1)
264 QString name =
filename.section(
'/', -1);
267 query.
prepare(
"INSERT INTO music_albumart "
268 "SET filename = :FILE, directory_id = :DIRID, "
269 "imagetype = :TYPE, hostname = :HOSTNAME;");
288 LOG(VB_GENERAL, LOG_WARNING, QString(
"Ignoring filename with unsupported filename: '%1'").arg(
filename));
292 LOG(VB_FILE, LOG_INFO, QString(
"Reading metadata from %1").arg(
filename));
299 QString album_cache_string;
312 album_cache_string = QString::number(data->
getArtistId()) +
"#"
313 + data->
Album().toLower();
340 album_cache_string = QString::number(data->
getArtistId()) +
"#"
341 + data->
Album().toLower();
372 LOG(VB_GENERAL, LOG_INFO,
"Cleaning old entries from music database");
378 if (!query.
exec(
"SELECT g.genre_id FROM music_genres g "
379 "LEFT JOIN music_songs s ON g.genre_id=s.genre_id "
380 "WHERE s.genre_id IS NULL;"))
381 MythDB::DBError(
"MusicFileScanner::cleanDB - select music_genres", query);
383 deletequery.
prepare(
"DELETE FROM music_genres WHERE genre_id=:GENREID");
386 int genreid = query.
value(0).toInt();
387 deletequery.
bindValue(
":GENREID", genreid);
388 if (!deletequery.
exec())
394 if (!query.
exec(
"SELECT a.album_id FROM music_albums a "
395 "LEFT JOIN music_songs s ON a.album_id=s.album_id "
396 "WHERE s.album_id IS NULL;"))
397 MythDB::DBError(
"MusicFileScanner::cleanDB - select music_albums", query);
399 deletequery.
prepare(
"DELETE FROM music_albums WHERE album_id=:ALBUMID");
402 int albumid = query.
value(0).toInt();
403 deletequery.
bindValue(
":ALBUMID", albumid);
404 if (!deletequery.
exec())
410 if (!query.
exec(
"SELECT a.artist_id FROM music_artists a "
411 "LEFT JOIN music_songs s ON a.artist_id=s.artist_id "
412 "LEFT JOIN music_albums l ON a.artist_id=l.artist_id "
413 "WHERE s.artist_id IS NULL AND l.artist_id IS NULL"))
414 MythDB::DBError(
"MusicFileScanner::cleanDB - select music_artists", query);
417 deletequery.
prepare(
"DELETE FROM music_artists WHERE artist_id=:ARTISTID");
420 int artistid = query.
value(0).toInt();
421 deletequery.
bindValue(
":ARTISTID", artistid);
422 if (!deletequery.
exec())
433 if (!query.
exec(
"SELECT d.directory_id, d.parent_id FROM music_directories d "
434 "LEFT JOIN music_songs s ON d.directory_id=s.directory_id "
435 "WHERE s.directory_id IS NULL ORDER BY directory_id DESC;"))
436 MythDB::DBError(
"MusicFileScanner::cleanDB - select music_directories", query);
438 deletequery.
prepare(
"DELETE FROM music_directories WHERE directory_id=:DIRECTORYID");
441 parentquery.
prepare(
"SELECT COUNT(*) FROM music_directories "
442 "WHERE parent_id=:DIRECTORYID ");
445 dirnamequery.
prepare(
"SELECT path FROM music_directories "
446 "WHERE directory_id=:DIRECTORYID ");
448 int deletedCount = 1;
450 while (deletedCount > 0)
459 int directoryid = query.
value(0).toInt();
462 parentquery.
bindValue(
":DIRECTORYID", directoryid);
463 if (!parentquery.
exec())
465 MythDB::DBError(
"MusicFileScanner::cleanDB - get parent directory count",
469 if (!parentquery.
next())
471 int parentCount = parentquery.
value(0).toInt();
472 if (parentCount != 0)
477 dirnamequery.
bindValue(
":DIRECTORYID", directoryid);
478 if (dirnamequery.
exec() && dirnamequery.
next())
480 LOG(VB_GENERAL, LOG_DEBUG,
481 QString(
"MusicFileScanner deleted directory %1 %2")
482 .arg(directoryid,5).arg(dirnamequery.
value(0).toString()));
485 deletequery.
bindValue(
":DIRECTORYID", directoryid);
486 if (!deletequery.
exec())
487 MythDB::DBError(
"MusicFileScanner::cleanDB - delete music_directories",
491 LOG(VB_GENERAL, LOG_INFO,
492 QString(
"MusicFileScanner deleted %1 directory entries")
497 if (!query.
exec(
"SELECT a.albumart_id FROM music_albumart a LEFT JOIN "
498 "music_songs s ON a.song_id=s.song_id WHERE "
499 "embedded='1' AND s.song_id IS NULL;"))
500 MythDB::DBError(
"MusicFileScanner::cleanDB - select music_albumart", query);
502 deletequery.
prepare(
"DELETE FROM music_albumart WHERE albumart_id=:ALBUMARTID");
505 int albumartid = query.
value(0).toInt();
506 deletequery.
bindValue(
":ALBUMARTID", albumartid);
507 if (!deletequery.
exec())
526 sqlfilename.remove(0, startDir.length());
528 QString directory = sqlfilename.section(
'/', 0, -2 ) ;
529 sqlfilename = sqlfilename.section(
'/', -1 ) ;
531 QString extension = sqlfilename.section(
'.', -1 ) ;
534 "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
536 if (nameFilter.indexOf(extension.toLower()) > -1)
539 query.
prepare(
"DELETE FROM music_albumart WHERE filename= :FILE AND "
540 "directory_id= :DIRID;");
555 query.
prepare(
"DELETE FROM music_songs WHERE filename = :NAME ;");
558 MythDB::DBError(
"MusicFileScanner::RemoveFileFromDB - deleting music_songs",
577 dbFilename.remove(0, startDir.length());
580 directory.remove(0, startDir.length());
581 directory = directory.section(
'/', 0, -2);
586 if (db_meta && disk_meta)
588 if (db_meta->
ID() <= 0)
590 LOG(VB_GENERAL, LOG_ERR, QString(
"Asked to update track with "
592 .arg(db_meta->
ID()));
598 disk_meta->
setID(db_meta->
ID());
603 QString album_cache_string;
616 album_cache_string = QString::number(disk_meta->
getArtistId()) +
"#" +
617 disk_meta->
Album().toLower();
645 album_cache_string = QString::number(disk_meta->
getArtistId()) +
"#" +
646 disk_meta->
Album().toLower();
672 if (!lastRun.isEmpty())
675 if (dtLastRun.isValid())
677 static constexpr int64_t kOneHour {60LL * 60};
680 LOG(VB_GENERAL, LOG_INFO,
"Music file scanner has been running for more than 60 minutes. Lets reset and try again");
688 LOG(VB_GENERAL, LOG_INFO,
"Music file scanner is already running");
698 LOG(VB_GENERAL, LOG_INFO,
"Music file scanner started");
702 QString status = QString(
"running");
710 MusicLoadedMap::Iterator iter;
712 for (
int x = 0; x < dirList.count(); x++)
714 QString startDir = dirList[x];
716 LOG(VB_GENERAL, LOG_INFO, QString(
"Searching '%1' for music files").arg(startDir));
727 LOG(VB_GENERAL, LOG_INFO,
"Updating database");
742 for (iter = music_files.begin(); iter != music_files.end(); iter++)
755 for (iter = art_files.begin(); iter != art_files.end(); iter++)
771 QString trackStatus = QString(
"total tracks found: %1 (unchanged: %2, added: %3, removed: %4, updated %5)")
774 QString coverartStatus = QString(
"total coverart found: %1 (unchanged: %2, added: %3, removed: %4, updated %5)")
779 LOG(VB_GENERAL, LOG_INFO,
"Music file scanner finished ");
780 LOG(VB_GENERAL, LOG_INFO, trackStatus);
781 LOG(VB_GENERAL, LOG_INFO, coverartStatus);
788 status = QString(
"success - %1 - %2").arg(trackStatus, coverartStatus);
801 MusicLoadedMap::Iterator iter;
804 query.
prepare(
"SELECT CONCAT_WS('/', path, filename), date_modified "
805 "FROM music_songs LEFT JOIN music_directories ON "
806 "music_songs.directory_id=music_directories.directory_id "
807 "WHERE filename NOT LIKE BINARY ('%://%') "
808 "AND hostname = :HOSTNAME");
815 LOG(VB_GENERAL, LOG_INFO,
"Checking tracks");
826 iter = music_files.find(name);
827 if (iter != music_files.end())
831 if (iter != music_files.end())
840 music_files.erase(iter);
860 MusicLoadedMap::Iterator iter;
863 query.
prepare(
"SELECT CONCAT_WS('/', path, filename) "
864 "FROM music_albumart "
865 "LEFT JOIN music_directories ON music_albumart.directory_id=music_directories.directory_id "
866 "WHERE music_albumart.embedded = 0 "
867 "AND music_albumart.hostname = :HOSTNAME");
874 LOG(VB_GENERAL, LOG_INFO,
"Checking artwork");
885 iter = music_files.find(name);
886 if (iter != music_files.end())
890 if (iter != music_files.end())
895 music_files.erase(iter);
void dumpToDatabase(void)
saves or updates the image details in the DB
static ImageType guessImageType(const QString &filename)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
int numRowsAffected() const
bool isActive(void) const
bool seek(int where, bool relative=false)
Wrap QSqlQuery::seek(int,bool)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
QVariant lastInsertId()
Return the id of the last inserted row.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
static void updateLastRunEnd(void)
QMap< QString, MusicFileData > MusicLoadedMap
void UpdateFileInDB(const QString &filename, const QString &startDir)
Updates a file in the database.
void RemoveFileFromDB(const QString &filename, const QString &startDir)
Removes a file from the database.
static bool IsArtFile(const QString &filename)
static void updateLastRunStart(void)
void SearchDirs(const QStringList &dirList)
Scan a list of directories recursively for music and albumart. Inserts, updates and removes any files...
static bool HasFileChanged(const QString &filename, const QString &date_modified)
Check if file has been modified since given date/time.
static int GetDirectoryId(const QString &directory, int parentid)
Get an ID for the given directory from the database. If it doesn't already exist in the database,...
MusicFileScanner(bool force=false)
void ScanMusic(MusicLoadedMap &music_files)
Check a list of files against musics files already in the database.
void BuildFileList(QString &directory, MusicLoadedMap &music_files, MusicLoadedMap &art_files, int parentid)
Builds a list of all the files found descending recursively into the given directory.
void AddFileToDB(const QString &filename, const QString &startDir)
Insert file details into database. If it is an audio file, read the metadata and insert that informat...
static bool IsRunning(void)
static void updateLastRunStatus(QString &status)
static bool IsMusicFile(const QString &filename)
static void cleanDB()
Clear orphaned entries from the genre, artist, album and albumart tables.
void ScanArtwork(MusicLoadedMap &music_files)
Check a list of files against images already in the database.
QString GetHostName(void)
void SaveSetting(const QString &key, int newValue)
QString GetSetting(const QString &key, const QString &defaultval="")
void SendMessage(const QString &message)
static void DBError(const QString &where, const MSqlQuery &query)
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
MusicFileLocation location