diff --git a/src/android/androidmusiclistener.cpp b/src/android/androidmusiclistener.cpp index a96e4273..d20f84b1 100644 --- a/src/android/androidmusiclistener.cpp +++ b/src/android/androidmusiclistener.cpp @@ -1,223 +1,219 @@ /* * Copyright 2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "androidmusiclistener.h" #include "databaseinterface.h" -#include "musicaudiotrack.h" #include #include // C++ code #include #include #include #include #include class AndroidMusicListenerPrivate { public: QString mSourceName; QHash mAllFiles; - QList mNewTracks; + DataTypes::ListTrackDataType mNewTracks; QHash mCovers; }; static void tracksAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicTracksScanStarted(); } static void sentMusicFile(JNIEnv */*env*/, jobject /*obj*/, jstring musicFile) { QAndroidJniObject musicFileJavaString(musicFile); AndroidMusicListener::currentInstance()->newMusicTrack(musicFileJavaString.toString()); } static void tracksAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicTracksScanFinished(); } static void albumsAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicAlbumsScanStarted(); } static void sentMusicAlbum(JNIEnv */*env*/, jobject /*obj*/, jstring musicAlbum) { QAndroidJniObject musicAlbumJavaString(musicAlbum); AndroidMusicListener::currentInstance()->newMusicAlbum(musicAlbumJavaString.toString()); } static void albumsAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/) { AndroidMusicListener::currentInstance()->androidMusicAlbumsScanFinished(); } AndroidMusicListener::AndroidMusicListener(QObject *parent) : QObject(parent), d(std::make_unique()) { AndroidMusicListener::mCurrentInstance = this; d->mSourceName = QStringLiteral("Android"); registerNativeMethods(); } DatabaseInterface *AndroidMusicListener::databaseInterface() const { return nullptr; } void AndroidMusicListener::registerNativeMethods() { JNINativeMethod methods[] {{"androidMusicScanTracksStarting", "()V", reinterpret_cast(tracksAndroidScanStarted)}, {"sendMusicFile", "(Ljava/lang/String;)V", reinterpret_cast(sentMusicFile)}, {"androidMusicScanTracksFinishing", "()V", reinterpret_cast(tracksAndroidScanFinished)}, {"androidMusicScanAlbumsStarting", "()V", reinterpret_cast(albumsAndroidScanStarted)}, {"sendMusicAlbum", "(Ljava/lang/String;)V", reinterpret_cast(sentMusicAlbum)}, {"androidMusicScanAlbumsFinishing", "()V", reinterpret_cast(albumsAndroidScanFinished)}, }; QAndroidJniObject javaClass("org/kde/elisa/ElisaAndroidMusicScanner"); QAndroidJniEnvironment env; jclass objectClass = env->GetObjectClass(javaClass.object()); env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0])); env->DeleteLocalRef(objectClass); } AndroidMusicListener *AndroidMusicListener::mCurrentInstance = nullptr; AndroidMusicListener *AndroidMusicListener::currentInstance() { return mCurrentInstance; } void AndroidMusicListener::androidMusicTracksScanStarted() { } void AndroidMusicListener::newMusicTrack(const QString &trackDescription) { auto trackData = trackDescription.split(QStringLiteral("||")); - auto newTrack = MusicAudioTrack{}; - newTrack.setTitle(trackData[1]); + auto newTrack = DataTypes::TrackDataType{}; + newTrack[DataTypes::TitleRole] = trackData[1]; bool conversionOK = false; if (trackData[2] != QLatin1String("null")) { - newTrack.setTrackNumber(trackData[2].toInt(&conversionOK)); + newTrack[DataTypes::TrackNumberRole] = trackData[2].toInt(&conversionOK); if (!conversionOK) { qInfo() << "newMusicTrack" << trackData[1] << trackData[2]; } } if (trackData[3] != QLatin1String("null")) { - newTrack.setYear(trackData[3].toInt(&conversionOK)); + newTrack[DataTypes::YearRole] = trackData[3].toInt(&conversionOK); if (!conversionOK) { qInfo() << "newMusicTrack" << trackData[1] << trackData[3]; } } if (trackData[4] != QLatin1String("null")) { - newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(trackData[4].toInt())); + newTrack[DataTypes::DurationRole] = QTime::fromMSecsSinceStartOfDay(trackData[4].toInt()); } - newTrack.setResourceURI(QUrl::fromLocalFile(trackData[5])); - newTrack.setArtist(trackData[6]); - newTrack.setAlbumName(trackData[8]); - newTrack.setComposer(trackData[10]); + newTrack[DataTypes::ResourceRole] = QUrl::fromLocalFile(trackData[5]); + newTrack[DataTypes::ArtistRole] = trackData[6]; + newTrack[DataTypes::AlbumRole] = trackData[8]; + newTrack[DataTypes::ComposerRole] = trackData[10]; d->mNewTracks.push_back(newTrack); } void AndroidMusicListener::androidMusicTracksScanFinished() { } void AndroidMusicListener::androidMusicAlbumsScanStarted() { } void AndroidMusicListener::newMusicAlbum(const QString &albumDescription) { auto albumData = albumDescription.split(QStringLiteral("||")); if (albumData[2] != QLatin1String("null)")) { d->mCovers[albumData[1]] = QUrl::fromLocalFile(albumData[2]); } } void AndroidMusicListener::androidMusicAlbumsScanFinished() { - Q_EMIT tracksList(d->mNewTracks, d->mCovers, d->mSourceName); + Q_EMIT tracksList(d->mNewTracks, d->mCovers); } void AndroidMusicListener::setDatabaseInterface(DatabaseInterface *model) { if (model) { connect(this, &AndroidMusicListener::tracksList, model, &DatabaseInterface::insertTracksList); connect(this, &AndroidMusicListener::removedTracksList, model, &DatabaseInterface::removeTracksList); - connect(this, &AndroidMusicListener::modifyTracksList, model, &DatabaseInterface::modifyTracksList); connect(this, &AndroidMusicListener::askRestoredTracks, model, &DatabaseInterface::askRestoredTracks); connect(model, &DatabaseInterface::restoredTracks, this, &AndroidMusicListener::restoredTracks); } Q_EMIT databaseInterfaceChanged(); if (model) { QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } } void AndroidMusicListener::applicationAboutToQuit() { } void AndroidMusicListener::quitListener() { applicationAboutToQuit(); Q_EMIT clearDatabase(d->mSourceName); } -void AndroidMusicListener::restoredTracks(const QString &musicSource, QHash allFiles) +void AndroidMusicListener::restoredTracks(QHash allFiles) { - if (d->mSourceName == musicSource) { - d->mAllFiles = allFiles; + d->mAllFiles = allFiles; - QAndroidJniObject::callStaticMethod("org/kde/elisa/ElisaAndroidMusicScanner", - "listAudioFiles", - "(Landroid/content/Context;)V", - QtAndroid::androidContext().object()); - } + QAndroidJniObject::callStaticMethod("org/kde/elisa/ElisaAndroidMusicScanner", + "listAudioFiles", + "(Landroid/content/Context;)V", + QtAndroid::androidContext().object()); } void AndroidMusicListener::init() { Q_EMIT askRestoredTracks(d->mSourceName); } AndroidMusicListener::~AndroidMusicListener() = default; #include "moc_androidmusiclistener.cpp" diff --git a/src/android/androidmusiclistener.h b/src/android/androidmusiclistener.h index 1659ebbb..bf5fab31 100644 --- a/src/android/androidmusiclistener.h +++ b/src/android/androidmusiclistener.h @@ -1,107 +1,107 @@ /* * Copyright 2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef ANDROIDMUSICLISTENER_H #define ANDROIDMUSICLISTENER_H +#include "datatypes.h" + #include #include #include #include #include #include class DatabaseInterface; class MusicAudioTrack; class AndroidMusicListenerPrivate; class AndroidMusicListener : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* databaseInterface READ databaseInterface WRITE setDatabaseInterface NOTIFY databaseInterfaceChanged) public: explicit AndroidMusicListener(QObject *parent = nullptr); ~AndroidMusicListener() override; DatabaseInterface* databaseInterface() const; static void registerNativeMethods(); static AndroidMusicListener* currentInstance(); void androidMusicTracksScanStarted(); void newMusicTrack(const QString &trackDescription); void androidMusicTracksScanFinished(); void androidMusicAlbumsScanStarted(); void newMusicAlbum(const QString &albumDescription); void androidMusicAlbumsScanFinished(); Q_SIGNALS: void databaseInterfaceChanged(); void indexingStarted(); void indexingFinished(); void clearDatabase(const QString &listenerName); - void tracksList(const QList &tracks, const QHash &covers, const QString &musicSource); + void tracksList(const DataTypes::ListTrackDataType &tracks, const QHash &covers); void removedTracksList(const QList &removedTracks); - void modifyTracksList(const QList &modifiedTracks, const QHash &covers, const QString &musicSource); - void askRestoredTracks(const QString &musicSource); public Q_SLOTS: void setDatabaseInterface(DatabaseInterface* databaseInterface); void applicationAboutToQuit(); void quitListener(); - void restoredTracks(const QString &musicSource, QHash allFiles); + void restoredTracks(QHash allFiles); void init(); private: static AndroidMusicListener* mCurrentInstance; std::unique_ptr d; }; #endif // ANDROIDMUSICLISTENER_H diff --git a/src/filescanner.cpp b/src/filescanner.cpp index c96926a0..55e045de 100644 --- a/src/filescanner.cpp +++ b/src/filescanner.cpp @@ -1,268 +1,267 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "filescanner.h" #include "config-upnp-qt.h" #include "abstractfile/indexercommon.h" #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND #include #include #include #include #include #include #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include #endif #endif #include #include #include #include #include #include class FileScannerPrivate { public: #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND KFileMetaData::ExtractorCollection mAllExtractors; KFileMetaData::PropertyMap mAllProperties; KFileMetaData::EmbeddedImageData mImageScanner; #endif QMimeDatabase mMimeDb; #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND const QHash propertyTranslation = { {KFileMetaData::Property::Artist, DataTypes::ColumnsRoles::ArtistRole}, {KFileMetaData::Property::AlbumArtist, DataTypes::ColumnsRoles::AlbumArtistRole}, {KFileMetaData::Property::Genre, DataTypes::ColumnsRoles::GenreRole}, {KFileMetaData::Property::Composer, DataTypes::ColumnsRoles::ComposerRole}, {KFileMetaData::Property::Lyricist, DataTypes::ColumnsRoles::LyricistRole}, {KFileMetaData::Property::Title, DataTypes::ColumnsRoles::TitleRole}, {KFileMetaData::Property::Album, DataTypes::ColumnsRoles::AlbumRole}, {KFileMetaData::Property::TrackNumber, DataTypes::ColumnsRoles::TrackNumberRole}, {KFileMetaData::Property::DiscNumber, DataTypes::ColumnsRoles::DiscNumberRole}, {KFileMetaData::Property::ReleaseYear, DataTypes::ColumnsRoles::YearRole}, {KFileMetaData::Property::Lyrics, DataTypes::ColumnsRoles::LyricsRole}, {KFileMetaData::Property::Comment, DataTypes::ColumnsRoles::CommentRole}, {KFileMetaData::Property::Rating, DataTypes::ColumnsRoles::RatingRole}, {KFileMetaData::Property::Channels, DataTypes::ColumnsRoles::ChannelsRole}, {KFileMetaData::Property::SampleRate, DataTypes::ColumnsRoles::SampleRateRole}, {KFileMetaData::Property::BitRate, DataTypes::ColumnsRoles::BitRateRole}, {KFileMetaData::Property::Duration, DataTypes::ColumnsRoles::DurationRole}, }; #endif const QStringList constSearchStrings = { QStringLiteral("*[Cc]over*.jpg"), QStringLiteral("*[Cc]over*.png"), QStringLiteral("*[Ff]older*.jpg"), QStringLiteral("*[Ff]older*.png"), QStringLiteral("*[Ff]ront*.jpg"), QStringLiteral("*[Ff]ront*.png") }; }; FileScanner::FileScanner() : d(std::make_unique()) { } bool FileScanner::shouldScanFile(const QString &scanFile) { const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(scanFile); return fileMimeType.name().startsWith(QLatin1String("audio/")); } FileScanner::~FileScanner() = default; DataTypes::TrackDataType FileScanner::scanOneFile(const QUrl &scanFile) { DataTypes::TrackDataType newTrack; if (!scanFile.isLocalFile() && !scanFile.scheme().isEmpty()) { return newTrack; } auto localFileName = scanFile.toLocalFile(); QFileInfo scanFileInfo(localFileName); newTrack[DataTypes::FileModificationTime] = scanFileInfo.metadataChangeTime(); newTrack[DataTypes::ResourceRole] = scanFile; newTrack[DataTypes::RatingRole] = 0; #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(localFileName); if (!fileMimeType.name().startsWith(QLatin1String("audio/"))) { return newTrack; } QString mimetype = fileMimeType.name(); QList exList = d->mAllExtractors.fetchExtractors(mimetype); if (exList.isEmpty()) { return newTrack; } KFileMetaData::Extractor* ex = exList.first(); KFileMetaData::SimpleExtractionResult result(localFileName, mimetype, KFileMetaData::ExtractionResult::ExtractMetaData); ex->extract(&result); d->mAllProperties = result.properties(); scanProperties(localFileName, newTrack); qCDebug(orgKdeElisaIndexer()) << "scanOneFile" << scanFile << "using KFileMetaData" << newTrack; #else Q_UNUSED(scanFile) - Q_UNUSED(mimeDatabase) qCDebug(orgKdeElisaIndexer()) << "scanOneFile" << scanFile << "no metadata provider" << newTrack; #endif return newTrack; } void FileScanner::scanProperties(const Baloo::File &match, DataTypes::TrackDataType &trackData) { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mAllProperties = match.properties(); scanProperties(match.path(), trackData); qCDebug(orgKdeElisaIndexer()) << "scanProperties" << match.path() << "using Baloo" << trackData; #else Q_UNUSED(match) Q_UNUSED(trackData) qCDebug(orgKdeElisaIndexer()) << "scanProperties" << "no metadata provider" << trackData; #endif } void FileScanner::scanProperties(const QString &localFileName, DataTypes::TrackDataType &trackData) { #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND if (d->mAllProperties.isEmpty()) { return; } using entry = std::pair; auto rangeBegin = d->mAllProperties.constKeyValueBegin(); QVariant value; while (rangeBegin != d->mAllProperties.constKeyValueEnd()) { auto key = (*rangeBegin).first; auto rangeEnd = std::find_if(rangeBegin, d->mAllProperties.constKeyValueEnd(), [key](entry e) { return e.first != key; }); auto distance = std::distance(rangeBegin, rangeEnd); if (distance > 1) { QStringList list; list.reserve(static_cast(distance)); std::for_each(rangeBegin, rangeEnd, [&list](entry s) { list.append(s.second.toString()); }); value = QLocale().createSeparatedList(list); } else { value = (*rangeBegin).second; if (value.canConvert()) { value = QLocale().createSeparatedList(value.toStringList()); } } auto translatedKey = d->propertyTranslation.find(key); if (translatedKey.value() == DataTypes::DurationRole) { trackData.insert(translatedKey.value(), QTime::fromMSecsSinceStartOfDay(int(1000 * (*rangeBegin).second.toDouble()))); } else if (translatedKey != d->propertyTranslation.end()) { trackData.insert(translatedKey.value(), (*rangeBegin).second); } rangeBegin = rangeEnd; } #if !defined Q_OS_ANDROID && !defined Q_OS_WIN auto fileData = KFileMetaData::UserMetaData(localFileName); QString comment = fileData.userComment(); if (!comment.isEmpty()) { trackData[DataTypes::CommentRole] = comment; } int rating = fileData.rating(); if (rating >= 0) { trackData[DataTypes::RatingRole] = rating; } #endif #else Q_UNUSED(localFileName) Q_UNUSED(trackData) #endif } QUrl FileScanner::searchForCoverFile(const QString &localFileName) { QFileInfo trackFilePath(localFileName); QDir trackFileDir = trackFilePath.absoluteDir(); trackFileDir.setFilter(QDir::Files); trackFileDir.setNameFilters(d->constSearchStrings); QFileInfoList coverFiles = trackFileDir.entryInfoList(); if (coverFiles.isEmpty()) { QString dirNamePattern = QLatin1String("*") + trackFileDir.dirName() + QLatin1String("*"); QString dirNameNoSpaces = dirNamePattern.remove(QLatin1Char(' ')); QStringList filters = { dirNamePattern + QStringLiteral(".jpg"), dirNamePattern + QStringLiteral(".png"), dirNameNoSpaces + QStringLiteral(".jpg"), dirNameNoSpaces + QStringLiteral(".png") }; trackFileDir.setNameFilters(filters); coverFiles = trackFileDir.entryInfoList(); } if (coverFiles.isEmpty()) { return QUrl(); } return QUrl::fromLocalFile(coverFiles.first().absoluteFilePath()); } bool FileScanner::checkEmbeddedCoverImage(const QString &localFileName) { #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND auto imageData = d->mImageScanner.imageData(localFileName); if (imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)) { if (!imageData[KFileMetaData::EmbeddedImageData::FrontCover].isEmpty()) { return true; } } #else Q_UNUSED(localFileName) #endif return false; } diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index 6fac2092..aa6c0f33 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,556 +1,554 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "musiclistenersmanager.h" #include "config-upnp-qt.h" #include "indexersManager.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnplistener.h" #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include "baloo/baloolistener.h" #include "baloo/baloodetector.h" #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND #include "android/androidmusiclistener.h" #endif #include "databaseinterface.h" #include "mediaplaylist.h" #include "file/filelistener.h" #include "file/localfilelisting.h" #include "trackslistener.h" #include "elisaapplication.h" #include "elisa_settings.h" #include "modeldataloader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class MusicListenersManagerPrivate { public: QThread mDatabaseThread; QThread mListenerThread; #if defined UPNPQT_FOUND && UPNPQT_FOUND UpnpListener mUpnpListener; #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND BalooDetector mBalooDetector; BalooListener mBalooListener; #endif FileListener mFileListener; #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND std::unique_ptr mAndroidMusicListener; #endif DatabaseInterface mDatabaseInterface; std::unique_ptr mTracksListener; QFileSystemWatcher mConfigFileWatcher; ElisaApplication *mElisaApplication = nullptr; int mImportedTracksCount = 0; bool mIndexerBusy = false; bool mFileSystemIndexerActive = false; bool mBalooIndexerActive = false; bool mBalooIndexerAvailable = false; bool mAndroidIndexerActive = false; bool mAndroidIndexerAvailable = false; }; MusicListenersManager::MusicListenersManager(QObject *parent) : QObject(parent), d(std::make_unique()) { connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, this, &MusicListenersManager::increaseImportedTracksCount); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND connect(&d->mBalooDetector, &BalooDetector::balooAvailabilityChanged, this, &MusicListenersManager::balooAvailabilityChanged); #endif connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone, this, &MusicListenersManager::databaseReady); connect(this, &MusicListenersManager::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); connect(&d->mDatabaseInterface, &DatabaseInterface::cleanedDatabase, this, &MusicListenersManager::cleanedDatabase); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &MusicListenersManager::applicationAboutToQuit); connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged, this, &MusicListenersManager::configChanged); d->mListenerThread.start(); d->mDatabaseThread.start(); d->mDatabaseInterface.moveToThread(&d->mDatabaseThread); const auto &localDataPaths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); auto databaseFileName = QString(); if (!localDataPaths.isEmpty()) { QDir myDataDirectory; myDataDirectory.mkpath(localDataPaths.first()); databaseFileName = localDataPaths.first() + QStringLiteral("/elisaDatabase.db"); } QMetaObject::invokeMethod(&d->mDatabaseInterface, "init", Qt::QueuedConnection, Q_ARG(QString, QStringLiteral("listeners")), Q_ARG(QString, databaseFileName)); qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is inactive"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; } MusicListenersManager::~MusicListenersManager() = default; DatabaseInterface *MusicListenersManager::viewDatabase() const { return &d->mDatabaseInterface; } void MusicListenersManager::subscribeForTracks(MediaPlayList *client) { createTracksListener(); connect(d->mTracksListener.get(), &TracksListener::trackHasChanged, client, &MediaPlayList::trackChanged); connect(d->mTracksListener.get(), &TracksListener::trackHasBeenRemoved, client, &MediaPlayList::trackRemoved); connect(d->mTracksListener.get(), &TracksListener::tracksListAdded, client, &MediaPlayList::tracksListAdded); connect(client, &MediaPlayList::newEntryInList, d->mTracksListener.get(), &TracksListener::newEntryInList); connect(client, &MediaPlayList::newUrlInList, d->mTracksListener.get(), &TracksListener::newUrlInList); connect(client, &MediaPlayList::newTrackByNameInList, d->mTracksListener.get(), &TracksListener::trackByNameInList); } int MusicListenersManager::importedTracksCount() const { return d->mImportedTracksCount; } ElisaApplication *MusicListenersManager::elisaApplication() const { return d->mElisaApplication; } TracksListener *MusicListenersManager::tracksListener() const { return d->mTracksListener.get(); } bool MusicListenersManager::indexerBusy() const { return d->mIndexerBusy; } bool MusicListenersManager::fileSystemIndexerActive() const { return d->mFileSystemIndexerActive; } bool MusicListenersManager::balooIndexerActive() const { return d->mBalooIndexerActive; } bool MusicListenersManager::balooIndexerAvailable() const { return d->mBalooIndexerAvailable; } bool MusicListenersManager::androidIndexerActive() const { return d->mAndroidIndexerActive; } bool MusicListenersManager::androidIndexerAvailable() const { return d->mAndroidIndexerAvailable; } auto MusicListenersManager::initializeRootPath() { auto initialRootPath = QStringList{}; auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); for (const auto &musicPath : qAsConst(systemMusicPaths)) { initialRootPath.push_back(musicPath); } Elisa::ElisaConfiguration::setRootPath(initialRootPath); Elisa::ElisaConfiguration::self()->save(); return initialRootPath; } void MusicListenersManager::databaseReady() { auto initialRootPath = Elisa::ElisaConfiguration::rootPath(); if (initialRootPath.isEmpty()) { initializeRootPath(); } d->mConfigFileWatcher.addPath(Elisa::ElisaConfiguration::self()->config()->name()); configChanged(); } void MusicListenersManager::applicationAboutToQuit() { d->mDatabaseInterface.applicationAboutToQuit(); Q_EMIT applicationIsTerminating(); d->mDatabaseThread.exit(); d->mDatabaseThread.wait(); d->mListenerThread.exit(); d->mListenerThread.wait(); } void MusicListenersManager::showConfiguration() { auto configureAction = d->mElisaApplication->action(QStringLiteral("options_configure")); configureAction->trigger(); } void MusicListenersManager::setElisaApplication(ElisaApplication *elisaApplication) { if (d->mElisaApplication == elisaApplication) { return; } d->mElisaApplication = elisaApplication; emit elisaApplicationChanged(); } void MusicListenersManager::playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError) { qCDebug(orgKdeElisaIndexersManager) << "MusicListenersManager::playBackError" << sourceInError; if (playerError == QMediaPlayer::ResourceError) { Q_EMIT removeTracksInError({sourceInError}); if (sourceInError.isLocalFile()) { Q_EMIT displayTrackError(sourceInError.toLocalFile()); } else { Q_EMIT displayTrackError(sourceInError.toString()); } } } void MusicListenersManager::deleteElementById(ElisaUtils::PlayListEntryType entryType, qulonglong databaseId) { switch(entryType) { case ElisaUtils::Radio: QMetaObject::invokeMethod(&d->mDatabaseInterface, "removeRadio", Qt::QueuedConnection, Q_ARG(qulonglong, databaseId)); break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Track: case ElisaUtils::FileName: case ElisaUtils::Unknown: break; } } void MusicListenersManager::connectModel(ModelDataLoader *dataLoader) { dataLoader->moveToThread(&d->mDatabaseThread); } void MusicListenersManager::resetMusicData() { Q_EMIT clearDatabase(); } void MusicListenersManager::configChanged() { auto currentConfiguration = Elisa::ElisaConfiguration::self(); d->mConfigFileWatcher.addPath(currentConfiguration->config()->name()); currentConfiguration->load(); currentConfiguration->read(); //resolve symlinks QStringList allRootPaths; auto inputRootPath = currentConfiguration->rootPath(); for (const auto &onePath : inputRootPath) { auto workPath = onePath; if (workPath.startsWith(QLatin1String("file:/"))) { auto urlPath = QUrl{workPath}; workPath = urlPath.toLocalFile(); } QFileInfo pathFileInfo(workPath); auto directoryPath = pathFileInfo.canonicalFilePath(); if (!directoryPath.isEmpty()) { if (directoryPath.rightRef(1) != QLatin1Char('/')) { directoryPath.append(QLatin1Char('/')); } allRootPaths.push_back(directoryPath); } } if (allRootPaths.isEmpty()) { allRootPaths = initializeRootPath(); } d->mFileListener.setAllRootPaths(allRootPaths); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setAllRootPaths(allRootPaths); #endif if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) { testBalooIndexerAvailability(); } #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (d->mBalooIndexerAvailable && !d->mBalooIndexerActive && d->mBalooListener.canHandleRootPaths()) { qCDebug(orgKdeElisaIndexersManager()) << "trigger start of baloo file indexer"; QMetaObject::invokeMethod(d->mFileListener.fileListing(), "stop", Qt::BlockingQueuedConnection); d->mFileSystemIndexerActive = false; startBalooIndexing(); } else if (!d->mFileSystemIndexerActive && d->mBalooIndexerActive && !d->mBalooListener.canHandleRootPaths()) { qCDebug(orgKdeElisaIndexersManager()) << "trigger stop of baloo file indexer"; QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "stop", Qt::BlockingQueuedConnection); d->mBalooIndexerActive = false; startLocalFileSystemIndexing(); } #endif if (d->mBalooIndexerActive) { qCInfo(orgKdeElisaIndexersManager()) << "trigger init of baloo file indexer"; #if defined KF5Baloo_FOUND && KF5Baloo_FOUND QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); #endif } else if (d->mFileSystemIndexerActive) { qCInfo(orgKdeElisaIndexersManager()) << "trigger init of local file indexer"; QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection); } #if defined UPNPQT_FOUND && UPNPQT_FOUND d->mUpnpListener.setDatabaseInterface(&d->mDatabaseInterface); d->mUpnpListener.moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mUpnpListener, &UpnpListener::applicationAboutToQuit, Qt::DirectConnection); #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND if (!d->mAndroidMusicListener) { d->mAndroidMusicListener = std::make_unique(); d->mAndroidMusicListener->moveToThread(&d->mListenerThread); d->mAndroidMusicListener->setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, d->mAndroidMusicListener.get(), &AndroidMusicListener::applicationAboutToQuit, Qt::DirectConnection); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); - connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::clearDatabase, - &d->mDatabaseInterface, &DatabaseInterface::removeAllTracksFromSource); } #endif } void MusicListenersManager::increaseImportedTracksCount(const DataTypes::ListTrackDataType &allTracks) { d->mImportedTracksCount += allTracks.size(); Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::decreaseImportedTracksCount() { --d->mImportedTracksCount; Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::monitorStartingListeners() { d->mIndexerBusy = true; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::monitorEndingListeners() { d->mIndexerBusy = false; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::cleanedDatabase() { d->mImportedTracksCount = 0; Q_EMIT importedTracksCountChanged(); Q_EMIT clearedDatabase(); } void MusicListenersManager::balooAvailabilityChanged() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooDetector.balooAvailability() || !d->mBalooListener.canHandleRootPaths()) { if (d->mBalooDetector.balooAvailability()) { qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available"; d->mBalooIndexerAvailable = true; } #else if (true) { #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooListener.canHandleRootPaths() && d->mBalooDetector.balooAvailability()) { qCInfo(orgKdeElisaIndexersManager()) << "Baloo cannot handle all configured paths: falling back to plain filex indexer"; } #endif if (!d->mFileSystemIndexerActive) { startLocalFileSystemIndexing(); } return; } qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available"; d->mBalooIndexerAvailable = true; Q_EMIT balooIndexerAvailableChanged(); startBalooIndexing(); } void MusicListenersManager::testBalooIndexerAvailability() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooDetector.checkBalooAvailability(); #else qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; d->mBalooIndexerAvailable = false; Q_EMIT balooIndexerAvailableChanged(); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; d->mBalooIndexerActive = false; Q_EMIT balooIndexerActiveChanged(); startLocalFileSystemIndexing(); #endif } void MusicListenersManager::startLocalFileSystemIndexing() { if (d->mFileSystemIndexerActive) { return; } d->mFileListener.setDatabaseInterface(&d->mDatabaseInterface); d->mFileListener.moveToThread(&d->mListenerThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mFileListener, &FileListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mFileListener, &FileListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mFileListener, &FileListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is active"; d->mFileSystemIndexerActive = true; Q_EMIT fileSystemIndexerActiveChanged(); } void MusicListenersManager::startBalooIndexing() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.moveToThread(&d->mListenerThread); d->mBalooListener.setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mBalooListener, &BalooListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mBalooListener, &BalooListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mBalooListener, &BalooListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); connect(&d->mBalooListener, &BalooListener::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is active"; d->mBalooIndexerActive = true; Q_EMIT balooIndexerActiveChanged(); #endif } void MusicListenersManager::createTracksListener() { if (!d->mTracksListener) { d->mTracksListener = std::make_unique(&d->mDatabaseInterface); d->mTracksListener->moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::removeTracksInError, &d->mDatabaseInterface, &DatabaseInterface::removeTracksList); connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, d->mTracksListener.get(), &TracksListener::trackRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, d->mTracksListener.get(), &TracksListener::tracksAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::trackModified, d->mTracksListener.get(), &TracksListener::trackModified); Q_EMIT tracksListenerChanged(); } } #include "moc_musiclistenersmanager.cpp"