diff --git a/src/baloo/localbaloofilelisting.cpp b/src/baloo/localbaloofilelisting.cpp index 02eea47c..dcb15e3f 100644 --- a/src/baloo/localbaloofilelisting.cpp +++ b/src/baloo/localbaloofilelisting.cpp @@ -1,420 +1,420 @@ /* * 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 "localbaloofilelisting.h" #include "baloo/baloocommon.h" #include "elisa_settings.h" #include "elisautils.h" #include "baloo/scheduler.h" #include "baloo/fileindexer.h" #include "baloo/main.h" #include "baloowatcherapplicationadaptor.h" #include "filescanner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class LocalBalooFileListingPrivate { public: Baloo::Query mQuery; QDBusServiceWatcher mServiceWatcher; QScopedPointer mBalooMainInterface; QScopedPointer mBalooIndexer; QScopedPointer mBalooScheduler; BalooWatcherApplicationAdaptor *mDbusAdaptor = nullptr; QAtomicInt mStopRequest = 0; bool mIsRegisteredToBaloo = false; bool mIsRegisteringToBaloo = false; bool mIsRegisteredToBalooWatcher = false; bool mIsRegisteringToBalooWatcher = false; }; LocalBalooFileListing::LocalBalooFileListing(QObject *parent) : AbstractFileListing(parent), d(std::make_unique()) { d->mQuery.addType(QStringLiteral("Audio")); setHandleNewFiles(false); auto sessionBus = QDBusConnection::sessionBus(); d->mDbusAdaptor = new BalooWatcherApplicationAdaptor(this); sessionBus.registerObject(QStringLiteral("/org/kde/BalooWatcherApplication"), d->mDbusAdaptor, QDBusConnection::ExportAllContents); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LocalBalooFileListing::serviceRegistered); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &LocalBalooFileListing::serviceOwnerChanged); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &LocalBalooFileListing::serviceUnregistered); d->mServiceWatcher.setConnection(sessionBus); d->mServiceWatcher.addWatchedService(QStringLiteral("org.kde.baloo")); if (sessionBus.interface()->isServiceRegistered(QStringLiteral("org.kde.baloo"))) { registerToBaloo(); } } LocalBalooFileListing::~LocalBalooFileListing() { } void LocalBalooFileListing::applicationAboutToQuit() { AbstractFileListing::applicationAboutToQuit(); d->mStopRequest = 1; } void LocalBalooFileListing::newBalooFile(const QString &fileName) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::newBalooFile" << fileName; auto scanFileInfo = QFileInfo(fileName); if (!scanFileInfo.exists()) { return; } if (!fileScanner().shouldScanFile(fileName)) { return; } Q_EMIT indexingStarted(); auto newFile = QUrl::fromLocalFile(fileName); auto newTrack = scanOneFile(newFile, scanFileInfo); if (newTrack.isValid()) { QFileInfo newFileInfo(fileName); addFileInDirectory(newFile, QUrl::fromLocalFile(newFileInfo.absoluteDir().absolutePath())); emitNewFiles({newTrack}); } Q_EMIT indexingFinished(); } void LocalBalooFileListing::registeredToBaloo(QDBusPendingCallWatcher *watcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBaloo"; if (!watcher) { return; } QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBaloo" << reply.error().name() << reply.error().message(); d->mIsRegisteredToBaloo = false; } else { d->mIsRegisteredToBaloo = true; } d->mIsRegisteringToBaloo = false; watcher->deleteLater(); } void LocalBalooFileListing::registeredToBalooWatcher(QDBusPendingCallWatcher *watcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBalooWatcher"; if (!watcher) { return; } QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBalooWatcher" << reply.error().name() << reply.error().message(); d->mIsRegisteredToBalooWatcher = false; } else { d->mIsRegisteredToBalooWatcher = true; } d->mIsRegisteringToBalooWatcher = false; watcher->deleteLater(); } void LocalBalooFileListing::registerToBaloo() { if (d->mIsRegisteringToBaloo || d->mIsRegisteringToBalooWatcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "already registering"; return; } qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo"; d->mIsRegisteringToBaloo = true; d->mIsRegisteringToBalooWatcher = true; auto sessionBus = QDBusConnection::sessionBus(); d->mBalooMainInterface.reset(new org::kde::baloo::main(QStringLiteral("org.kde.baloo"), QStringLiteral("/"), sessionBus, this)); if (!d->mBalooMainInterface->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/main interface"; return; } d->mBalooIndexer.reset(new org::kde::baloo::fileindexer(QStringLiteral("org.kde.baloo"), QStringLiteral("/fileindexer"), sessionBus, this)); if (!d->mBalooIndexer->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/fileindexer interface"; return; } connect(d->mBalooIndexer.data(), &org::kde::baloo::fileindexer::finishedIndexingFile, this, &LocalBalooFileListing::newBalooFile); d->mBalooScheduler.reset(new org::kde::baloo::scheduler(QStringLiteral("org.kde.baloo"), QStringLiteral("/scheduler"), sessionBus, this)); if (!d->mBalooScheduler->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/scheduler interface"; return; } qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "call registerMonitor"; auto answer = d->mBalooIndexer->registerMonitor(); if (answer.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::executeInit" << answer.error().name() << answer.error().message(); } auto pendingCallWatcher = new QDBusPendingCallWatcher(answer); connect(pendingCallWatcher, &QDBusPendingCallWatcher::finished, this, &LocalBalooFileListing::registeredToBaloo); if (pendingCallWatcher->isFinished()) { registeredToBaloo(pendingCallWatcher); } auto pendingCall = d->mBalooMainInterface->registerBalooWatcher(QStringLiteral("org.mpris.MediaPlayer2.elisa/org/kde/BalooWatcherApplication")); qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "call registerBalooWatcher"; auto pendingCallWatcher2 = new QDBusPendingCallWatcher(pendingCall); connect(pendingCallWatcher2, &QDBusPendingCallWatcher::finished, this, &LocalBalooFileListing::registeredToBalooWatcher); if (pendingCallWatcher2->isFinished()) { registeredToBalooWatcher(pendingCallWatcher2); } } void LocalBalooFileListing::renamedFiles(const QString &from, const QString &to, const QStringList &listFiles) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::renamedFiles" << from << to << listFiles; } void LocalBalooFileListing::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); Q_UNUSED(newOwner); qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceOwnerChanged" << serviceName << oldOwner << newOwner; if (serviceName == QLatin1String("org.kde.baloo") && !newOwner.isEmpty()) { d->mIsRegisteredToBaloo = false; d->mIsRegisteredToBalooWatcher = false; registerToBaloo(); } } void LocalBalooFileListing::serviceRegistered(const QString &serviceName) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceRegistered" << serviceName; if (serviceName == QLatin1String("org.kde.baloo")) { registerToBaloo(); } } void LocalBalooFileListing::serviceUnregistered(const QString &serviceName) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceUnregistered" << serviceName; if (serviceName == QLatin1String("org.kde.baloo")) { d->mIsRegisteredToBaloo = false; d->mIsRegisteredToBalooWatcher = false; } } void LocalBalooFileListing::executeInit(QHash allFiles) { AbstractFileListing::executeInit(std::move(allFiles)); } void LocalBalooFileListing::triggerRefreshOfContent() { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent"; Q_EMIT indexingStarted(); AbstractFileListing::triggerRefreshOfContent(); const auto &rootPaths = allRootPaths(); bool hasSingleRootPath = (rootPaths.size() == 1); auto singleRootPath = rootPaths.at(0); auto resultIterator = d->mQuery.exec(); auto newFiles = DataTypes::ListTrackDataType(); while(resultIterator.next() && d->mStopRequest == 0) { const auto &fileName = resultIterator.filePath(); if (hasSingleRootPath) { if (!fileName.startsWith(singleRootPath)) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "does not match root paths"; continue; } } else { bool isIncluded = false; for (const auto &oneRootPath : rootPaths) { if (fileName.startsWith(oneRootPath)) { isIncluded = true; break; } } if (!isIncluded) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "does not match root paths"; continue; } } const auto &newFileUrl = QUrl::fromLocalFile(resultIterator.filePath()); auto scanFileInfo = QFileInfo(fileName); if (!scanFileInfo.exists()) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "file does not exists"; continue; } auto itExistingFile = allFiles().find(newFileUrl); if (itExistingFile != allFiles().end()) { - if (*itExistingFile >= scanFileInfo.fileTime(QFile::FileModificationTime)) { + if (*itExistingFile >= scanFileInfo.metadataChangeTime()) { allFiles().erase(itExistingFile); qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "file not modified since last scan"; continue; } } const auto currentDirectory = QUrl::fromLocalFile(scanFileInfo.absoluteDir().absolutePath()); addFileInDirectory(newFileUrl, currentDirectory); const auto &newTrack = scanOneFile(newFileUrl, scanFileInfo); if (newTrack.isValid()) { newFiles.push_back(newTrack); if (newFiles.size() > 500 && d->mStopRequest == 0) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << "insert new tracks in database" << newFiles.count(); emitNewFiles(newFiles); newFiles.clear(); } } else { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "invalid track" << newTrack; } } if (!newFiles.isEmpty() && d->mStopRequest == 0) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << "insert new tracks in database" << newFiles.count(); emitNewFiles(newFiles); } setWaitEndTrackRemoval(false); checkFilesToRemove(); if (!waitEndTrackRemoval()) { Q_EMIT indexingFinished(); } } DataTypes::TrackDataType LocalBalooFileListing::scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo) { DataTypes::TrackDataType trackData; auto localFileName = scanFile.toLocalFile(); Baloo::File match(localFileName); match.load(); fileScanner().scanProperties(match, trackData); trackData[DataTypes::FileModificationTime] = scanFileInfo.metadataChangeTime(); trackData[DataTypes::ResourceRole] = scanFile; if (!trackData.isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::scanOneFile" << scanFile << "falling back to plain file metadata analysis"; trackData = AbstractFileListing::scanOneFile(scanFile, scanFileInfo); } if (trackData.isValid()) { trackData[DataTypes::HasEmbeddedCover] = checkEmbeddedCoverImage(localFileName); addCover(trackData); } else { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::scanOneFile" << scanFile << "invalid track"; } return trackData; } #include "moc_localbaloofilelisting.cpp" diff --git a/src/datatypes.h b/src/datatypes.h index 14c558af..a44d0855 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -1,376 +1,376 @@ /* * Copyright 2016-2017 Matthieu Gallien * Copyright 2019 Alexander Stippich * * 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 . */ #ifndef DATATYPES_H #define DATATYPES_H #include "elisaLib_export.h" #include #include #include #include #include #include #include class ELISALIB_EXPORT DataTypes : public QObject { Q_OBJECT public: enum ColumnsRoles { TitleRole = Qt::UserRole + 1, SecondaryTextRole, ImageUrlRole, ShadowForImageRole, ChildModelRole, DurationRole, StringDurationRole, ArtistRole, AllArtistsRole, HighestTrackRating, AlbumRole, AlbumArtistRole, IsValidAlbumArtistRole, TrackNumberRole, DiscNumberRole, RatingRole, GenreRole, LyricistRole, ComposerRole, CommentRole, YearRole, ChannelsRole, BitRateRole, SampleRateRole, ResourceRole, IdRole, ParentIdRole, DatabaseIdRole, IsSingleDiscAlbumRole, ContainerDataRole, IsPartialDataRole, AlbumIdRole, HasEmbeddedCover, FileModificationTime, FirstPlayDate, LastPlayDate, PlayCounter, PlayFrequency, ElementTypeRole, LyricsRole, }; Q_ENUM(ColumnsRoles) private: using DataType = QMap; public: class TrackDataType : public DataType { public: using DataType::DataType; TrackDataType(bool aValid, QString aId, QString aParentId, QString aTitle, QString aArtist, QString aAlbumName, QString aAlbumArtist, int aTrackNumber, int aDiscNumber, QTime aDuration, QUrl aResourceURI, const QDateTime &fileModificationTime, QUrl aAlbumCover, int rating, bool aIsSingleDiscAlbum, QString aGenre, QString aComposer, QString aLyricist, bool aHasEmbeddedCover) : DataType({{key_type::TitleRole, aTitle}, {key_type::AlbumRole, aAlbumName}, {key_type::ArtistRole, aArtist}, {key_type::AlbumArtistRole, aAlbumArtist}, {key_type::IdRole, aId}, {key_type::ParentIdRole, aParentId}, {key_type::TrackNumberRole, aTrackNumber}, {key_type::DiscNumberRole, aDiscNumber}, {key_type::DurationRole, aDuration}, {key_type::ResourceRole, aResourceURI}, {key_type::FileModificationTime, fileModificationTime}, {key_type::ImageUrlRole, aAlbumCover}, {key_type::RatingRole, rating}, {key_type::IsSingleDiscAlbumRole, aIsSingleDiscAlbum}, {key_type::GenreRole, aGenre}, {key_type::ComposerRole, aComposer}, {key_type::LyricistRole, aLyricist}, {key_type::HasEmbeddedCover, aHasEmbeddedCover},}) { Q_UNUSED(aValid) } bool isValid() const { return !isEmpty() && duration().isValid(); } qulonglong databaseId() const { return operator[](key_type::DatabaseIdRole).toULongLong(); } QString title() const { return operator[](key_type::TitleRole).toString(); } QString artist() const { return operator[](key_type::ArtistRole).toString(); } qulonglong albumId() const { return operator[](key_type::AlbumIdRole).toULongLong(); } bool hasAlbum() const { return find(key_type::AlbumRole) != end(); } QString album() const { return operator[](key_type::AlbumRole).toString(); } QString albumArtist() const { return operator[](key_type::AlbumArtistRole).toString(); } bool hasAlbumArtist() const { return find(key_type::AlbumArtistRole) != end(); } bool hasTrackNumber() const { return find(key_type::TrackNumberRole) != end(); } int trackNumber() const { return operator[](key_type::TrackNumberRole).toInt(); } bool hasDiscNumber() const { return find(key_type::DiscNumberRole) != end(); } int discNumber() const { return operator[](key_type::DiscNumberRole).toInt(); } QTime duration() const { return operator[](key_type::DurationRole).toTime(); } QUrl resourceURI() const { return operator[](key_type::ResourceRole).toUrl(); } QUrl albumCover() const { return operator[](key_type::ImageUrlRole).toUrl(); } bool isSingleDiscAlbum() const { return operator[](key_type::IsSingleDiscAlbumRole).toBool(); } int rating() const { return operator[](key_type::RatingRole).toInt(); } QString genre() const { return operator[](key_type::GenreRole).toString(); } QString composer() const { return operator[](key_type::ComposerRole).toString(); } QString lyricist() const { return operator[](key_type::LyricistRole).toString(); } QString lyrics() const { return operator[](key_type::LyricsRole).toString(); } QString comment() const { return operator[](key_type::CommentRole).toString(); } int year() const { return operator[](key_type::YearRole).toInt(); } int channels() const { return operator[](key_type::ChannelsRole).toInt(); } bool hasChannels() const { return find(key_type::ChannelsRole) != end(); } int bitRate() const { return operator[](key_type::BitRateRole).toInt(); } bool hasBitRate() const { return find(key_type::BitRateRole) != end(); } int sampleRate() const { return operator[](key_type::SampleRateRole).toInt(); } bool hasSampleRate() const { return find(key_type::SampleRateRole) != end(); } bool hasEmbeddedCover() const { return operator[](key_type::HasEmbeddedCover).toBool(); } QDateTime fileModificationTime() const { return operator[](key_type::FileModificationTime).toDateTime(); } }; using ListTrackDataType = QList; using ListRadioDataType = QList; class AlbumDataType : public DataType { public: using DataType::DataType; qulonglong databaseId() const { return operator[](key_type::DatabaseIdRole).toULongLong(); } QString title() const { return operator[](key_type::TitleRole).toString(); } QString artist() const { return operator[](key_type::ArtistRole).toString(); } bool isValidArtist() const { const auto &artistData = operator[](key_type::ArtistRole); return artistData.isValid() && !artistData.toString().isEmpty(); } QStringList genres() const { return operator[](key_type::GenreRole).toStringList(); } QUrl albumArtURI() const { return operator[](key_type::ImageUrlRole).toUrl(); } bool isSingleDiscAlbum() const { return operator[](key_type::IsSingleDiscAlbumRole).toBool(); } bool isValid() const { return !isEmpty(); } }; using ListAlbumDataType = QList; class ArtistDataType : public DataType { public: using DataType::DataType; qulonglong databaseId() const { return operator[](key_type::DatabaseIdRole).toULongLong(); } }; using ListArtistDataType = QList; class GenreDataType : public DataType { public: using DataType::DataType; qulonglong databaseId() const { return operator[](key_type::DatabaseIdRole).toULongLong(); } QString title() const { return operator[](key_type::TitleRole).toString(); } }; using ListGenreDataType = QList; }; Q_DECLARE_METATYPE(DataTypes::TrackDataType) Q_DECLARE_METATYPE(DataTypes::AlbumDataType) Q_DECLARE_METATYPE(DataTypes::ArtistDataType) Q_DECLARE_METATYPE(DataTypes::GenreDataType) Q_DECLARE_METATYPE(DataTypes::ListTrackDataType) Q_DECLARE_METATYPE(DataTypes::ListAlbumDataType) Q_DECLARE_METATYPE(DataTypes::ListArtistDataType) Q_DECLARE_METATYPE(DataTypes::ListGenreDataType) -#endif // DATABASEINTERFACE_H +#endif // DATATYPES_H diff --git a/src/elisaqmlplugin.cpp b/src/elisaqmlplugin.cpp index 60f8000b..4a20a803 100644 --- a/src/elisaqmlplugin.cpp +++ b/src/elisaqmlplugin.cpp @@ -1,178 +1,179 @@ /* * 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 "elisaqmlplugin.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnpcontrolconnectionmanager.h" #include "upnp/upnpcontrolmediaserver.h" #include "upnp/upnpcontrolcontentdirectory.h" #include "upnp/upnpcontentdirectorymodel.h" #include "upnpdevicedescription.h" #include "upnp/didlparser.h" #include "upnp/upnpdiscoverallmusic.h" #include "upnpssdpengine.h" #include "upnpabstractservice.h" #include "upnpcontrolabstractdevice.h" #include "upnpcontrolabstractservice.h" #include "upnpbasictypes.h" #endif #include "elisautils.h" #include "elisaapplication.h" #include "progressindicator.h" #include "mediaplaylist.h" #include "managemediaplayercontrol.h" #include "manageheaderbar.h" #include "manageaudioplayer.h" #include "musiclistenersmanager.h" #include "trackslistener.h" #include "viewmanager.h" #include "databaseinterface.h" #include "datatypes.h" #include "models/datamodel.h" #include "models/trackmetadatamodel.h" #include "models/trackcontextmetadatamodel.h" #include "models/viewsmodel.h" #include "models/gridviewproxymodel.h" #include "models/alltracksproxymodel.h" #include "models/singlealbumproxymodel.h" #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND #include "embeddedcoverageimageprovider.h" #endif #if defined KF5KIO_FOUND && KF5KIO_FOUND #include "models/filebrowsermodel.h" #include "models/filebrowserproxymodel.h" #endif #include "audiowrapper.h" #if defined Qt5DBus_FOUND && Qt5DBus_FOUND #include "mpris2/mpris2.h" #include "mpris2/mediaplayer2player.h" #endif #include #include #include #include #include #include void ElisaQmlTestPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND engine->addImageProvider(QStringLiteral("cover"), new EmbeddedCoverageImageProvider); #endif } void ElisaQmlTestPlugin::registerTypes(const char *uri) { #if defined UPNPQT_FOUND && UPNPQT_FOUND qmlRegisterType(uri, 1, 0, "UpnpSsdpEngine"); qmlRegisterType(uri, 1, 0, "UpnpDiscoverAllMusic"); qmlRegisterType(uri, 1, 0, "UpnpAbstractDevice"); qmlRegisterType(uri, 1, 0, "UpnpAbstractService"); qmlRegisterType(uri, 1, 0, "UpnpControlAbstractDevice"); qmlRegisterType(uri, 1, 0, "UpnpControlAbstractService"); qmlRegisterType(uri, 1, 0, "UpnpControlConnectionManager"); qmlRegisterType(uri, 1, 0, "UpnpControlMediaServer"); qmlRegisterType(uri, 1, 0, "UpnpContentDirectoryModel"); qmlRegisterType(uri, 1, 0, "DidlParser"); qmlRegisterType(uri, 1, 0, "UpnpControlContentDirectory"); qmlRegisterType(uri, 1, 0, "UpnpDeviceDescription"); qRegisterMetaType(); qRegisterMetaType >(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); #endif qmlRegisterType(uri, 1, 0, "MediaPlayList"); qmlRegisterType(uri, 1, 0, "ManageMediaPlayerControl"); qmlRegisterType(uri, 1, 0, "ManageHeaderBar"); qmlRegisterType(uri, 1, 0, "ManageAudioPlayer"); qmlRegisterType(uri, 1, 0, "ProgressIndicator"); qmlRegisterType(uri, 1, 0, "MusicListenersManager"); qmlRegisterType(uri, 1, 0, "ViewManager"); qmlRegisterType(uri, 1, 0, "DataModel"); qmlRegisterType(uri, 1, 0, "TrackMetadataModel"); qmlRegisterType(uri, 1, 0, "TrackContextMetaDataModel"); qmlRegisterType(uri, 1, 0, "ViewsModel"); qmlRegisterType(uri, 1, 0, "GridViewProxyModel"); qmlRegisterType(uri, 1, 0, "AllTracksProxyModel"); qmlRegisterType(uri, 1, 0, "SingleAlbumProxyModel"); #if defined KF5KIO_FOUND && KF5KIO_FOUND qmlRegisterType(uri, 1, 0, "FileBrowserModel"); qmlRegisterType(uri, 1, 0, "FileBrowserProxyModel"); #endif qmlRegisterType(uri, 1, 0, "AudioWrapper"); qmlRegisterUncreatableType(uri, 1, 0, "DatabaseInterface", QStringLiteral("Only created in c++")); #if defined Qt5DBus_FOUND && Qt5DBus_FOUND qmlRegisterType(uri, 1, 0, "Mpris2"); qRegisterMetaType(); #endif qRegisterMetaType(); qRegisterMetaType>("QHash"); qRegisterMetaType>("QHash"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType("DataTypes::ListTrackDataType"); qRegisterMetaType("DataTypes::ListRadioDataType"); qRegisterMetaType("DataTypes::ListAlbumDataType"); qRegisterMetaType("DataTypes::ListArtistDataType"); qRegisterMetaType("DataTypes::ListGenreDataType"); qRegisterMetaType("ModelDataLoader::ListTrackDataType"); qRegisterMetaType("ModelDataLoader::ListRadioDataType"); qRegisterMetaType("ModelDataLoader::ListAlbumDataType"); qRegisterMetaType("ModelDataLoader::ListArtistDataType"); qRegisterMetaType("ModelDataLoader::ListGenreDataType"); qRegisterMetaType("ModelDataLoader::AlbumDataType"); qRegisterMetaType("TracksListener::ListTrackDataType"); qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType>("QMap"); qRegisterMetaType("ElisaUtils::PlayListEnqueueMode"); qRegisterMetaType("ElisaUtils::PlayListEnqueueTriggerPlay"); qRegisterMetaType("ElisaUtils::PlayListEntryType"); qRegisterMetaType("ElisaUtils::EntryData"); qRegisterMetaType("ElisaUtils::EntryDataList"); qRegisterMetaType("ElisaUtils::FilterType"); qRegisterMetaType("DataTypes::TrackDataType"); qRegisterMetaType("DataTypes::AlbumDataType"); qRegisterMetaType("DataTypes::ArtistDataType"); qRegisterMetaType("DataTypes::GenreDataType"); + qRegisterMetaType("DataTypes::ColumnsRoles"); qRegisterMetaType("ModelDataLoader::TrackDataType"); qRegisterMetaType("TracksListener::TrackDataType"); qRegisterMetaType("ViewManager::ViewsType"); qRegisterMetaType("ViewManager::SortOrder"); qmlRegisterUncreatableType(uri, 1, 0, "ElisaApplication", QStringLiteral("only one and done in c++")); qmlRegisterUncreatableMetaObject(ElisaUtils::staticMetaObject, uri, 1, 0, "ElisaUtils", QStringLiteral("Namespace ElisaUtils")); } diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index 01c19f52..763d9ba5 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,491 +1,492 @@ /* * 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; } void MusicListenersManager::databaseReady() { auto initialRootPath = Elisa::ElisaConfiguration::rootPath(); if (initialRootPath.isEmpty()) { auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); for (const auto &musicPath : qAsConst(systemMusicPaths)) { initialRootPath.push_back(musicPath); } Elisa::ElisaConfiguration::setRootPath(initialRootPath); Elisa::ElisaConfiguration::self()->save(); } 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(); const auto &allRootPaths = currentConfiguration->rootPath(); d->mFileListener.setAllRootPaths(allRootPaths); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setAllRootPaths(allRootPaths); #endif if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) { testBalooIndexerAvailability(); } if (d->mBalooIndexerActive) { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); #endif } else if (d->mFileSystemIndexerActive) { 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(); } void MusicListenersManager::balooAvailabilityChanged() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooDetector.balooAvailability()) { #else if (true) { #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"