diff --git a/src/filescanner.cpp b/src/filescanner.cpp index a6a79a03..c3c1a962 100644 --- a/src/filescanner.cpp +++ b/src/filescanner.cpp @@ -1,269 +1,268 @@ /* * 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 -#if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND - -static 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 class FileScannerPrivate { public: #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND KFileMetaData::ExtractorCollection mAllExtractors; KFileMetaData::PropertyMap mAllProperties; KFileMetaData::EmbeddedImageData mImageScanner; #endif QMimeDatabase mMimeDb; -}; -static 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") +#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](const 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](const 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 = propertyTranslation.find(key); + 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 != propertyTranslation.end()) { + } 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(constSearchStrings); + 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/main.cpp b/src/main.cpp index d687c0b9..4fbfe41b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,185 +1,185 @@ /* * Copyright 2015-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 "config-upnp-qt.h" #include "elisa-version.h" #include "elisaapplication.h" #include "elisa_settings.h" //#define QT_QML_DEBUG #if defined KF5Declarative_FOUND && KF5Declarative_FOUND #include #include #endif #include #include #include #if defined KF5Crash_FOUND && KF5Crash_FOUND #include #endif #if defined KF5DBusAddons_FOUND && KF5DBusAddons_FOUND #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND #include #endif #include #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND #include #include #endif #if defined Q_OS_ANDROID int __attribute__((visibility("default"))) main(int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif { #if defined Q_OS_ANDROID if(argc > 1 && strcmp(argv[1], "-service") == 0){ QAndroidService app(argc, argv); qInfo() << "Service starting..."; // My service stuff return app.exec(); } qInfo() << "Application starting..."; #endif QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); qputenv("QT_GSTREAMER_USE_PLAYBIN_VOLUME", "true"); QApplication app(argc, argv); #if defined KF5Declarative_FOUND && KF5Declarative_FOUND KQuickAddons::QtQuickSettings::init(); #endif KLocalizedString::setApplicationDomain("elisa"); #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND qInfo() << QCoreApplication::arguments(); QAndroidJniObject::callStaticMethod("org/kde/elisa/ElisaService", "startMyService", "(Landroid/content/Context;)V", QtAndroid::androidContext().object()); #endif #if defined KF5Crash_FOUND && KF5Crash_FOUND KCrash::initialize(); #endif QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("elisa"))); KAboutData aboutData( QStringLiteral("elisa"), i18n("Elisa"), QStringLiteral(ELISA_VERSION_STRING), i18n("A Simple Music Player written with KDE Frameworks"), KAboutLicense::LGPL_V3, i18n("(c) 2015-2019, Elisa contributors")); aboutData.addAuthor(QStringLiteral("Matthieu Gallien"),i18n("Creator"), QStringLiteral("mgallien@mgallien.fr")); aboutData.addAuthor(QStringLiteral("Alexander Stippich"), i18n("Author"), QStringLiteral("a.stippich@gmx.net")); aboutData.addCredit(QStringLiteral("Andrew Lake"), i18n("Concept and design work"), QStringLiteral("jamboarder@gmail.com")); aboutData.addCredit(QStringLiteral("Luigi Toscano"), i18n("Localization support"), QStringLiteral("luigi.toscano@tiscali.it")); aboutData.addCredit(QStringLiteral("Safa Alfulaij"), i18n("Right to left support in interface"), QStringLiteral("safa1996alfulaij@gmail.com")); aboutData.addCredit(QStringLiteral("Diego Gangl"), i18n("Various improvements to the interface"), QStringLiteral("diego@sinestesia.co")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); QQuickStyle::setFallbackStyle(QStringLiteral("Fusion")); QQmlApplicationEngine engine; engine.addImportPath(QStringLiteral("qrc:/imports")); QQmlFileSelector selector(&engine); #if defined KF5Declarative_FOUND && KF5Declarative_FOUND KDeclarative::KDeclarative decl; decl.setDeclarativeEngine(&engine); decl.setupEngine(&engine); decl.setupContext(); #endif engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); #if defined KF5DBusAddons_FOUND && KF5DBusAddons_FOUND KDBusService elisaService(KDBusService::Unique); #endif std::unique_ptr myApp = std::make_unique(); #if defined KF5DBusAddons_FOUND && KF5DBusAddons_FOUND QObject::connect(&elisaService, &KDBusService::activateActionRequested, myApp.get(), &ElisaApplication::activateActionRequested); QObject::connect(&elisaService, &KDBusService::activateRequested, myApp.get(), &ElisaApplication::activateRequested); QObject::connect(&elisaService, &KDBusService::openRequested, myApp.get(), &ElisaApplication::openRequested); #endif auto arguments = ElisaUtils::EntryDataList{}; auto realArgumentsList = parser.positionalArguments(); - for (auto oneArgument : realArgumentsList) { + for (const auto &oneArgument : realArgumentsList) { arguments.push_back(ElisaUtils::EntryData{{}, {}, QUrl(oneArgument)}); } myApp->setArguments(arguments); engine.rootContext()->setContextProperty(QStringLiteral("elisa"), myApp.release()); engine.load(QUrl(QStringLiteral("qrc:/qml/ElisaMainWindow.qml"))); return app.exec(); } diff --git a/src/manageaudioplayer.cpp b/src/manageaudioplayer.cpp index 8118021f..fa9c1273 100644 --- a/src/manageaudioplayer.cpp +++ b/src/manageaudioplayer.cpp @@ -1,598 +1,598 @@ /* * Copyright 2016 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 "manageaudioplayer.h" #include "mediaplaylist.h" #include #include ManageAudioPlayer::ManageAudioPlayer(QObject *parent) : QObject(parent) { } QPersistentModelIndex ManageAudioPlayer::currentTrack() const { return mCurrentTrack; } QAbstractItemModel *ManageAudioPlayer::playListModel() const { return mPlayListModel; } int ManageAudioPlayer::urlRole() const { return mUrlRole; } int ManageAudioPlayer::isPlayingRole() const { return mIsPlayingRole; } QUrl ManageAudioPlayer::playerSource() const { if (!mCurrentTrack.isValid()) { return QUrl(); } return mCurrentTrack.data(mUrlRole).toUrl(); } QMediaPlayer::MediaStatus ManageAudioPlayer::playerStatus() const { return mPlayerStatus; } QMediaPlayer::State ManageAudioPlayer::playerPlaybackState() const { return mPlayerPlaybackState; } QMediaPlayer::Error ManageAudioPlayer::playerError() const { return mPlayerError; } qint64 ManageAudioPlayer::audioDuration() const { return mAudioDuration; } bool ManageAudioPlayer::playerIsSeekable() const { return mPlayerIsSeekable; } qint64 ManageAudioPlayer::playerPosition() const { return mPlayerPosition; } qint64 ManageAudioPlayer::playControlPosition() const { return mPlayerPosition; } QVariantMap ManageAudioPlayer::persistentState() const { auto persistentStateValue = QVariantMap(); persistentStateValue[QStringLiteral("isPlaying")] = mPlayingState; persistentStateValue[QStringLiteral("playerPosition")] = mPlayerPosition; if (mCurrentTrack.isValid()) { persistentStateValue[QStringLiteral("audioPlayerCurrentTitle")] = mCurrentTrack.data(mTitleRole); persistentStateValue[QStringLiteral("audioPlayerCurrentArtistName")] = mCurrentTrack.data(mArtistNameRole); persistentStateValue[QStringLiteral("audioPlayerCurrentAlbumName")] = mCurrentTrack.data(mAlbumNameRole); } else { persistentStateValue[QStringLiteral("audioPlayerCurrentTitle")] = {}; persistentStateValue[QStringLiteral("audioPlayerCurrentArtistName")] = {}; persistentStateValue[QStringLiteral("audioPlayerCurrentAlbumName")] = {}; } return persistentStateValue; } int ManageAudioPlayer::playListPosition() const { if (mCurrentTrack.isValid()) { return mCurrentTrack.row(); } return 0; } int ManageAudioPlayer::titleRole() const { return mTitleRole; } int ManageAudioPlayer::artistNameRole() const { return mArtistNameRole; } int ManageAudioPlayer::albumNameRole() const { return mAlbumNameRole; } void ManageAudioPlayer::setCurrentTrack(const QPersistentModelIndex ¤tTrack) { mOldCurrentTrack = mCurrentTrack; mCurrentTrack = currentTrack; if (mCurrentTrack.isValid()) { restorePreviousState(); } mPlayerError = QMediaPlayer::NoError; if (mOldCurrentTrack != mCurrentTrack || mPlayingState) { Q_EMIT currentTrackChanged(); } switch (mPlayerPlaybackState) { case QMediaPlayer::StoppedState: Q_EMIT playerSourceChanged(mCurrentTrack.data(mUrlRole).toUrl()); break; case QMediaPlayer::PlayingState: case QMediaPlayer::PausedState: triggerStop(); if (mPlayingState && !mCurrentTrack.isValid()) { mPlayingState = false; } mSkippingCurrentTrack = true; break; } } void ManageAudioPlayer::saveForUndoClearPlaylist(){ mUndoPlayingState = mPlayingState; mUndoPlayerPosition = mPlayerPosition; Q_EMIT saveUndoPositionInAudioWrapper(mUndoPlayerPosition); } void ManageAudioPlayer::restoreForUndoClearPlaylist(){ mPlayerPosition = mUndoPlayerPosition; Q_EMIT seek(mPlayerPosition); mPlayingState = mUndoPlayingState; Q_EMIT restoreUndoPositionInAudioWrapper(); } void ManageAudioPlayer::setPlayListModel(QAbstractItemModel *aPlayListModel) { if (mPlayListModel == aPlayListModel) { return; } if (mPlayListModel) { disconnect(mPlayListModel, &QAbstractItemModel::dataChanged, this, &ManageAudioPlayer::tracksDataChanged); } mPlayListModel = aPlayListModel; if (mPlayListModel) { connect(mPlayListModel, &QAbstractItemModel::dataChanged, this, &ManageAudioPlayer::tracksDataChanged); } Q_EMIT playListModelChanged(); } void ManageAudioPlayer::setUrlRole(int value) { mUrlRole = value; Q_EMIT urlRoleChanged(); notifyPlayerSourceProperty(); restorePreviousState(); } void ManageAudioPlayer::setIsPlayingRole(int value) { if (mIsPlayingRole == value) { return; } mIsPlayingRole = value; Q_EMIT isPlayingRoleChanged(); } void ManageAudioPlayer::setPlayerStatus(QMediaPlayer::MediaStatus playerStatus) { if (mPlayerStatus == playerStatus) { return; } mPlayerStatus = playerStatus; Q_EMIT playerStatusChanged(); switch (mPlayerStatus) { case QMediaPlayer::NoMedia: break; case QMediaPlayer::LoadingMedia: break; case QMediaPlayer::LoadedMedia: if (mPlayingState) { triggerPlay(); } break; case QMediaPlayer::BufferingMedia: break; case QMediaPlayer::StalledMedia: break; case QMediaPlayer::BufferedMedia: break; case QMediaPlayer::EndOfMedia: break; case QMediaPlayer::InvalidMedia: triggerSkipNextTrack(); break; case QMediaPlayer::UnknownMediaStatus: break; } } void ManageAudioPlayer::setPlayerPlaybackState(QMediaPlayer::State playerPlaybackState) { if (mPlayerPlaybackState == playerPlaybackState) { return; } mPlayerPlaybackState = playerPlaybackState; Q_EMIT playerPlaybackStateChanged(); if (!mSkippingCurrentTrack) { switch(mPlayerPlaybackState) { case QMediaPlayer::StoppedState: if (mPlayerStatus == QMediaPlayer::EndOfMedia || mPlayerStatus == QMediaPlayer::InvalidMedia) { triggerSkipNextTrack(); } if (mPlayListModel && mCurrentTrack.isValid()) { mPlayListModel->setData(mCurrentTrack, MediaPlayList::NotPlaying, mIsPlayingRole); } break; case QMediaPlayer::PlayingState: if (mPlayListModel && mCurrentTrack.isValid()) { mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPlaying, mIsPlayingRole); Q_EMIT startedPlayingTrack(mCurrentTrack.data(mUrlRole).toUrl(), QDateTime::currentDateTime()); } break; case QMediaPlayer::PausedState: if (mPlayListModel && mCurrentTrack.isValid()) { mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPaused, mIsPlayingRole); } break; } } else { switch(mPlayerPlaybackState) { case QMediaPlayer::StoppedState: notifyPlayerSourceProperty(); mSkippingCurrentTrack = false; if (mPlayListModel && mOldCurrentTrack.isValid()) { mPlayListModel->setData(mOldCurrentTrack, MediaPlayList::NotPlaying, mIsPlayingRole); } break; case QMediaPlayer::PlayingState: if (mPlayListModel && mCurrentTrack.isValid()) { mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPlaying, mIsPlayingRole); Q_EMIT startedPlayingTrack(mCurrentTrack.data(mUrlRole).toUrl(), QDateTime::currentDateTime()); } break; case QMediaPlayer::PausedState: if (mPlayListModel && mCurrentTrack.isValid()) { mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPaused, mIsPlayingRole); } break; } } } void ManageAudioPlayer::setPlayerError(QMediaPlayer::Error playerError) { if (mPlayerError == playerError) { return; } mPlayerError = playerError; Q_EMIT playerErrorChanged(); if (mPlayerError != QMediaPlayer::NoError) { auto currentSource = playerSource(); Q_EMIT sourceInError(currentSource, mPlayerError); if (currentSource.isLocalFile()) { Q_EMIT displayTrackError(currentSource.toLocalFile()); } else { Q_EMIT displayTrackError(currentSource.toString()); } } } void ManageAudioPlayer::ensurePause() { if (mPlayingState) { mPlayingState = false; triggerPause(); } } void ManageAudioPlayer::ensurePlay() { if (!mPlayingState) { mPlayingState = true; triggerPlay(); } } void ManageAudioPlayer::stop() { mPlayingState = false; triggerStop(); } void ManageAudioPlayer::playPause() { mPlayingState = !mPlayingState; switch (mPlayerStatus) { case QMediaPlayer::LoadedMedia: case QMediaPlayer::BufferingMedia: case QMediaPlayer::BufferedMedia: case QMediaPlayer::LoadingMedia: if (mPlayingState) { triggerPlay(); } else { triggerPause(); } break; case QMediaPlayer::EndOfMedia: if (mPlayerPlaybackState == QMediaPlayer::PlayingState && !mPlayingState) { triggerPause(); } else if (mPlayerPlaybackState == QMediaPlayer::PausedState && mPlayingState) { triggerPlay(); } break; case QMediaPlayer::NoMedia: case QMediaPlayer::StalledMedia: case QMediaPlayer::InvalidMedia: case QMediaPlayer::UnknownMediaStatus: break; } } void ManageAudioPlayer::setAudioDuration(qint64 audioDuration) { if (mAudioDuration == audioDuration) { return; } mAudioDuration = audioDuration; Q_EMIT audioDurationChanged(); } void ManageAudioPlayer::setPlayerIsSeekable(bool playerIsSeekable) { if (mPlayerIsSeekable == playerIsSeekable) { return; } mPlayerIsSeekable = playerIsSeekable; Q_EMIT playerIsSeekableChanged(); } void ManageAudioPlayer::setPlayerPosition(qint64 playerPosition) { if (mPlayerPosition == playerPosition) { return; } mPlayerPosition = playerPosition; Q_EMIT playerPositionChanged(); - QTimer::singleShot(0, [this]() {Q_EMIT playControlPositionChanged();}); + QTimer::singleShot(0, this, [this]() {Q_EMIT playControlPositionChanged();}); } void ManageAudioPlayer::setCurrentPlayingForRadios(QString title, QString nowPlaying) { if (mPlayListModel && mCurrentTrack.isValid()) { Q_EMIT currentPlayingForRadiosChanged(title, MediaPlayList::TitleRole); Q_EMIT currentPlayingForRadiosChanged(nowPlaying, MediaPlayList::ArtistRole); } } void ManageAudioPlayer::setPlayControlPosition(int playerPosition) { Q_EMIT seek(playerPosition); } void ManageAudioPlayer::setPersistentState(const QVariantMap &persistentStateValue) { if (mPersistentState == persistentStateValue) { return; } mPersistentState = persistentStateValue; Q_EMIT persistentStateChanged(); if (mCurrentTrack.isValid()) { restorePreviousState(); } } void ManageAudioPlayer::playerSeek(int position) { Q_EMIT seek(position); } void ManageAudioPlayer::playListFinished() { mPlayingState = false; } void ManageAudioPlayer::tracksDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { if (!mCurrentTrack.isValid()) { return; } if (mCurrentTrack.row() > bottomRight.row() || mCurrentTrack.row() < topLeft.row()) { return; } if (mCurrentTrack.column() > bottomRight.column() || mCurrentTrack.column() < topLeft.column()) { return; } if (roles.isEmpty()) { notifyPlayerSourceProperty(); restorePreviousState(); } else { for(auto oneRole : roles) { if (oneRole == mUrlRole) { notifyPlayerSourceProperty(); restorePreviousState(); } } } } void ManageAudioPlayer::setTitleRole(int titleRole) { if (mTitleRole == titleRole) { return; } mTitleRole = titleRole; Q_EMIT titleRoleChanged(); if (mCurrentTrack.isValid()) { restorePreviousState(); } } void ManageAudioPlayer::setArtistNameRole(int artistNameRole) { if (mArtistNameRole == artistNameRole) { return; } mArtistNameRole = artistNameRole; Q_EMIT artistNameRoleChanged(); if (mCurrentTrack.isValid()) { restorePreviousState(); } } void ManageAudioPlayer::setAlbumNameRole(int albumNameRole) { if (mAlbumNameRole == albumNameRole) { return; } mAlbumNameRole = albumNameRole; Q_EMIT albumNameRoleChanged(); if (mCurrentTrack.isValid()) { restorePreviousState(); } } void ManageAudioPlayer::notifyPlayerSourceProperty() { auto newUrlValue = mCurrentTrack.data(mUrlRole); if (mSkippingCurrentTrack || mOldPlayerSource != newUrlValue) { Q_EMIT playerSourceChanged(mCurrentTrack.data(mUrlRole).toUrl()); mOldPlayerSource = newUrlValue; } } void ManageAudioPlayer::triggerPlay() { - QTimer::singleShot(0, [this]() {Q_EMIT playerPlay();}); + QTimer::singleShot(0, this, [this]() {Q_EMIT playerPlay();}); } void ManageAudioPlayer::triggerPause() { - QTimer::singleShot(0, [this]() {Q_EMIT playerPause();}); + QTimer::singleShot(0, this, [this]() {Q_EMIT playerPause();}); } void ManageAudioPlayer::triggerStop() { - QTimer::singleShot(0, [this]() {Q_EMIT playerStop();}); + QTimer::singleShot(0, this, [this]() {Q_EMIT playerStop();}); } void ManageAudioPlayer::triggerSkipNextTrack() { - QTimer::singleShot(0, [this]() {Q_EMIT skipNextTrack();}); + QTimer::singleShot(0, this, [this]() {Q_EMIT skipNextTrack();}); } void ManageAudioPlayer::restorePreviousState() { if (mPersistentState.isEmpty()) { return; } auto itTitle = mPersistentState.find(QStringLiteral("audioPlayerCurrentTitle")); auto itArtistName = mPersistentState.find(QStringLiteral("audioPlayerCurrentArtistName")); auto itAlbumName = mPersistentState.find(QStringLiteral("audioPlayerCurrentAlbumName")); if (itTitle == mPersistentState.end() || itArtistName == mPersistentState.end() || itAlbumName == mPersistentState.end()) { return; } if (*itTitle != mCurrentTrack.data(mTitleRole) || (itArtistName->isValid() && *itArtistName != mCurrentTrack.data(mArtistNameRole)) || (itAlbumName->isValid() && *itAlbumName != mCurrentTrack.data(mAlbumNameRole))) { if (mCurrentTrack.isValid() && mCurrentTrack.data(mTitleRole).isValid() && mCurrentTrack.data(mArtistNameRole).isValid() && mCurrentTrack.data(mAlbumNameRole).isValid()) { mPersistentState.clear(); } return; } if (!mCurrentTrack.data(mUrlRole).toUrl().isValid()) { return; } auto isPlaying = mPersistentState.find(QStringLiteral("isPlaying")); if (isPlaying != mPersistentState.end() && mPlayingState != isPlaying->toBool()) { mPlayingState = isPlaying->toBool(); } auto playerPosition = mPersistentState.find(QStringLiteral("playerPosition")); if (playerPosition != mPersistentState.end()) { mPlayerPosition = playerPosition->toLongLong(); Q_EMIT seek(mPlayerPosition); } mPersistentState.clear(); } #include "moc_manageaudioplayer.cpp" diff --git a/src/managemediaplayercontrol.h b/src/managemediaplayercontrol.h index 43667ec7..663b01f1 100644 --- a/src/managemediaplayercontrol.h +++ b/src/managemediaplayercontrol.h @@ -1,128 +1,122 @@ /* * Copyright 2016 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 . */ #ifndef MANAGEMEDIAPLAYERCONTROL_H #define MANAGEMEDIAPLAYERCONTROL_H #include "elisaLib_export.h" #include #include class QAbstractItemModel; class ELISALIB_EXPORT ManageMediaPlayerControl : public QObject { Q_OBJECT Q_PROPERTY(bool playControlEnabled READ playControlEnabled NOTIFY playControlEnabledChanged) Q_PROPERTY(bool skipBackwardControlEnabled READ skipBackwardControlEnabled NOTIFY skipBackwardControlEnabledChanged) Q_PROPERTY(bool skipForwardControlEnabled READ skipForwardControlEnabled NOTIFY skipForwardControlEnabledChanged) Q_PROPERTY(bool musicPlaying READ musicPlaying NOTIFY musicPlayingChanged) Q_PROPERTY(QPersistentModelIndex previousTrack READ previousTrack WRITE setPreviousTrack NOTIFY previousTrackChanged) Q_PROPERTY(QPersistentModelIndex currentTrack READ currentTrack WRITE setCurrentTrack NOTIFY currentTrackChanged) Q_PROPERTY(QPersistentModelIndex nextTrack READ nextTrack WRITE setNextTrack NOTIFY nextTrackChanged) public: explicit ManageMediaPlayerControl(QObject *parent = nullptr); bool playControlEnabled() const; bool skipBackwardControlEnabled() const; bool skipForwardControlEnabled() const; bool musicPlaying() const; QPersistentModelIndex previousTrack() const; QPersistentModelIndex currentTrack() const; QPersistentModelIndex nextTrack() const; Q_SIGNALS: void playControlEnabledChanged(); void skipBackwardControlEnabledChanged(); void skipForwardControlEnabledChanged(); void musicPlayingChanged(); void previousTrackChanged(); void currentTrackChanged(); void nextTrackChanged(); public Q_SLOTS: void playerPausedOrStopped(); void playerPlaying(); void setPreviousTrack(const QPersistentModelIndex &previousTrack); void setCurrentTrack(const QPersistentModelIndex ¤tTrack); void setNextTrack(const QPersistentModelIndex &nextTrack); private: QPersistentModelIndex mPreviousTrack; QPersistentModelIndex mCurrentTrack; QPersistentModelIndex mNextTrack; - bool mCurrentTrackWillBeRemoved = false; - - bool mSkipBackwardControlWasEnabled = false; - - bool mSkipForwardControlWasEnabled = false; - bool mIsInPlayingState = false; }; #endif // MANAGEMEDIAPLAYERCONTROL_H diff --git a/src/modeldataloader.h b/src/modeldataloader.h index f047cb20..c4370094 100644 --- a/src/modeldataloader.h +++ b/src/modeldataloader.h @@ -1,142 +1,142 @@ /* * 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 . */ #ifndef MODELDATALOADER_H #define MODELDATALOADER_H #include "elisaLib_export.h" #include "elisautils.h" #include "databaseinterface.h" #include "datatypes.h" #include "models/datamodel.h" #include #include class ModelDataLoaderPrivate; class ELISALIB_EXPORT ModelDataLoader : public QObject { Q_OBJECT public: using ListAlbumDataType = DataTypes::ListAlbumDataType; using ListArtistDataType = DataTypes::ListArtistDataType; using ListGenreDataType = DataTypes::ListGenreDataType; using ListTrackDataType = DataTypes::ListTrackDataType; using ListRadioDataType = DataTypes::ListRadioDataType; using TrackDataType = DataTypes::TrackDataType; using AlbumDataType = DataTypes::AlbumDataType; using FilterType = ElisaUtils::FilterType; explicit ModelDataLoader(QObject *parent = nullptr); ~ModelDataLoader() override; void setDatabase(DatabaseInterface *database); Q_SIGNALS: void allAlbumsData(const ModelDataLoader::ListAlbumDataType &allData); void allArtistsData(const ModelDataLoader::ListArtistDataType &allData); void allGenresData(const ModelDataLoader::ListGenreDataType &allData); void allTracksData(const ModelDataLoader::ListTrackDataType &allData); void allRadiosData(const ModelDataLoader::ListRadioDataType &radiosData); void radioAdded(const ModelDataLoader::TrackDataType &radiosData); void radioModified(const ModelDataLoader::TrackDataType &radiosData); void allTrackData(const ModelDataLoader::TrackDataType &allData); void allRadioData(const ModelDataLoader::TrackDataType &allData); void tracksAdded(const ModelDataLoader::ListTrackDataType &newData); void trackModified(const ModelDataLoader::TrackDataType &modifiedTrack); void trackRemoved(qulonglong removedTrackId); void genresAdded(const ModelDataLoader::ListGenreDataType &newData); void artistsAdded(const ModelDataLoader::ListArtistDataType &newData); void artistRemoved(qulonglong removedDatabaseId); void albumsAdded(const ModelDataLoader::ListAlbumDataType &newData); void albumRemoved(qulonglong removedDatabaseId); void albumModified(const ModelDataLoader::AlbumDataType &modifiedAlbum); void saveRadioModified(const ModelDataLoader::TrackDataType &trackDataType); void removeRadio(qulonglong radioId); void radioRemoved(qulonglong radioId); void clearedDatabase(); public Q_SLOTS: void loadData(ElisaUtils::PlayListEntryType dataType); void loadDataByAlbumId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); void loadDataByGenre(ElisaUtils::PlayListEntryType dataType, const QString &genre); void loadDataByArtist(ElisaUtils::PlayListEntryType dataType, const QString &artist); void loadDataByGenreAndArtist(ElisaUtils::PlayListEntryType dataType, const QString &genre, const QString &artist); void loadDataByDatabaseIdAndUrl(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId, const QUrl &url); void loadDataByUrl(ElisaUtils::PlayListEntryType dataType, const QUrl &url); void loadRecentlyPlayedData(ElisaUtils::PlayListEntryType dataType); void loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType); private Q_SLOTS: - void databaseTracksAdded(const ListTrackDataType &newData); + void databaseTracksAdded(const ModelDataLoader::ListTrackDataType &newData); - void databaseArtistsAdded(const ListArtistDataType &newData); + void databaseArtistsAdded(const ModelDataLoader::ListArtistDataType &newData); - void databaseAlbumsAdded(const ListAlbumDataType &newData); + void databaseAlbumsAdded(const ModelDataLoader::ListAlbumDataType &newData); private: std::unique_ptr d; }; #endif // MODELDATALOADER_H diff --git a/src/models/trackmetadatamodel.cpp b/src/models/trackmetadatamodel.cpp index 5301ab9d..569cef1f 100644 --- a/src/models/trackmetadatamodel.cpp +++ b/src/models/trackmetadatamodel.cpp @@ -1,565 +1,563 @@ /* * 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 "trackmetadatamodel.h" #include "musiclistenersmanager.h" #include #include TrackMetadataModel::TrackMetadataModel(QObject *parent) : QAbstractListModel(parent) { connect(&mLyricsValueWatcher, &QFutureWatcher::finished, this, &TrackMetadataModel::lyricsValueIsReady); } TrackMetadataModel::~TrackMetadataModel() { if (mLyricsValueWatcher.isRunning() && !mLyricsValueWatcher.isFinished()) { mLyricsValueWatcher.waitForFinished(); } } int TrackMetadataModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return mTrackData.count(); } QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const { auto result = QVariant{}; const auto currentKey = mTrackKeys[index.row()]; switch (role) { case Qt::DisplayRole: switch (currentKey) { case DataTypes::TrackNumberRole: { auto trackNumber = mTrackData.trackNumber(); if (trackNumber > 0) { result = trackNumber; } break; } case DataTypes::DiscNumberRole: { auto discNumber = mTrackData.discNumber(); if (discNumber > 0) { result = discNumber; } break; } case DataTypes::ChannelsRole: { auto channels = mTrackData.channels(); if (channels > 0) { result = channels; } break; } case DataTypes::BitRateRole: { auto bitRate = mTrackData.bitRate(); if (bitRate > 0) { result = bitRate; } break; } case DataTypes::SampleRateRole: { auto sampleRate = mTrackData.sampleRate(); if (sampleRate > 0) { result = sampleRate; } break; } default: result = mTrackData[currentKey]; break; } break; case ItemNameRole: switch (currentKey) { case DataTypes::TitleRole: result = i18nc("Track title for track metadata view", "Title"); break; case DataTypes::DurationRole: result = i18nc("Duration label for track metadata view", "Duration"); break; case DataTypes::ArtistRole: result = i18nc("Track artist for track metadata view", "Artist"); break; case DataTypes::AlbumRole: result = i18nc("Album name for track metadata view", "Album"); break; case DataTypes::AlbumArtistRole: result = i18nc("Album artist for track metadata view", "Album Artist"); break; case DataTypes::TrackNumberRole: result = i18nc("Track number for track metadata view", "Track Number"); break; case DataTypes::DiscNumberRole: result = i18nc("Disc number for track metadata view", "Disc Number"); break; case DataTypes::RatingRole: result = i18nc("Rating label for information panel", "Rating"); break; case DataTypes::GenreRole: result = i18nc("Genre label for track metadata view", "Genre"); break; case DataTypes::LyricistRole: result = i18nc("Lyricist label for track metadata view", "Lyricist"); break; case DataTypes::ComposerRole: result = i18nc("Composer name for track metadata view", "Composer"); break; case DataTypes::CommentRole: result = i18nc("Comment label for track metadata view", "Comment"); break; case DataTypes::YearRole: result = i18nc("Year label for track metadata view", "Year"); break; case DataTypes::ChannelsRole: result = i18nc("Channels label for track metadata view", "Channels"); break; case DataTypes::BitRateRole: result = i18nc("Bit rate label for track metadata view", "Bit Rate"); break; case DataTypes::SampleRateRole: result = i18nc("Sample Rate label for track metadata view", "Sample Rate"); break; case DataTypes::LastPlayDate: result = i18nc("Last play date label for track metadata view", "Last played"); break; case DataTypes::PlayCounter: result = i18nc("Play counter label for track metadata view", "Play count"); break; case DataTypes::LyricsRole: result = i18nc("Lyrics label for track metadata view", "Lyrics"); break; case DataTypes::ResourceRole: result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address"); break; case DataTypes::SecondaryTextRole: case DataTypes::ImageUrlRole: case DataTypes::ShadowForImageRole: case DataTypes::ChildModelRole: case DataTypes::StringDurationRole: case DataTypes::IsValidAlbumArtistRole: case DataTypes::AllArtistsRole: case DataTypes::HighestTrackRating: case DataTypes::IdRole: case DataTypes::ParentIdRole: case DataTypes::DatabaseIdRole: case DataTypes::IsSingleDiscAlbumRole: case DataTypes::ContainerDataRole: case DataTypes::IsPartialDataRole: case DataTypes::AlbumIdRole: case DataTypes::HasEmbeddedCover: case DataTypes::FileModificationTime: case DataTypes::FirstPlayDate: case DataTypes::PlayFrequency: case DataTypes::ElementTypeRole: break; } break; case ItemTypeRole: switch (currentKey) { case DataTypes::TitleRole: result = TextEntry; break; case DataTypes::ResourceRole: result = TextEntry; break; case DataTypes::ArtistRole: result = TextEntry; break; case DataTypes::AlbumRole: result = TextEntry; break; case DataTypes::AlbumArtistRole: result = TextEntry; break; case DataTypes::TrackNumberRole: result = IntegerEntry; break; case DataTypes::DiscNumberRole: result = IntegerEntry; break; case DataTypes::RatingRole: result = RatingEntry; break; case DataTypes::GenreRole: result = TextEntry; break; case DataTypes::LyricistRole: result = TextEntry; break; case DataTypes::ComposerRole: result = TextEntry; break; case DataTypes::CommentRole: result = TextEntry; break; case DataTypes::YearRole: result = IntegerEntry; break; case DataTypes::LastPlayDate: result = DateEntry; break; case DataTypes::PlayCounter: result = IntegerEntry; break; case DataTypes::LyricsRole: result = LongTextEntry; break; case DataTypes::DurationRole: case DataTypes::SampleRateRole: case DataTypes::BitRateRole: case DataTypes::ChannelsRole: case DataTypes::SecondaryTextRole: case DataTypes::ImageUrlRole: case DataTypes::ShadowForImageRole: case DataTypes::ChildModelRole: case DataTypes::StringDurationRole: case DataTypes::IsValidAlbumArtistRole: case DataTypes::AllArtistsRole: case DataTypes::HighestTrackRating: case DataTypes::IdRole: case DataTypes::ParentIdRole: case DataTypes::DatabaseIdRole: case DataTypes::IsSingleDiscAlbumRole: case DataTypes::ContainerDataRole: case DataTypes::IsPartialDataRole: case DataTypes::AlbumIdRole: case DataTypes::HasEmbeddedCover: case DataTypes::FileModificationTime: case DataTypes::FirstPlayDate: case DataTypes::PlayFrequency: case DataTypes::ElementTypeRole: break; } break; } return result; } bool TrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { mTrackData[mTrackKeys[index.row()]] = value; emit dataChanged(index, index, QVector() << role); return true; } return false; } QHash TrackMetadataModel::roleNames() const { auto names = QAbstractListModel::roleNames(); names[ItemNameRole] = "name"; names[ItemTypeRole] = "type"; return names; } QString TrackMetadataModel::fileUrl() const { return mFileUrl; } QUrl TrackMetadataModel::coverUrl() const { if (mCoverImage.isEmpty()) { return QUrl(QStringLiteral("image://icon/media-optical-audio")); } else { return mCoverImage; } } MusicListenersManager *TrackMetadataModel::manager() const { return mManager; } QString TrackMetadataModel::lyrics() const { return mFullData[TrackDataType::key_type::LyricsRole].toString(); } qulonglong TrackMetadataModel::databaseId() const { return mDatabaseId; } void TrackMetadataModel::trackData(const TrackMetadataModel::TrackDataType &trackData) { if (!mFullData.isEmpty() && trackData.databaseId() != mFullData.databaseId()) { return; } const QList fieldsForTrack({DataTypes::TitleRole, DataTypes::ArtistRole, DataTypes::AlbumRole, DataTypes::AlbumArtistRole, DataTypes::TrackNumberRole, DataTypes::DiscNumberRole, DataTypes::RatingRole, DataTypes::GenreRole, DataTypes::LyricistRole, DataTypes::ComposerRole, DataTypes::CommentRole, DataTypes::YearRole, DataTypes::LastPlayDate, DataTypes::PlayCounter}); fillDataFromTrackData(trackData, fieldsForTrack); } void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData, const QList &fieldsForTrack) { beginResetModel(); mFullData = trackData; mTrackData.clear(); mTrackKeys.clear(); for (DataTypes::ColumnsRoles role : fieldsForTrack) { if (trackData.constFind(role) != trackData.constEnd()) { if (role == DataTypes::RatingRole) { if (trackData[role].toInt() == 0) { continue; } } mTrackKeys.push_back(role); mTrackData[role] = trackData[role]; } } filterDataFromTrackData(); endResetModel(); fetchLyrics(); mDatabaseId = trackData[DataTypes::DatabaseIdRole].toULongLong(); Q_EMIT databaseIdChanged(); mCoverImage = trackData[DataTypes::ImageUrlRole].toUrl(); Q_EMIT coverUrlChanged(); auto rawFileUrl = trackData[DataTypes::ResourceRole].toUrl(); if (rawFileUrl.isLocalFile()) { mFileUrl = rawFileUrl.toLocalFile(); } else { mFileUrl = rawFileUrl.toString(); } Q_EMIT fileUrlChanged(); } void TrackMetadataModel::filterDataFromTrackData() { } void TrackMetadataModel::removeMetaData(DataTypes::ColumnsRoles metaData) { auto itMetaData = std::find(mTrackKeys.begin(), mTrackKeys.end(), metaData); if (itMetaData == mTrackKeys.end()) { return; } mTrackKeys.erase(itMetaData); mTrackData.remove(metaData); } TrackMetadataModel::TrackDataType::mapped_type TrackMetadataModel::dataFromType(TrackDataType::key_type metaData) const { return mFullData[metaData]; } void TrackMetadataModel::fillLyricsDataFromTrack() { beginInsertRows({}, mTrackData.size(), mTrackData.size()); mTrackKeys.push_back(DataTypes::LyricsRole); mTrackData[DataTypes::LyricsRole] = mLyricsValueWatcher.result(); endInsertRows(); } void TrackMetadataModel::lyricsValueIsReady() { if (!mLyricsValueWatcher.result().isEmpty()) { fillLyricsDataFromTrack(); mFullData[DataTypes::LyricsRole] = mLyricsValueWatcher.result(); Q_EMIT lyricsChanged(); } } void TrackMetadataModel::initializeByIdAndUrl(ElisaUtils::PlayListEntryType type, qulonglong databaseId, const QUrl &url) { mFullData.clear(); mTrackData.clear(); mCoverImage.clear(); mFileUrl.clear(); Q_EMIT lyricsChanged(); Q_EMIT needDataByDatabaseIdAndUrl(type, databaseId, url); } void TrackMetadataModel::initialize(MusicListenersManager *newManager, DatabaseInterface *trackDatabase) { mManager = newManager; Q_EMIT managerChanged(); if (mManager) { mDataLoader.setDatabase(mManager->viewDatabase()); - connect(this, &TrackMetadataModel::needDataByUrl, - mManager->tracksListener(), &TracksListener::trackByFileNameInList); - connect(mManager->tracksListener(), &TracksListener::trackHasChanged, - this, &TrackMetadataModel::trackData); } else if (trackDatabase) { mDataLoader.setDatabase(trackDatabase); } if (mManager) { mManager->connectModel(&mDataLoader); } connect(this, &TrackMetadataModel::needDataByDatabaseIdAndUrl, &mDataLoader, &ModelDataLoader::loadDataByDatabaseIdAndUrl); connect(this, &TrackMetadataModel::needDataByUrl, &mDataLoader, &ModelDataLoader::loadDataByUrl); connect(this, &TrackMetadataModel::saveRadioData, &mDataLoader, &ModelDataLoader::saveRadioModified); connect(this, &TrackMetadataModel::deleteRadioData, &mDataLoader, &ModelDataLoader::removeRadio); connect(&mDataLoader, &ModelDataLoader::trackModified, this, &TrackMetadataModel::trackData); connect(&mDataLoader, &ModelDataLoader::allTrackData, this, &TrackMetadataModel::trackData); connect(&mDataLoader, &ModelDataLoader::allRadioData, this, &TrackMetadataModel::radioData); connect(&mDataLoader, &ModelDataLoader::radioAdded, this, &TrackMetadataModel::radioData); connect(&mDataLoader, &ModelDataLoader::radioModified, this, &TrackMetadataModel::radioData); + connect(this, &TrackMetadataModel::needDataByUrl, + &mDataLoader, &ModelDataLoader::loadDataByUrl); } void TrackMetadataModel::fetchLyrics() { auto lyricicsValue = QtConcurrent::run(QThreadPool::globalInstance(), [=]() { auto trackData = mFileScanner.scanOneFile(mFullData[DataTypes::ResourceRole].toUrl()); if (!trackData.lyrics().isEmpty()) { return trackData.lyrics(); } return QString{}; }); mLyricsValueWatcher.setFuture(lyricicsValue); } void TrackMetadataModel::initializeForNewRadio() { mFullData.clear(); mTrackData.clear(); fillDataForNewRadio(); } void TrackMetadataModel::fillDataForNewRadio() { beginResetModel(); mTrackData.clear(); mTrackKeys.clear(); for (auto role : { DataTypes::TitleRole, DataTypes::ResourceRole, DataTypes::CommentRole, DataTypes::DatabaseIdRole }) { mTrackKeys.push_back(role); if (role == DataTypes::DatabaseIdRole) { mTrackData[role] = -1; } else { mTrackData[role] = QString(); } } filterDataFromTrackData(); endResetModel(); } void TrackMetadataModel::initializeByUrl(ElisaUtils::PlayListEntryType type, const QUrl &url) { mFullData.clear(); mTrackData.clear(); mCoverImage.clear(); mFileUrl.clear(); Q_EMIT lyricsChanged(); Q_EMIT needDataByUrl(type, url); } void TrackMetadataModel::setManager(MusicListenersManager *newManager) { initialize(newManager, nullptr); } void TrackMetadataModel::setDatabase(DatabaseInterface *trackDatabase) { initialize(nullptr, trackDatabase); } void TrackMetadataModel::saveData() { Q_EMIT saveRadioData(mTrackData); } void TrackMetadataModel::deleteRadio() { if (mTrackData[DataTypes::DatabaseIdRole]>=0) { Q_EMIT deleteRadioData(mTrackData[DataTypes::DatabaseIdRole].toULongLong()); } } void TrackMetadataModel::radioData(const TrackDataType &radiosData) { if (!mFullData.isEmpty() && mFullData[DataTypes::DatabaseIdRole].toInt() != -1 && mFullData.databaseId() != radiosData.databaseId()) { return; } const QList fieldsForTrack({DataTypes::TitleRole, DataTypes::ResourceRole, DataTypes::CommentRole, DataTypes::DatabaseIdRole}); fillDataFromTrackData(radiosData, fieldsForTrack); } #include "moc_trackmetadatamodel.cpp" diff --git a/src/trackslistener.cpp b/src/trackslistener.cpp index e2cf1339..50bbee2f 100644 --- a/src/trackslistener.cpp +++ b/src/trackslistener.cpp @@ -1,302 +1,302 @@ /* * Copyright 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 "trackslistener.h" #include "playListLogging.h" #include "databaseinterface.h" #include "datatypes.h" #include "filescanner.h" #include #include #include #include #include class TracksListenerPrivate { public: QSet mTracksByIdSet; QSet mRadiosByIdSet; QList> mTracksByNameSet; QList mTracksByFileNameSet; DatabaseInterface *mDatabase = nullptr; FileScanner mFileScanner; }; TracksListener::TracksListener(DatabaseInterface *database, QObject *parent) : QObject(parent), d(std::make_unique()) { d->mDatabase = database; } TracksListener::~TracksListener() = default; void TracksListener::tracksAdded(const ListTrackDataType &allTracks) { for (const auto &oneTrack : allTracks) { if (d->mTracksByIdSet.contains(oneTrack.databaseId())) { Q_EMIT trackHasChanged(oneTrack); } if (d->mTracksByNameSet.isEmpty()) { return; } for (auto itTrack = d->mTracksByNameSet.begin(); itTrack != d->mTracksByNameSet.end(); ) { if (!std::get<0>(*itTrack).isEmpty() && std::get<0>(*itTrack) != oneTrack.title()) { ++itTrack; continue; } if (!std::get<1>(*itTrack).isEmpty() && std::get<1>(*itTrack) != oneTrack.artist()) { ++itTrack; continue; } if (!std::get<2>(*itTrack).isEmpty() && std::get<2>(*itTrack) != oneTrack.album()) { ++itTrack; continue; } if (std::get<3>(*itTrack) != oneTrack.trackNumber()) { ++itTrack; continue; } if (std::get<4>(*itTrack) != oneTrack.discNumber()) { ++itTrack; continue; } Q_EMIT trackHasChanged(TrackDataType(oneTrack)); d->mTracksByIdSet.insert(oneTrack.databaseId()); itTrack = d->mTracksByNameSet.erase(itTrack); } } } void TracksListener::trackRemoved(qulonglong id) { if (d->mTracksByIdSet.contains(id)) { Q_EMIT trackHasBeenRemoved(id); } } void TracksListener::trackModified(const TrackDataType &modifiedTrack) { if (d->mTracksByIdSet.contains(modifiedTrack.databaseId())) { Q_EMIT trackHasChanged(modifiedTrack); } } void TracksListener::trackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album, const QVariant &trackNumber, const QVariant &discNumber) { const auto realTitle = title.toString(); const auto realArtist = artist.toString(); const auto albumIsValid = !album.isNull() && album.isValid() && !album.toString().isEmpty(); auto realAlbum = std::optional{}; if (albumIsValid) { realAlbum = album.toString(); } auto trackNumberIsValid = bool{}; const auto trackNumberValue = trackNumber.toInt(&trackNumberIsValid); auto realTrackNumber = std::optional{}; if (trackNumberIsValid) { realTrackNumber = trackNumberValue; } auto discNumberIsValid = bool{}; const auto discNumberValue = discNumber.toInt(&discNumberIsValid); auto realDiscNumber = std::optional{}; if (discNumberIsValid) { realDiscNumber = discNumberValue; } auto newTrackId = d->mDatabase->trackIdFromTitleAlbumTrackDiscNumber(realTitle, realArtist, realAlbum, realTrackNumber, realDiscNumber); if (newTrackId == 0) { auto newTrack = std::tuple(realTitle, realArtist, album.toString(), trackNumber.toInt(), discNumber.toInt()); d->mTracksByNameSet.push_back(newTrack); return; } d->mTracksByIdSet.insert(newTrackId); auto newTrack = d->mDatabase->trackDataFromDatabaseId(newTrackId); if (!newTrack.isEmpty()) { Q_EMIT trackHasChanged(newTrack); } } -void TracksListener::trackByFileNameInList(ElisaUtils::PlayListEntryType databaseIdType, const QUrl &fileName) +void TracksListener::trackByFileNameInList(const QUrl &fileName) { if (fileName.isLocalFile() || fileName.scheme().isEmpty()) { auto newTrackId = d->mDatabase->trackIdFromFileName(fileName); if (newTrackId == 0) { auto newTrack = d->mFileScanner.scanOneFile(fileName); if (newTrack.isValid()) { d->mTracksByFileNameSet.push_back(fileName); Q_EMIT trackHasChanged(newTrack); return; } d->mTracksByFileNameSet.push_back(fileName); return; } } else { auto newRadioId = d->mDatabase->radioIdFromFileName(fileName); if (newRadioId) { auto newRadio = d->mDatabase->radioDataFromDatabaseId(newRadioId); if (!newRadio.isEmpty()) { Q_EMIT trackHasChanged({newRadio}); } } } } void TracksListener::newAlbumInList(qulonglong newDatabaseId, const QString &entryTitle) { qCDebug(orgKdeElisaPlayList()) << "TracksListener::newAlbumInList" << newDatabaseId << entryTitle << d->mDatabase->albumData(newDatabaseId); Q_EMIT tracksListAdded(newDatabaseId, entryTitle, ElisaUtils::Album, d->mDatabase->albumData(newDatabaseId)); } void TracksListener::newEntryInList(qulonglong newDatabaseId, const QString &entryTitle, ElisaUtils::PlayListEntryType databaseIdType) { qCDebug(orgKdeElisaPlayList()) << "TracksListener::newEntryInList" << newDatabaseId << entryTitle << databaseIdType; switch (databaseIdType) { case ElisaUtils::Track: { d->mTracksByIdSet.insert(newDatabaseId); auto newTrack = d->mDatabase->trackDataFromDatabaseId(newDatabaseId); if (!newTrack.isEmpty()) { Q_EMIT trackHasChanged(newTrack); } break; } case ElisaUtils::Radio: { d->mRadiosByIdSet.insert(newDatabaseId); auto newRadio = d->mDatabase->radioDataFromDatabaseId(newDatabaseId); if (!newRadio.isEmpty()) { Q_EMIT trackHasChanged(newRadio); } break; } case ElisaUtils::Artist: newArtistInList(newDatabaseId, entryTitle); break; case ElisaUtils::FileName: newUrlInList(QUrl::fromLocalFile(entryTitle), ElisaUtils::FileName); break; case ElisaUtils::Album: newAlbumInList(newDatabaseId, entryTitle); break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Unknown: break; } } void TracksListener::newUrlInList(const QUrl &entryUrl, ElisaUtils::PlayListEntryType databaseIdType) { switch (databaseIdType) { case ElisaUtils::Track: case ElisaUtils::FileName: { auto newDatabaseId = d->mDatabase->trackIdFromFileName(entryUrl); if (!newDatabaseId) { - trackByFileNameInList(databaseIdType, entryUrl); + trackByFileNameInList(entryUrl); return; } d->mTracksByIdSet.insert(newDatabaseId); auto newTrack = d->mDatabase->trackDataFromDatabaseIdAndUrl(newDatabaseId, entryUrl); if (!newTrack.isEmpty()) { Q_EMIT trackHasChanged(newTrack); } break; } case ElisaUtils::Radio: { auto newDatabaseId = d->mDatabase->radioIdFromFileName(entryUrl); if (!newDatabaseId) { return; } d->mRadiosByIdSet.insert(newDatabaseId); auto newRadio = d->mDatabase->radioDataFromDatabaseId(newDatabaseId); if (!newRadio.isEmpty()) { Q_EMIT trackHasChanged(newRadio); } break; } case ElisaUtils::Artist: case ElisaUtils::Album: case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::Genre: case ElisaUtils::Unknown: break; } } void TracksListener::newArtistInList(qulonglong newDatabaseId, const QString &artist) { auto newTracks = d->mDatabase->tracksDataFromAuthor(artist); if (newTracks.isEmpty()) { return; } for (const auto &oneTrack : newTracks) { d->mTracksByIdSet.insert(oneTrack.databaseId()); } Q_EMIT tracksListAdded(newDatabaseId, artist, ElisaUtils::Artist, newTracks); } #include "moc_trackslistener.cpp" diff --git a/src/trackslistener.h b/src/trackslistener.h index 2d5cd8d3..3eeb6e33 100644 --- a/src/trackslistener.h +++ b/src/trackslistener.h @@ -1,88 +1,88 @@ /* * Copyright 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 . */ #ifndef TRACKSLISTENER_H #define TRACKSLISTENER_H #include "elisaLib_export.h" #include "databaseinterface.h" #include "datatypes.h" #include "elisautils.h" #include #include class TracksListenerPrivate; class ELISALIB_EXPORT TracksListener : public QObject { Q_OBJECT public: using ListTrackDataType = DataTypes::ListTrackDataType; using TrackDataType = DataTypes::TrackDataType; explicit TracksListener(DatabaseInterface *database, QObject *parent = nullptr); ~TracksListener() override; Q_SIGNALS: void trackHasChanged(const TracksListener::TrackDataType &audioTrack); void trackHasBeenRemoved(qulonglong id); void tracksListAdded(qulonglong newDatabaseId, const QString &entryTitle, ElisaUtils::PlayListEntryType databaseIdType, const TracksListener::ListTrackDataType &tracks); public Q_SLOTS: void tracksAdded(const TracksListener::ListTrackDataType &allTracks); void trackRemoved(qulonglong id); void trackModified(const TracksListener::TrackDataType &modifiedTrack); void trackByNameInList(const QVariant &title, const QVariant &artist, const QVariant &album, const QVariant &trackNumber, const QVariant &discNumber); void newEntryInList(qulonglong newDatabaseId, const QString &entryTitle, ElisaUtils::PlayListEntryType databaseIdType); - void trackByFileNameInList(ElisaUtils::PlayListEntryType databaseIdType, const QUrl &fileName); + void trackByFileNameInList(const QUrl &fileName); void newUrlInList(const QUrl &entryUrl, ElisaUtils::PlayListEntryType databaseIdType); private: void newArtistInList(qulonglong newDatabaseId, const QString &artist); void newAlbumInList(qulonglong newDatabaseId, const QString &entryTitle); std::unique_ptr d; }; #endif // TRACKSLISTENER_H