diff --git a/src/audiowrapper.h b/src/audiowrapper.h --- a/src/audiowrapper.h +++ b/src/audiowrapper.h @@ -122,6 +122,8 @@ void positionChanged(qint64 position); + void currentPlayingForRadiosChanged(QString title, QString nowPlaying); + void seekableChanged(bool seekable); void playing(); diff --git a/src/audiowrapper_libvlc.cpp b/src/audiowrapper_libvlc.cpp --- a/src/audiowrapper_libvlc.cpp +++ b/src/audiowrapper_libvlc.cpp @@ -233,7 +233,15 @@ void AudioWrapper::setSource(const QUrl &source) { - d->mMedia = libvlc_media_new_path(d->mInstance, QDir::toNativeSeparators(source.toLocalFile()).toUtf8().constData()); + if (source.isLocalFile()) { + qCDebug(orgKdeElisaPlayerVlc) << "AudioWrapper::setSource reading local resource"; + d->mMedia = libvlc_media_new_path(d->mInstance, QDir::toNativeSeparators(source.toLocalFile()).toUtf8().constData()); + } else { + qCDebug(orgKdeElisaPlayerVlc) << "AudioWrapper::setSource reading remote resource"; + const char * charUrl = source.url().toUtf8().constData(); + d->mMedia = libvlc_media_new_location(d->mInstance, charUrl); + } + if (!d->mMedia) { qCDebug(orgKdeElisaPlayerVlc) << "AudioWrapper::setSource" << "failed creating media" @@ -557,6 +565,13 @@ mParent->playerPositionSignalChanges(mPreviousPosition); } + + if (this->mMedia) { + QString title = QLatin1String(libvlc_media_get_meta(this->mMedia, libvlc_meta_Title)); + QString nowPlaying = QLatin1String(libvlc_media_get_meta(this->mMedia, libvlc_meta_NowPlaying)); + + Q_EMIT mParent->currentPlayingForRadiosChanged(title, nowPlaying); + } } void AudioWrapperPrivate::signalSeekableChange(bool isSeekable) diff --git a/src/databaseinterface.h b/src/databaseinterface.h --- a/src/databaseinterface.h +++ b/src/databaseinterface.h @@ -246,6 +246,8 @@ using ListTrackDataType = QList; + using ListRadioDataType = QList; + class AlbumDataType : public DataType { public: @@ -357,6 +359,8 @@ ListTrackDataType allTracksData(); + ListRadioDataType allRadiosData(); + ListTrackDataType recentlyPlayedTracksData(int count); ListTrackDataType frequentlyPlayedTracksData(int count); @@ -383,8 +387,9 @@ TrackDataType trackDataFromDatabaseId(qulonglong id); - qulonglong trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, - std::optional trackNumber, std::optional discNumber); + TrackDataType radioDataFromDatabaseId(qulonglong id); + + qulonglong trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, std::optional trackNumber, std::optional discNumber); qulonglong trackIdFromFileName(const QUrl &fileName); @@ -426,6 +431,12 @@ void finishRemovingTracksList(); + void radioAdded(const DatabaseInterface::TrackDataType radio); + + void radioModified(const DatabaseInterface::TrackDataType radio); + + void radioRemoved(qulonglong radioId); + public Q_SLOTS: void insertTracksList(const QList &tracks, const QHash &covers); @@ -438,6 +449,10 @@ void clearData(); + void insertRadio(const TrackDataType &oneTrack); + + void removeRadio(qulonglong radioId); + private: enum class TrackFileInsertType { @@ -472,6 +487,8 @@ qulonglong internalTrackIdFromFileName(const QUrl &fileName); + qulonglong internalRadioIdFromHttpAddress(const QString &httpAddress); + ListTrackDataType internalTracksFromAuthor(const QString &artistName); QList internalAlbumIdsFromAuthor(const QString &artistName); @@ -515,6 +532,8 @@ TrackDataType buildTrackDataFromDatabaseRecord(const QSqlRecord &trackRecord) const; + TrackDataType buildRadioDataFromDatabaseRecord(const QSqlRecord &trackRecord) const; + void internalRemoveTracksList(const QList &removedTracks); void internalRemoveTracksList(const QHash &removedTracks, qulonglong sourceId); @@ -539,12 +558,16 @@ ListTrackDataType internalAllTracksPartialData(); + ListRadioDataType internalAllRadiosPartialData(); + ListTrackDataType internalRecentlyPlayedTracksData(int count); ListTrackDataType internalFrequentlyPlayedTracksData(int count); TrackDataType internalOneTrackPartialData(qulonglong databaseId); + TrackDataType internalOneRadioPartialData(qulonglong databaseId); + ListGenreDataType internalAllGenresPartialData(); ListArtistDataType internalAllComposersPartialData(); @@ -570,6 +593,8 @@ void upgradeDatabaseV13(); + void upgradeDatabaseV14(); + void checkDatabaseSchema(); void checkAlbumsTableSchema(); diff --git a/src/databaseinterface.cpp b/src/databaseinterface.cpp --- a/src/databaseinterface.cpp +++ b/src/databaseinterface.cpp @@ -49,17 +49,20 @@ mSelectTrackQuery(mTracksDatabase), mSelectAlbumIdFromTitleQuery(mTracksDatabase), mInsertAlbumQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumIdArtistQuery(mTracksDatabase), mInsertTrackQuery(mTracksDatabase), mSelectTracksFromArtist(mTracksDatabase), - mSelectTrackFromIdQuery(mTracksDatabase), mSelectCountAlbumsForArtistQuery(mTracksDatabase), + mSelectTrackFromIdQuery(mTracksDatabase), mSelectRadioFromIdQuery(mTracksDatabase), + mSelectCountAlbumsForArtistQuery(mTracksDatabase), mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery(mTracksDatabase), mSelectAllAlbumsFromArtistQuery(mTracksDatabase), mSelectAllArtistsQuery(mTracksDatabase), mInsertArtistsQuery(mTracksDatabase), mSelectArtistByNameQuery(mTracksDatabase), mSelectArtistQuery(mTracksDatabase), mUpdateTrackStatistics(mTracksDatabase), mRemoveTrackQuery(mTracksDatabase), mRemoveAlbumQuery(mTracksDatabase), mRemoveArtistQuery(mTracksDatabase), mSelectAllTracksQuery(mTracksDatabase), + mSelectAllRadiosQuery(mTracksDatabase), mInsertTrackMapping(mTracksDatabase), mUpdateTrackFirstPlayStatistics(mTracksDatabase), mInsertMusicSource(mTracksDatabase), mSelectMusicSource(mTracksDatabase), mUpdateTrackPriority(mTracksDatabase), mUpdateTrackFileModifiedTime(mTracksDatabase), mSelectTracksMapping(mTracksDatabase), mSelectTracksMappingPriority(mTracksDatabase), + mSelectRadioIdFromHttpAddress(mTracksDatabase), mUpdateAlbumArtUriFromAlbumIdQuery(mTracksDatabase), mSelectTracksMappingPriorityByTrackId(mTracksDatabase), mSelectAlbumIdsFromArtist(mTracksDatabase), mSelectAllTrackFilesQuery(mTracksDatabase), mRemoveTracksMappingFromSource(mTracksDatabase), mRemoveTracksMapping(mTracksDatabase), @@ -76,15 +79,17 @@ mSelectCountAlbumsForLyricistQuery(mTracksDatabase), mSelectAllGenresQuery(mTracksDatabase), mSelectGenreForArtistQuery(mTracksDatabase), mSelectGenreForAlbumQuery(mTracksDatabase), mUpdateTrackQuery(mTracksDatabase), mUpdateAlbumArtistQuery(mTracksDatabase), + mUpdateRadioQuery(mTracksDatabase), mUpdateAlbumArtistInTracksQuery(mTracksDatabase), mQueryMaximumTrackIdQuery(mTracksDatabase), mQueryMaximumAlbumIdQuery(mTracksDatabase), mQueryMaximumArtistIdQuery(mTracksDatabase), mQueryMaximumLyricistIdQuery(mTracksDatabase), mQueryMaximumComposerIdQuery(mTracksDatabase), mQueryMaximumGenreIdQuery(mTracksDatabase), mSelectAllArtistsWithGenreFilterQuery(mTracksDatabase), mSelectAllAlbumsShortWithGenreArtistFilterQuery(mTracksDatabase), mSelectAllAlbumsShortWithArtistFilterQuery(mTracksDatabase), mSelectAllRecentlyPlayedTracksQuery(mTracksDatabase), mSelectAllFrequentlyPlayedTracksQuery(mTracksDatabase), mClearTracksTable(mTracksDatabase), mClearAlbumsTable(mTracksDatabase), mClearArtistsTable(mTracksDatabase), mClearComposerTable(mTracksDatabase), mClearGenreTable(mTracksDatabase), mClearLyricistTable(mTracksDatabase), - mArtistMatchGenreQuery(mTracksDatabase), mSelectTrackIdQuery(mTracksDatabase) + mArtistMatchGenreQuery(mTracksDatabase), mSelectTrackIdQuery(mTracksDatabase), + mInsertRadioQuery(mTracksDatabase), mDeleteRadioQuery(mTracksDatabase) { } @@ -106,6 +111,8 @@ QSqlQuery mSelectTrackFromIdQuery; + QSqlQuery mSelectRadioFromIdQuery; + QSqlQuery mSelectCountAlbumsForArtistQuery; QSqlQuery mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery; @@ -130,6 +137,8 @@ QSqlQuery mSelectAllTracksQuery; + QSqlQuery mSelectAllRadiosQuery; + QSqlQuery mInsertTrackMapping; QSqlQuery mUpdateTrackFirstPlayStatistics; @@ -146,6 +155,8 @@ QSqlQuery mSelectTracksMappingPriority; + QSqlQuery mSelectRadioIdFromHttpAddress; + QSqlQuery mUpdateAlbumArtUriFromAlbumIdQuery; QSqlQuery mSelectTracksMappingPriorityByTrackId; @@ -208,6 +219,8 @@ QSqlQuery mUpdateAlbumArtistQuery; + QSqlQuery mUpdateRadioQuery; + QSqlQuery mUpdateAlbumArtistInTracksQuery; QSqlQuery mQueryMaximumTrackIdQuery; @@ -248,6 +261,10 @@ QSqlQuery mSelectTrackIdQuery; + QSqlQuery mInsertRadioQuery; + + QSqlQuery mDeleteRadioQuery; + QSet mModifiedTrackIds; QSet mModifiedAlbumIds; @@ -364,6 +381,29 @@ return result; } +DatabaseInterface::ListRadioDataType DatabaseInterface::allRadiosData() +{ + auto result = ListRadioDataType{}; + + if (!d) { + return result; + } + + auto transactionResult = startTransaction(); + if (!transactionResult) { + return result; + } + + result = internalAllRadiosPartialData(); + + transactionResult = finishTransaction(); + if (!transactionResult) { + return result; + } + + return result; +} + DatabaseInterface::ListTrackDataType DatabaseInterface::recentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; @@ -702,8 +742,31 @@ return result; } +DatabaseInterface::TrackDataType DatabaseInterface::radioDataFromDatabaseId(qulonglong id) +{ + auto result = TrackDataType(); + + if (!d) { + return result; + } + + auto transactionResult = startTransaction(); + if (!transactionResult) { + return result; + } + + result = internalOneRadioPartialData(id); + + transactionResult = finishTransaction(); + if (!transactionResult) { + return result; + } + + return result; +} + qulonglong DatabaseInterface::trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional &album, - std::optional trackNumber, std::optional discNumber) + std::optional trackNumber, std::optional discNumber) { auto result = qulonglong(0); @@ -1153,6 +1216,7 @@ upgradeDatabaseV11(); upgradeDatabaseV12(); upgradeDatabaseV13(); + upgradeDatabaseV14(); checkDatabaseSchema(); } else if (listTables.contains(QStringLiteral("DatabaseVersionV9"))) { @@ -1165,13 +1229,17 @@ if (!listTables.contains(QStringLiteral("DatabaseVersionV13"))) { upgradeDatabaseV13(); } + if (!listTables.contains(QStringLiteral("DatabaseVersionV14"))) { + upgradeDatabaseV14(); + } checkDatabaseSchema(); } else { createDatabaseV9(); upgradeDatabaseV11(); upgradeDatabaseV12(); upgradeDatabaseV13(); + upgradeDatabaseV14(); } } @@ -2735,6 +2803,107 @@ qCInfo(orgKdeElisaDatabase) << "finished update to v13 of database schema"; } +void DatabaseInterface::upgradeDatabaseV14() +{ + qCInfo(orgKdeElisaDatabase) << "begin update to v14 of database schema"; + + { + QSqlQuery createSchemaQuery(d->mTracksDatabase); + + const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `DatabaseVersionV14` (`Version` INTEGER PRIMARY KEY NOT NULL)")); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastError(); + + Q_EMIT databaseError(); + } + } + + { + QSqlQuery disableForeignKeys(d->mTracksDatabase); + + auto result = disableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=OFF")); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << disableForeignKeys.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << disableForeignKeys.lastError(); + + Q_EMIT databaseError(); + } + } + + d->mTracksDatabase.transaction(); + + { + QSqlQuery createSchemaQuery(d->mTracksDatabase); + + const auto &result = createSchemaQuery.exec(QStringLiteral("CREATE TABLE `Radios` (" + "`ID` INTEGER PRIMARY KEY AUTOINCREMENT, " + "`HttpAddress` VARCHAR(255) NOT NULL, " + "`Priority` INTEGER NOT NULL, " + "`Title` VARCHAR(85) NOT NULL, " + "`Rating` INTEGER NOT NULL DEFAULT 0, " + "`Genre` VARCHAR(55), " + "`Comment` VARCHAR(255), " + "UNIQUE (" + "`HttpAddress`" + "), " + "UNIQUE (" + "`Priority`, `Title`, `HttpAddress`" + ") " + "CONSTRAINT fk_tracks_genre FOREIGN KEY (`Genre`) REFERENCES `Genre`(`Name`))" + )); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << createSchemaQuery.lastError(); + + Q_EMIT databaseError(); + } + } + + { + QSqlQuery createSchemaQuery(d->mTracksDatabase); + + //Find webradios (french): https://doc.ubuntu-fr.org/liste_radio_france + //English: https://www.radio.fr/language/english (to get the link play a radio and look for streamUrl in the html elements page). + const auto &result = createSchemaQuery.exec(QStringLiteral("INSERT INTO `Radios` (`HttpAddress`, `Priority`, `Title`) " + "SELECT 'http://classicrock.stream.ouifm.fr/ouifm3.mp3', 1, 'OuiFM_Classic_Rock' UNION ALL " + "SELECT 'http://rock70s.stream.ouifm.fr/ouifmseventies.mp3', 1, 'OuiFM_70s' UNION ALL " + "SELECT 'http://jazzradio.ice.infomaniak.ch/jazzradio-high.mp3', 2 , 'Jazz_Radio' UNION ALL " + "SELECT 'http://cdn.nrjaudio.fm/audio1/fr/30601/mp3_128.mp3?origine=playerweb', 1, 'Nostalgie' UNION ALL " + "SELECT 'https://scdn.nrjaudio.fm/audio1/fr/30713/aac_64.mp3?origine=playerweb', 1, 'Nostalgie Johnny' UNION ALL " + "SELECT 'http://sc-classrock.1.fm:8200', 1, 'Classic rock replay' UNION ALL " + "SELECT 'http://agnes.torontocast.com:8151/stream', 1, 'Instrumentals Forever' UNION ALL " + "SELECT 'https://stream.laut.fm/jahfari', 1, 'Jahfari'" + )); + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << createSchemaQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << createSchemaQuery.lastError(); + + Q_EMIT databaseError(); + } + } + + d->mTracksDatabase.commit(); + + { + QSqlQuery enableForeignKeys(d->mTracksDatabase); + + auto result = enableForeignKeys.exec(QStringLiteral(" PRAGMA foreign_keys=ON")); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << enableForeignKeys.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::upgradeDatabaseV14" << enableForeignKeys.lastError(); + + Q_EMIT databaseError(); + } + } + + qCInfo(orgKdeElisaDatabase) << "finished update to v14 of database schema"; +} + void DatabaseInterface::checkDatabaseSchema() { checkAlbumsTableSchema(); @@ -3451,6 +3620,29 @@ } } + { + auto selectAllRadiosText = QStringLiteral("SELECT " + "radios.`ID`, " + "radios.`Title`, " + "radios.`HttpAddress`, " + "radios.`Rating`, " + "trackGenre.`Name`, " + "radios.`Comment` " + "FROM " + "`Radios` radios " + "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = radios.`Genre` " + ""); + + auto result = prepareQuery(d->mSelectAllRadiosQuery, selectAllRadiosText); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRadiosQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectAllRadiosQuery.lastError(); + + Q_EMIT databaseError(); + } + } + { auto selectAllTracksText = QStringLiteral("SELECT " "tracks.`ID`, " @@ -4245,6 +4437,31 @@ Q_EMIT databaseError(); } } + + { + auto selectRadioFromIdQueryText = QStringLiteral("SELECT " + "radios.`ID`, " + "radios.`Title`, " + "radios.`HttpAddress`, " + "radios.`Rating`, " + "trackGenre.`Name`, " + "radios.`Comment` " + "FROM " + "`Radios` radios " + "LEFT JOIN `Genre` trackGenre ON trackGenre.`Name` = radios.`Genre` " + "WHERE " + "radios.`ID` = :radioId " + ""); + + auto result = prepareQuery(d->mSelectRadioFromIdQuery, selectRadioFromIdQueryText); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioFromIdQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioFromIdQuery.lastError(); + + Q_EMIT databaseError(); + } + } { auto selectCountAlbumsQueryText = QStringLiteral("SELECT count(*) " "FROM `Albums` album " @@ -4650,6 +4867,24 @@ } } + { + auto selectRadioIdFromHttpAddress = QStringLiteral("SELECT " + "`ID` " + "FROM " + "`Radios` " + "WHERE " + "`HttpAddress` = :httpAddress"); + + auto result = prepareQuery(d->mSelectRadioIdFromHttpAddress, selectRadioIdFromHttpAddress); + + if (!result) { + qCInfo(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioIdFromHttpAddress.lastQuery(); + qCInfo(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mSelectRadioIdFromHttpAddress.lastError(); + + Q_EMIT databaseError(); + } + } + { auto selectTracksMappingPriorityQueryText = QStringLiteral("SELECT " "max(tracks.`Priority`) AS Priority " @@ -4871,6 +5106,66 @@ } } + { + auto insertRadioQueryText = QStringLiteral("INSERT INTO `Radios` " + "(" + "`Title`, " + "`httpAddress`, " + "`Comment`, " + "`Rating`, " + "`Priority`) " + "VALUES " + "(" + ":title, " + ":httpAddress, " + ":comment, " + ":trackRating," + "1)"); + + auto result = prepareQuery(d->mInsertRadioQuery, insertRadioQueryText); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertRadioQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mInsertRadioQuery.lastError(); + + Q_EMIT databaseError(); + } + } + + { + auto deleteRadioQueryText = QStringLiteral("DELETE FROM `Radios` " + "WHERE `ID` = :radioId"); + + auto result = prepareQuery(d->mDeleteRadioQuery, deleteRadioQueryText); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mDeleteRadioQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mDeleteRadioQuery.lastError(); + + Q_EMIT databaseError(); + } + } + + { + auto updateRadioQueryText = QStringLiteral("UPDATE `Radios` " + "SET " + "`HttpAddress` = :httpAddress, " + "`Title` = :title, " + "`Comment` = :comment, " + "`Rating` = :trackRating " + "WHERE " + "`ID` = :radioId"); + + auto result = prepareQuery(d->mUpdateRadioQuery, updateRadioQueryText); + + if (!result) { + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateRadioQuery.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::initRequest" << d->mUpdateRadioQuery.lastError(); + + Q_EMIT databaseError(); + } + } + { auto updateAlbumArtistQueryText = QStringLiteral("UPDATE `Albums` " "SET " @@ -6093,6 +6388,27 @@ return result; } +DatabaseInterface::TrackDataType DatabaseInterface::buildRadioDataFromDatabaseRecord(const QSqlRecord &trackRecord) const +{ + TrackDataType result; + + result[TrackDataType::key_type::DatabaseIdRole] = trackRecord.value(0); + result[TrackDataType::key_type::TitleRole] = trackRecord.value(1); + + result[TrackDataType::key_type::AlbumRole] = QStringLiteral("Radios"); + result[TrackDataType::key_type::ArtistRole] = trackRecord.value(1); + + result[TrackDataType::key_type::ResourceRole] = trackRecord.value(2); + result[TrackDataType::key_type::RatingRole] = trackRecord.value(3); + if (!trackRecord.value(4).isNull()) { + result[TrackDataType::key_type::GenreRole] = trackRecord.value(4); + } + result[TrackDataType::key_type::CommentRole] = trackRecord.value(5); + result[DataType::key_type::ElementTypeRole] = ElisaUtils::Radio; + + return result; +} + void DatabaseInterface::internalRemoveTracksList(const QList &removedTracks) { QSet modifiedAlbums; @@ -6480,6 +6796,68 @@ d->mUpdateTrackQuery.finish(); } +void DatabaseInterface::insertRadio(const TrackDataType &oneTrack) +{ + QSqlQuery query = d->mUpdateRadioQuery; + + if (oneTrack.databaseId() == -1ull) { + query = d->mInsertRadioQuery; + } + + query.bindValue(QStringLiteral(":httpAddress"), oneTrack.resourceURI()); + query.bindValue(QStringLiteral(":radioId"), oneTrack.databaseId()); + query.bindValue(QStringLiteral(":title"), oneTrack.title()); + query.bindValue(QStringLiteral(":comment"), oneTrack.comment()); + query.bindValue(QStringLiteral(":trackRating"), oneTrack.rating()); + + auto result = execQuery(query); + + if (!result || !query.isActive()) { + Q_EMIT databaseError(); + + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.boundValues(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastError(); + }else{ + TrackDataType radio(oneTrack); + radio[TrackDataType::key_type::ArtistRole] = radio[TrackDataType::key_type::TitleRole]; + if (radio[TrackDataType::key_type::DatabaseIdRole] == -1) { + radio[TrackDataType::key_type::DatabaseIdRole] = internalRadioIdFromHttpAddress(oneTrack.resourceURI().toString()); + radio[TrackDataType::key_type::AlbumRole] = QStringLiteral("Radios"); + radio[TrackDataType::key_type::ArtistRole] = radio[TrackDataType::key_type::TitleRole]; + radio[DataType::key_type::ElementTypeRole] = ElisaUtils::Radio; + // Genre and rating missing for now, see buildRadioDataFromDatabaseRecord. Should be added if used for radios. + + Q_EMIT radioAdded(radio); + } else { + Q_EMIT radioModified(radio); + } + } + + query.finish(); +} + +void DatabaseInterface::removeRadio(qulonglong radioId) +{ + QSqlQuery query = d->mDeleteRadioQuery; + + query.bindValue(QStringLiteral(":radioId"), radioId); + + auto result = execQuery(query); + + if (!result || !query.isActive()) { + Q_EMIT databaseError(); + + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.boundValues(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::updateTrackInDatabase" << query.lastError(); + }else{ + Q_EMIT radioRemoved(radioId); + } + + query.finish(); +} + void DatabaseInterface::removeAlbumInDatabase(qulonglong albumId) { d->mRemoveAlbumQuery.bindValue(QStringLiteral(":albumId"), albumId); @@ -6822,6 +7200,42 @@ return result; } +qulonglong DatabaseInterface::internalRadioIdFromHttpAddress(const QString &httpAddress) +{ + auto result = qulonglong(0); + + if (!d) { + return result; + } + + d->mSelectRadioIdFromHttpAddress.bindValue(QStringLiteral(":httpAddress"), httpAddress); + + auto queryResult = execQuery(d->mSelectRadioIdFromHttpAddress); + + if (!queryResult || !d->mSelectRadioIdFromHttpAddress.isSelect() || !d->mSelectRadioIdFromHttpAddress.isActive()) { + Q_EMIT databaseError(); + + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.lastQuery(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.boundValues(); + qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalTrackIdFromFileName" << d->mSelectRadioIdFromHttpAddress.lastError(); + + d->mSelectRadioIdFromHttpAddress.finish(); + + return result; + } + + if (d->mSelectRadioIdFromHttpAddress.next()) { + const auto ¤tRecordValue = d->mSelectRadioIdFromHttpAddress.record().value(0); + if (currentRecordValue.isValid()) { + result = currentRecordValue.toULongLong(); + } + } + + d->mSelectRadioIdFromHttpAddress.finish(); + + return result; +} + DatabaseInterface::ListTrackDataType DatabaseInterface::internalTracksFromAuthor(const QString &ArtistName) { auto allTracks = ListTrackDataType{}; @@ -7023,6 +7437,27 @@ return result; } +DatabaseInterface::ListRadioDataType DatabaseInterface::internalAllRadiosPartialData() +{ + auto result = ListRadioDataType{}; + + if (!internalGenericPartialData(d->mSelectAllRadiosQuery)) { + return result; + } + + while(d->mSelectAllRadiosQuery.next()) { + const auto ¤tRecord = d->mSelectAllRadiosQuery.record(); + + auto newData = buildRadioDataFromDatabaseRecord(currentRecord); + + result.push_back(newData); + } + + d->mSelectAllRadiosQuery.finish(); + + return result; +} + DatabaseInterface::ListTrackDataType DatabaseInterface::internalRecentlyPlayedTracksData(int count) { auto result = ListTrackDataType{}; @@ -7090,6 +7525,27 @@ return result; } +DatabaseInterface::TrackDataType DatabaseInterface::internalOneRadioPartialData(qulonglong databaseId) +{ + auto result = TrackDataType{}; + + d->mSelectRadioFromIdQuery.bindValue(QStringLiteral(":radioId"), databaseId); + + if (!internalGenericPartialData(d->mSelectRadioFromIdQuery)) { + return result; + } + + if (d->mSelectRadioFromIdQuery.next()) { + const auto ¤tRecord = d->mSelectRadioFromIdQuery.record(); + + result = buildRadioDataFromDatabaseRecord(currentRecord); + } + + d->mSelectRadioFromIdQuery.finish(); + + return result; +} + DatabaseInterface::ListGenreDataType DatabaseInterface::internalAllGenresPartialData() { ListGenreDataType result; diff --git a/src/elisaapplication.cpp b/src/elisaapplication.cpp --- a/src/elisaapplication.cpp +++ b/src/elisaapplication.cpp @@ -361,21 +361,22 @@ QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::playerSourceChanged, d->mAudioWrapper.get(), &AudioWrapper::setSource); QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::startedPlayingTrack, d->mMusicManager->viewDatabase(), &DatabaseInterface::trackHasStartedPlaying); + QObject::connect(d->mAudioControl.get(), &ManageAudioPlayer::currentPlayingForRadiosChanged, d->mMediaPlayList.get(), &MediaPlayList::updateRadioData); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::ensurePlay, d->mAudioControl.get(), &ManageAudioPlayer::ensurePlay); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::playListFinished, d->mAudioControl.get(), &ManageAudioPlayer::playListFinished); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::currentTrackChanged, d->mAudioControl.get(), &ManageAudioPlayer::setCurrentTrack); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::clearPlayListPlayer, d->mAudioControl.get(), &ManageAudioPlayer::saveForUndoClearPlaylist); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::undoClearPlayListPlayer, d->mAudioControl.get(), &ManageAudioPlayer::restoreForUndoClearPlaylist); - QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::playbackStateChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerPlaybackState); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::statusChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerStatus); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::errorChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerError); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::durationChanged, d->mAudioControl.get(), &ManageAudioPlayer::setAudioDuration); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::seekableChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerIsSeekable); QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::positionChanged, d->mAudioControl.get(), &ManageAudioPlayer::setPlayerPosition); + QObject::connect(d->mAudioWrapper.get(), &AudioWrapper::currentPlayingForRadiosChanged, d->mAudioControl.get(), &ManageAudioPlayer::setCurrentPlayingForRadios); d->mPlayerControl->setPlayListModel(d->mMediaPlayList.get()); QObject::connect(d->mMediaPlayList.get(), &MediaPlayList::currentTrackChanged, d->mPlayerControl.get(), &ManageMediaPlayerControl::setCurrentTrack); diff --git a/src/elisaqmlplugin.cpp b/src/elisaqmlplugin.cpp --- a/src/elisaqmlplugin.cpp +++ b/src/elisaqmlplugin.cpp @@ -146,10 +146,12 @@ qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType("DatabaseInterface::ListTrackDataType"); + qRegisterMetaType("DatabaseInterface::ListRadioDataType"); qRegisterMetaType("DatabaseInterface::ListAlbumDataType"); qRegisterMetaType("DatabaseInterface::ListArtistDataType"); qRegisterMetaType("DatabaseInterface::ListGenreDataType"); qRegisterMetaType("ModelDataLoader::ListTrackDataType"); + qRegisterMetaType("ModelDataLoader::ListRadioDataType"); qRegisterMetaType("ModelDataLoader::ListAlbumDataType"); qRegisterMetaType("ModelDataLoader::ListArtistDataType"); qRegisterMetaType("ModelDataLoader::ListGenreDataType"); diff --git a/src/elisautils.h b/src/elisautils.h --- a/src/elisautils.h +++ b/src/elisautils.h @@ -55,6 +55,7 @@ Track, FileName, Unknown, + Radio, }; Q_ENUM_NS(PlayListEntryType) diff --git a/src/manageaudioplayer.h b/src/manageaudioplayer.h --- a/src/manageaudioplayer.h +++ b/src/manageaudioplayer.h @@ -203,6 +203,8 @@ void startedPlayingTrack(const QUrl &fileName, const QDateTime &time); + void currentPlayingForRadiosChanged(const QVariant &value, int role); + public Q_SLOTS: void setCurrentTrack(const QPersistentModelIndex ¤tTrack); @@ -237,6 +239,8 @@ void setPlayerPosition(qint64 playerPosition); + void setCurrentPlayingForRadios(const QString title, const QString nowPlaying); + void setPlayControlPosition(int playerPosition); void setPersistentState(const QVariantMap &persistentStateValue); diff --git a/src/manageaudioplayer.cpp b/src/manageaudioplayer.cpp --- a/src/manageaudioplayer.cpp +++ b/src/manageaudioplayer.cpp @@ -413,6 +413,13 @@ QTimer::singleShot(0, [this]() {Q_EMIT playControlPositionChanged();}); } +void ManageAudioPlayer::setCurrentPlayingForRadios(const QString title, const 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); diff --git a/src/mediaplaylist.h b/src/mediaplaylist.h --- a/src/mediaplaylist.h +++ b/src/mediaplaylist.h @@ -269,6 +269,8 @@ void undoClearPlayList(); + void updateRadioData(const QVariant &value, int role); + private Q_SLOTS: void loadPlayListLoaded(); @@ -294,7 +296,7 @@ void enqueueFilesList(const ElisaUtils::EntryDataList &newEntries); - void enqueueTracksListById(const ElisaUtils::EntryDataList &newEntries); + void enqueueTracksListById(const ElisaUtils::EntryDataList &newEntries, ElisaUtils::PlayListEntryType type); void enqueueOneEntry(const ElisaUtils::EntryData &entryData, ElisaUtils::PlayListEntryType type); @@ -323,10 +325,9 @@ mTrackNumber(trackNumber), mDiscNumber(discNumber), mEntryType(entryType) { } - MediaPlayListEntry(QVariant title, QVariant artist, QVariant album, QVariant trackNumber, - QVariant discNumber, ElisaUtils::PlayListEntryType entryType = ElisaUtils::Unknown) + MediaPlayListEntry(qulonglong id, QVariant title, QVariant artist, QVariant album, QVariant trackNumber, QVariant discNumber, ElisaUtils::PlayListEntryType entryType = ElisaUtils::Unknown) : mTitle(std::move(title)), mAlbum(std::move(album)), mArtist(std::move(artist)), - mTrackNumber(std::move(trackNumber)), mDiscNumber(std::move(discNumber)), mEntryType(entryType) { + mTrackNumber(trackNumber), mDiscNumber(discNumber), mId(id), mEntryType(entryType) { } explicit MediaPlayListEntry(const MusicAudioTrack &track) diff --git a/src/mediaplaylist.cpp b/src/mediaplaylist.cpp --- a/src/mediaplaylist.cpp +++ b/src/mediaplaylist.cpp @@ -220,7 +220,8 @@ return modelModified; } - if (role < ColumnsRoles::IsValidRole || role > ColumnsRoles::IsPlayingRole) { + if ((role != ColumnsRoles::TitleRole && role != ColumnsRoles::ArtistRole) && + (role < ColumnsRoles::IsValidRole || role > ColumnsRoles::IsPlayingRole)) { return modelModified; } @@ -241,6 +242,32 @@ break; } + case ColumnsRoles::TitleRole: + { + modelModified = true; + d->mData[index.row()].mTitle = value; + d->mTrackData[index.row()][static_cast(role)] = value; + Q_EMIT dataChanged(index, index, {role}); + + if (!d->mCurrentTrack.isValid()) { + resetCurrentTrack(); + } + + break; + } + case ColumnsRoles::ArtistRole: + { + modelModified = true; + d->mData[index.row()].mArtist = value; + d->mTrackData[index.row()][static_cast(role)] = value; + Q_EMIT dataChanged(index, index, {role}); + + if (!d->mCurrentTrack.isValid()) { + resetCurrentTrack(); + } + + break; + } default: modelModified = false; } @@ -338,7 +365,9 @@ Q_EMIT persistentStateChanged(); if (!newEntry.mIsValid) { - if (newEntry.mTrackUrl.isValid()) { + if (newEntry.mEntryType == ElisaUtils::Radio) { + Q_EMIT newEntryInList(newEntry.mId, {}, ElisaUtils::Radio); + } else if (newEntry.mTrackUrl.isValid()) { auto entryURL = newEntry.mTrackUrl.toUrl(); if (entryURL.isLocalFile()) { auto entryString = entryURL.toLocalFile(); @@ -425,13 +454,13 @@ Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0), {MediaPlayList::IsPlayingRole}); } -void MediaPlayList::enqueueTracksListById(const ElisaUtils::EntryDataList &newEntries) +void MediaPlayList::enqueueTracksListById(const ElisaUtils::EntryDataList &newEntries, ElisaUtils::PlayListEntryType type) { enqueueCommon(); beginInsertRows(QModelIndex(), d->mData.size(), d->mData.size() + newEntries.size() - 1); for (const auto &newTrack : newEntries) { - auto newMediaPlayListEntry = MediaPlayListEntry{std::get<0>(newTrack), std::get<1>(newTrack), ElisaUtils::Track}; + auto newMediaPlayListEntry = MediaPlayListEntry{std::get<0>(newTrack), std::get<1>(newTrack), type}; d->mData.push_back(newMediaPlayListEntry); d->mTrackData.push_back({}); Q_EMIT newEntryInList(newMediaPlayListEntry.mId, newMediaPlayListEntry.mTitle.toString(), newMediaPlayListEntry.mEntryType); @@ -565,6 +594,14 @@ Q_EMIT undoClearPlayListPlayer(); } +void MediaPlayList::updateRadioData(const QVariant &value, int role) +{ + auto convertedRole = static_cast(role); + if (d->mCurrentTrack.data(convertedRole) != value) { + this->setData(d->mCurrentTrack, value, role); + } +} + void MediaPlayList::enqueueCommon() { displayOrHideUndoInline(false); @@ -633,6 +670,7 @@ case ElisaUtils::Artist: case ElisaUtils::Genre: case ElisaUtils::Track: + case ElisaUtils::Radio: enqueueOneEntry(newEntry, databaseIdType); break; case ElisaUtils::FileName: @@ -674,7 +712,8 @@ switch (databaseIdType) { case ElisaUtils::Track: - enqueueTracksListById(newEntries); + case ElisaUtils::Radio: + enqueueTracksListById(newEntries, databaseIdType); break; case ElisaUtils::FileName: enqueueFilesList(newEntries); @@ -725,6 +764,7 @@ if (oneEntry.mIsValid) { const auto &oneTrack = d->mTrackData[trackIndex]; + oneData.push_back(QString::number(oneTrack.databaseId())); oneData.push_back(oneTrack.title()); oneData.push_back(oneTrack.artist()); if (oneTrack.hasAlbum()) { @@ -800,19 +840,20 @@ for (auto &oneData : persistentState) { auto trackData = oneData.toStringList(); - if (trackData.size() != 6) { + if (trackData.size() != 7) { continue; } - auto restoredTitle = trackData[0]; - auto restoredArtist = trackData[1]; - auto restoredAlbum = trackData[2]; - auto restoredTrackNumber = trackData[3]; - auto restoredDiscNumber = trackData[4]; + auto restoredId = trackData[0].toULongLong(); + auto restoredTitle = trackData[1]; + auto restoredArtist = trackData[2]; + auto restoredAlbum = trackData[3]; + auto restoredTrackNumber = trackData[4]; + auto restoredDiscNumber = trackData[5]; - auto mEntryType = static_cast(trackData[5].toInt()); + auto mEntryType = static_cast(trackData[6].toInt()); - enqueueRestoredEntry({restoredTitle, restoredArtist, restoredAlbum, restoredTrackNumber, restoredDiscNumber, mEntryType}); + enqueueRestoredEntry({restoredId, restoredTitle, restoredArtist, restoredAlbum, restoredTrackNumber, restoredDiscNumber, mEntryType}); } restorePlayListPosition(); @@ -921,6 +962,24 @@ resetCurrentTrack(); } continue; + } else if (oneEntry.mEntryType == ElisaUtils::Radio ) { + if (track.databaseId() != oneEntry.mId) { + continue; + } + + d->mTrackData[i] = track; + oneEntry.mId = track.databaseId(); + oneEntry.mIsValid = true; + + Q_EMIT dataChanged(index(i, 0), index(i, 0), {}); + + restorePlayListPosition(); + + if (!d->mCurrentTrack.isValid()) { + resetCurrentTrack(); + } + + break; } else if (oneEntry.mEntryType != ElisaUtils::Artist && !oneEntry.mIsValid && !oneEntry.mTrackUrl.isValid()) { if (track.find(TrackDataType::key_type::TitleRole) != track.end() && track.title() != oneEntry.mTitle) { diff --git a/src/modeldataloader.h b/src/modeldataloader.h --- a/src/modeldataloader.h +++ b/src/modeldataloader.h @@ -41,6 +41,7 @@ using ListArtistDataType = DatabaseInterface::ListArtistDataType; using ListGenreDataType = DatabaseInterface::ListGenreDataType; using ListTrackDataType = DatabaseInterface::ListTrackDataType; + using ListRadioDataType = DatabaseInterface::ListRadioDataType; using TrackDataType = DatabaseInterface::TrackDataType; using AlbumDataType = DatabaseInterface::AlbumDataType; @@ -62,8 +63,16 @@ 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(ModelDataLoader::ListTrackDataType newData); void trackModified(const ModelDataLoader::TrackDataType &modifiedTrack); @@ -82,6 +91,12 @@ void albumModified(const ModelDataLoader::AlbumDataType &modifiedAlbum); + void saveRadioModified(const ModelDataLoader::TrackDataType trackDataType); + + void removeRadio(qulonglong radioId); + + void radioRemoved(qulonglong radioId); + public Q_SLOTS: void loadData(ElisaUtils::PlayListEntryType dataType); @@ -107,6 +122,10 @@ void loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType); + void updateRadioData(ModelDataLoader::TrackDataType trackDataType); + + void deleteRadioData(qulonglong radioId); + private: void databaseTracksAdded(const ListTrackDataType &newData); @@ -127,6 +146,12 @@ void databaseAlbumModified(const AlbumDataType &modifiedAlbum); + void databaseRadioAdded(const TrackDataType radio); + + void databaseRadioModified(const TrackDataType radio); + + void databaseRadioRemoved(qulonglong radioId); + private: std::unique_ptr d; diff --git a/src/modeldataloader.cpp b/src/modeldataloader.cpp --- a/src/modeldataloader.cpp +++ b/src/modeldataloader.cpp @@ -75,6 +75,16 @@ this, &ModelDataLoader::databaseArtistsAdded); connect(database, &DatabaseInterface::artistRemoved, this, &ModelDataLoader::databaseArtistRemoved); + connect(this, &ModelDataLoader::saveRadioModified, + database, &DatabaseInterface::insertRadio); + connect(this, &ModelDataLoader::removeRadio, + database, &DatabaseInterface::removeRadio); + connect(database, &DatabaseInterface::radioAdded, + this, &ModelDataLoader::databaseRadioAdded); + connect(database, &DatabaseInterface::radioModified, + this, &ModelDataLoader::databaseRadioModified); + connect(database, &DatabaseInterface::radioRemoved, + this, &ModelDataLoader::databaseRadioRemoved); } void ModelDataLoader::loadData(ElisaUtils::PlayListEntryType dataType) @@ -106,6 +116,9 @@ case ElisaUtils::FileName: case ElisaUtils::Unknown: break; + case ElisaUtils::Radio: + Q_EMIT allRadiosData(d->mDatabase->allRadiosData()); + break; } } @@ -135,6 +148,7 @@ break; case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -160,6 +174,7 @@ case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -185,6 +200,7 @@ case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -211,6 +227,7 @@ case ElisaUtils::Track: case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -229,6 +246,9 @@ case ElisaUtils::Track: Q_EMIT allTrackData(d->mDatabase->trackDataFromDatabaseId(databaseId)); break; + case ElisaUtils::Radio: + Q_EMIT allRadioData(d->mDatabase->radioDataFromDatabaseId(databaseId)); + break; case ElisaUtils::Album: case ElisaUtils::Artist: case ElisaUtils::Composer: @@ -263,6 +283,7 @@ case ElisaUtils::Genre: case ElisaUtils::Lyricist: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -287,6 +308,7 @@ case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -311,6 +333,7 @@ case ElisaUtils::Lyricist: case ElisaUtils::FileName: case ElisaUtils::Unknown: + case ElisaUtils::Radio: break; } } @@ -439,5 +462,29 @@ Q_EMIT albumModified(modifiedAlbum); } +void ModelDataLoader::updateRadioData(TrackDataType trackDataType) +{ + Q_EMIT saveRadioModified(trackDataType); +} + +void ModelDataLoader::deleteRadioData(qulonglong radioId) +{ + Q_EMIT removeRadio(radioId); +} + +void ModelDataLoader::databaseRadioAdded(TrackDataType radio) +{ + Q_EMIT radioAdded(radio); +} + +void ModelDataLoader::databaseRadioModified(TrackDataType radio) +{ + Q_EMIT radioModified(radio); +} + +void ModelDataLoader::databaseRadioRemoved(qulonglong radioId) +{ + Q_EMIT radioRemoved(radioId); +} #include "moc_modeldataloader.cpp" diff --git a/src/models/datamodel.h b/src/models/datamodel.h --- a/src/models/datamodel.h +++ b/src/models/datamodel.h @@ -50,6 +50,8 @@ public: + using ListRadioDataType = DatabaseInterface::ListRadioDataType; + using ListTrackDataType = DatabaseInterface::ListTrackDataType; using TrackDataType = DatabaseInterface::TrackDataType; @@ -117,10 +119,14 @@ void tracksAdded(DataModel::ListTrackDataType newData); + void radiosAdded(DataModel::ListRadioDataType newData); + void trackModified(const DataModel::TrackDataType &modifiedTrack); void trackRemoved(qulonglong removedTrackId); + void radioRemoved(qulonglong removedRadioId); + void genresAdded(DataModel::ListGenreDataType newData); void artistsAdded(DataModel::ListArtistDataType newData); @@ -143,7 +149,11 @@ private: - int trackIndexFromId(qulonglong id) const; + void radioAdded(const DataModel::TrackDataType radiosData); + + void radioModified(const DataModel::TrackDataType &modifiedRadio); + + int indexFromId(qulonglong id) const; void connectModel(DatabaseInterface *database); @@ -154,6 +164,8 @@ void askModelData(); + void removeRadios(); + std::unique_ptr d; }; diff --git a/src/models/datamodel.cpp b/src/models/datamodel.cpp --- a/src/models/datamodel.cpp +++ b/src/models/datamodel.cpp @@ -34,6 +34,8 @@ DataModel::ListTrackDataType mAllTrackData; + DataModel::ListRadioDataType mAllRadiosData; + DataModel::ListAlbumDataType mAllAlbumData; DataModel::ListArtistDataType mAllArtistData; @@ -123,14 +125,14 @@ return result; } - const auto dataCount = d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); + const auto dataCount = d->mModelType == ElisaUtils::Radio ? d->mAllRadiosData.size() : d->mAllTrackData.size() + d->mAllAlbumData.size() + d->mAllArtistData.size() + d->mAllGenreData.size(); Q_ASSERT(index.isValid()); Q_ASSERT(index.column() == 0); - Q_ASSERT(index.row() >= 0 && index.row() < dataCount); Q_ASSERT(!index.parent().isValid()); Q_ASSERT(index.model() == this); Q_ASSERT(index.internalId() == 0); + Q_ASSERT(index.row() >= 0 && index.row() < dataCount); switch(role) { @@ -149,6 +151,9 @@ case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][GenreDataType::key_type::TitleRole]; break; + case ElisaUtils::Radio: + result = d->mAllRadiosData[index.row()][GenreDataType::key_type::TitleRole]; + break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: @@ -158,13 +163,46 @@ break; case DatabaseInterface::ColumnsRoles::DurationRole: { - if (d->mModelType == ElisaUtils::Track) { + switch (d->mModelType) + { + case ElisaUtils::Track: + { auto trackDuration = d->mAllTrackData[index.row()][TrackDataType::key_type::DurationRole].toTime(); if (trackDuration.hour() == 0) { result = trackDuration.toString(QStringLiteral("mm:ss")); } else { result = trackDuration.toString(); } + break; + } + case ElisaUtils::Album: + case ElisaUtils::Artist: + case ElisaUtils::Genre: + case ElisaUtils::Lyricist: + case ElisaUtils::Composer: + case ElisaUtils::FileName: + case ElisaUtils::Radio: + case ElisaUtils::Unknown: + break; + } + break; + } + case DatabaseInterface::ColumnsRoles::IsSingleDiscAlbumRole: + { + switch (d->mModelType) + { + case ElisaUtils::Track: + case ElisaUtils::Radio: + result = false; + break; + case ElisaUtils::Album: + case ElisaUtils::Artist: + case ElisaUtils::Genre: + case ElisaUtils::Lyricist: + case ElisaUtils::Composer: + case ElisaUtils::FileName: + case ElisaUtils::Unknown: + break; } break; } @@ -183,6 +221,9 @@ case ElisaUtils::Genre: result = d->mAllGenreData[index.row()][static_cast(role)]; break; + case ElisaUtils::Radio: + result = d->mAllRadiosData[index.row()][static_cast(role)]; + break; case ElisaUtils::Lyricist: case ElisaUtils::Composer: case ElisaUtils::FileName: @@ -343,12 +384,13 @@ } } -int DataModel::trackIndexFromId(qulonglong id) const +int DataModel::indexFromId(qulonglong id) const { int result; + DataModel::ListTrackDataType mAllData = d->mModelType == ElisaUtils::Radio ? d->mAllRadiosData: d->mAllTrackData; - for (result = 0; result < d->mAllTrackData.size(); ++result) { - if (d->mAllTrackData[result].databaseId() == id) { + for (result = 0; result < mAllData.size(); ++result) { + if (mAllData[result].databaseId() == id) { return result; } } @@ -364,6 +406,8 @@ connect(&d->mDataLoader, &ModelDataLoader::allTracksData, this, &DataModel::tracksAdded); + connect(&d->mDataLoader, &ModelDataLoader::allRadiosData, + this, &DataModel::radiosAdded); connect(&d->mDataLoader, &ModelDataLoader::allAlbumsData, this, &DataModel::albumsAdded); connect(&d->mDataLoader, &ModelDataLoader::allArtistsData, @@ -388,6 +432,12 @@ this, &DataModel::artistsAdded); connect(&d->mDataLoader, &ModelDataLoader::artistRemoved, this, &DataModel::artistRemoved); + connect(&d->mDataLoader, &ModelDataLoader::radioAdded, + this, &DataModel::radioAdded); + connect(&d->mDataLoader, &ModelDataLoader::radioModified, + this, &DataModel::radioModified); + connect(&d->mDataLoader, &ModelDataLoader::radioRemoved, + this, &DataModel::radioRemoved); } void DataModel::tracksAdded(ListTrackDataType newData) @@ -402,7 +452,7 @@ if (d->mFilterType == ElisaUtils::FilterById && !d->mAllTrackData.isEmpty()) { for (const auto &newTrack : newData) { - auto trackIndex = trackIndexFromId(newTrack.databaseId()); + auto trackIndex = indexFromId(newTrack.databaseId()); if (trackIndex != -1) { continue; @@ -451,6 +501,67 @@ } } +void DataModel::radiosAdded(ListRadioDataType newData) +{ + if (newData.isEmpty() && d->mModelType == ElisaUtils::Radio) { + setBusy(false); + } + + if (newData.isEmpty() || d->mModelType != ElisaUtils::Radio) { + return; + } + + if (d->mFilterType == ElisaUtils::FilterById && !d->mAllRadiosData.isEmpty()) { + for (const auto &newTrack : newData) { + auto trackIndex = indexFromId(newTrack.databaseId()); + + if (trackIndex != -1) { + continue; + } + + bool trackInserted = false; + for (int trackIndex = 0; trackIndex < d->mAllRadiosData.count(); ++trackIndex) { + const auto &oneTrack = d->mAllRadiosData[trackIndex]; + + if (oneTrack.trackNumber() > newTrack.trackNumber()) { + beginInsertRows({}, trackIndex, trackIndex); + d->mAllRadiosData.insert(trackIndex, newTrack); + endInsertRows(); + + if (d->mAllRadiosData.size() == 1) { + setBusy(false); + } + + trackInserted = true; + break; + } + } + + if (!trackInserted) { + beginInsertRows({}, d->mAllRadiosData.count(), d->mAllRadiosData.count()); + d->mAllRadiosData.insert(d->mAllRadiosData.count(), newTrack); + endInsertRows(); + + if (d->mAllRadiosData.size() == 1) { + setBusy(false); + } + } + } + } else { + if (d->mAllRadiosData.isEmpty()) { + beginInsertRows({}, 0, newData.size() - 1); + d->mAllRadiosData.swap(newData); + endInsertRows(); + + setBusy(false); + } else { + beginInsertRows({}, d->mAllRadiosData.size(), d->mAllRadiosData.size() + newData.size() - 1); + d->mAllRadiosData.append(newData); + endInsertRows(); + } + } +} + void DataModel::trackModified(const TrackDataType &modifiedTrack) { if (d->mModelType != ElisaUtils::Track) { @@ -462,7 +573,7 @@ return; } - auto trackIndex = trackIndexFromId(modifiedTrack.databaseId()); + auto trackIndex = indexFromId(modifiedTrack.databaseId()); if (trackIndex == -1) { return; @@ -488,14 +599,30 @@ } } +void DataModel::radioModified(const TrackDataType &modifiedRadio) +{ + if (d->mModelType != ElisaUtils::Radio) { + return; + } + + auto trackIndex = indexFromId(modifiedRadio.databaseId()); + + if (trackIndex == -1) { + return; + } + + d->mAllRadiosData[trackIndex] = modifiedRadio; + Q_EMIT dataChanged(index(trackIndex, 0), index(trackIndex, 0)); +} + void DataModel::trackRemoved(qulonglong removedTrackId) { if (d->mModelType != ElisaUtils::Track) { return; } if (!d->mAlbumTitle.isEmpty() && !d->mAlbumArtist.isEmpty()) { - auto trackIndex = trackIndexFromId(removedTrackId); + auto trackIndex = indexFromId(removedTrackId); if (trackIndex == -1) { return; @@ -520,6 +647,49 @@ } } +void DataModel::radioRemoved(qulonglong removedRadioId) +{ + if (d->mModelType != ElisaUtils::Radio) { + return; + } + + + auto itRadio = std::find_if(d->mAllRadiosData.begin(), d->mAllRadiosData.end(), + [removedRadioId](auto track) {return track.databaseId() == removedRadioId;}); + + if (itRadio == d->mAllRadiosData.end()) { + return; + } + + auto position = itRadio - d->mAllRadiosData.begin(); + + beginRemoveRows({}, position, position); + d->mAllRadiosData.erase(itRadio); + endRemoveRows(); +} + +void DataModel::radioAdded(const DataModel::TrackDataType radioData) +{ + if (d->mModelType != ElisaUtils::Radio) { + return; + } + + ListRadioDataType list; + list.append(radioData); + radiosAdded(list); +} + +void DataModel::removeRadios() +{ + if (d->mModelType != ElisaUtils::Radio) { + return; + } + + beginRemoveRows({}, 0, d->mAllRadiosData.size()); + d->mAllRadiosData.clear(); + endRemoveRows(); +} + void DataModel::genresAdded(DataModel::ListGenreDataType newData) { if (newData.isEmpty() && d->mModelType == ElisaUtils::Genre) { diff --git a/src/models/trackmetadatamodel.h b/src/models/trackmetadatamodel.h --- a/src/models/trackmetadatamodel.h +++ b/src/models/trackmetadatamodel.h @@ -53,6 +53,10 @@ READ lyrics NOTIFY lyricsChanged) + Q_PROPERTY(bool isRadio + READ isRadio + WRITE setIsRadio) + public: enum ColumnRoles @@ -95,6 +99,8 @@ QString lyrics() const; + bool isRadio(); + Q_SIGNALS: void needDataByDatabaseId(ElisaUtils::PlayListEntryType dataType, qulonglong databaseId); @@ -109,22 +115,50 @@ void lyricsChanged(); + void saveRadioData(DatabaseInterface::TrackDataType trackDataType); + + void deleteRadioData(qulonglong radioId); + + void disableApplyButton(); + + void hideDeleteButton(); + + void showDeleteButton(); + + void closeWindow(); + public Q_SLOTS: void trackData(const TrackMetadataModel::TrackDataType &trackData); void initializeByTrackId(qulonglong databaseId); void initializeByTrackFileName(const QString &fileName); + void initializeForNewRadio(); + void setManager(MusicListenersManager *newManager); + void setIsRadio(bool isRadio); + void setDatabase(DatabaseInterface *trackDatabase); + void saveData(); + + void deleteRadio(); + + void radioAdded(TrackMetadataModel::TrackDataType radiosData); + + void radioModified(); + + void radioRemoved(); + protected: void fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData); + void fillDataForNewRadio(); + virtual void filterDataFromTrackData(); void removeMetaData(DatabaseInterface::ColumnsRoles metaData); @@ -144,6 +178,10 @@ void fetchLyrics(); + QVariant dataGeneral(const QModelIndex &index, int role) const; + + QVariant dataRadio(const QModelIndex &index, int role) const; + TrackDataType mFullData; TrackDataType mTrackData; @@ -158,12 +196,13 @@ MusicListenersManager *mManager = nullptr; + bool mIsRadio; + FileScanner mFileScanner; QMimeDatabase mMimeDatabase; QFutureWatcher mLyricsValueWatcher; - }; #endif // TRACKMETADATAMODEL_H diff --git a/src/models/trackmetadatamodel.cpp b/src/models/trackmetadatamodel.cpp --- a/src/models/trackmetadatamodel.cpp +++ b/src/models/trackmetadatamodel.cpp @@ -23,6 +23,16 @@ #include +const QList mFieldsForClassicTrack({DatabaseInterface::TitleRole, DatabaseInterface::ArtistRole, DatabaseInterface::AlbumRole, + DatabaseInterface::AlbumArtistRole, DatabaseInterface::TrackNumberRole, DatabaseInterface::DiscNumberRole, + DatabaseInterface::RatingRole, DatabaseInterface::GenreRole, DatabaseInterface::LyricistRole, + DatabaseInterface::ComposerRole, DatabaseInterface::CommentRole, DatabaseInterface::YearRole, + DatabaseInterface::LastPlayDate, DatabaseInterface::PlayCounter}); + +const QList mFieldsForRadioTrack({DatabaseInterface::TitleRole,DatabaseInterface::ResourceRole, DatabaseInterface::CommentRole, DatabaseInterface::DatabaseIdRole, + DatabaseInterface::ArtistRole, DatabaseInterface::AlbumRole}); + + TrackMetadataModel::TrackMetadataModel(QObject *parent) : QAbstractListModel(parent) { @@ -46,7 +56,7 @@ return mTrackData.count(); } -QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const +QVariant TrackMetadataModel::dataGeneral(const QModelIndex &index, int role) const { auto result = QVariant{}; @@ -162,6 +172,9 @@ case DatabaseInterface::LyricsRole: result = i18nc("Lyrics label for track metadata view", "Lyrics"); break; + case DatabaseInterface::ResourceRole: + result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address"); + break; case DatabaseInterface::SecondaryTextRole: case DatabaseInterface::ImageUrlRole: case DatabaseInterface::ShadowForImageRole: @@ -171,7 +184,6 @@ case DatabaseInterface::IsValidAlbumArtistRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: - case DatabaseInterface::ResourceRole: case DatabaseInterface::IdRole: case DatabaseInterface::DatabaseIdRole: case DatabaseInterface::IsSingleDiscAlbumRole: @@ -192,6 +204,9 @@ case DatabaseInterface::TitleRole: result = TextEntry; break; + case DatabaseInterface::ResourceRole: + result = TextEntry; + break; case DatabaseInterface::ArtistRole: result = TextEntry; break; @@ -247,7 +262,124 @@ case DatabaseInterface::IsValidAlbumArtistRole: case DatabaseInterface::AllArtistsRole: case DatabaseInterface::HighestTrackRating: + case DatabaseInterface::IdRole: + case DatabaseInterface::DatabaseIdRole: + case DatabaseInterface::IsSingleDiscAlbumRole: + case DatabaseInterface::ContainerDataRole: + case DatabaseInterface::IsPartialDataRole: + case DatabaseInterface::AlbumIdRole: + case DatabaseInterface::HasEmbeddedCover: + case DatabaseInterface::FileModificationTime: + case DatabaseInterface::FirstPlayDate: + case DatabaseInterface::PlayFrequency: + case DatabaseInterface::ElementTypeRole: + break; + } + break; + } + + return result; +} + +QVariant TrackMetadataModel::dataRadio(const QModelIndex &index, int role) const +{ + auto result = QVariant{}; + + const auto currentKey = mTrackKeys[index.row()]; + + switch (role) + { + case Qt::DisplayRole: + result = mTrackData[currentKey]; + break; + case ItemNameRole: + switch (currentKey) + { + case DatabaseInterface::TitleRole: + result = i18nc("Track title for track metadata view", "Title"); + break; + case DatabaseInterface::CommentRole: + result = i18nc("Comment label for track metadata view", "Comment"); + break; + case DatabaseInterface::ResourceRole: + result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address"); + break; + case DatabaseInterface::ChannelsRole: + case DatabaseInterface::BitRateRole: + case DatabaseInterface::SampleRateRole: + case DatabaseInterface::LastPlayDate: + case DatabaseInterface::PlayCounter: + case DatabaseInterface::LyricsRole: + case DatabaseInterface::YearRole: + case DatabaseInterface::ComposerRole: + case DatabaseInterface::ArtistRole: + case DatabaseInterface::AlbumRole: + case DatabaseInterface::AlbumArtistRole: + case DatabaseInterface::TrackNumberRole: + case DatabaseInterface::DiscNumberRole: + case DatabaseInterface::RatingRole: + case DatabaseInterface::GenreRole: + case DatabaseInterface::LyricistRole: + case DatabaseInterface::DurationRole: + case DatabaseInterface::SecondaryTextRole: + case DatabaseInterface::ImageUrlRole: + case DatabaseInterface::ShadowForImageRole: + case DatabaseInterface::ChildModelRole: + case DatabaseInterface::StringDurationRole: + case DatabaseInterface::MilliSecondsDurationRole: + case DatabaseInterface::AllArtistsRole: + case DatabaseInterface::HighestTrackRating: + case DatabaseInterface::IdRole: + case DatabaseInterface::DatabaseIdRole: + case DatabaseInterface::IsSingleDiscAlbumRole: + case DatabaseInterface::ContainerDataRole: + case DatabaseInterface::IsPartialDataRole: + case DatabaseInterface::AlbumIdRole: + case DatabaseInterface::HasEmbeddedCover: + case DatabaseInterface::FileModificationTime: + case DatabaseInterface::FirstPlayDate: + case DatabaseInterface::PlayFrequency: + case DatabaseInterface::ElementTypeRole: + break; + } + break; + case ItemTypeRole: + switch (currentKey) + { + case DatabaseInterface::TitleRole: + result = TextEntry; + break; case DatabaseInterface::ResourceRole: + result = TextEntry; + break; + case DatabaseInterface::CommentRole: + result = TextEntry; + break; + case DatabaseInterface::ArtistRole: + case DatabaseInterface::AlbumRole: + case DatabaseInterface::AlbumArtistRole: + case DatabaseInterface::TrackNumberRole: + case DatabaseInterface::DiscNumberRole: + case DatabaseInterface::RatingRole: + case DatabaseInterface::GenreRole: + case DatabaseInterface::LyricistRole: + case DatabaseInterface::ComposerRole: + case DatabaseInterface::YearRole: + case DatabaseInterface::LastPlayDate: + case DatabaseInterface::PlayCounter: + case DatabaseInterface::LyricsRole: + case DatabaseInterface::DurationRole: + case DatabaseInterface::SampleRateRole: + case DatabaseInterface::BitRateRole: + case DatabaseInterface::ChannelsRole: + case DatabaseInterface::SecondaryTextRole: + case DatabaseInterface::ImageUrlRole: + case DatabaseInterface::ShadowForImageRole: + case DatabaseInterface::ChildModelRole: + case DatabaseInterface::StringDurationRole: + case DatabaseInterface::MilliSecondsDurationRole: + case DatabaseInterface::AllArtistsRole: + case DatabaseInterface::HighestTrackRating: case DatabaseInterface::IdRole: case DatabaseInterface::DatabaseIdRole: case DatabaseInterface::IsSingleDiscAlbumRole: @@ -267,10 +399,20 @@ return result; } +QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const +{ + if (this->mIsRadio) { + return dataRadio(index, role); + } else { + return dataGeneral(index, role); + } +} + bool TrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { - // FIXME: Implement me! + mTrackData[mTrackKeys[index.row()]] = value; + emit dataChanged(index, index, QVector() << role); return true; } @@ -327,11 +469,7 @@ mTrackData.clear(); mTrackKeys.clear(); - for (auto role : {DatabaseInterface::TitleRole, DatabaseInterface::ArtistRole, DatabaseInterface::AlbumRole, - DatabaseInterface::AlbumArtistRole, DatabaseInterface::TrackNumberRole, DatabaseInterface::DiscNumberRole, - DatabaseInterface::RatingRole, DatabaseInterface::GenreRole, DatabaseInterface::LyricistRole, - DatabaseInterface::ComposerRole, DatabaseInterface::CommentRole, DatabaseInterface::YearRole, - DatabaseInterface::LastPlayDate, DatabaseInterface::PlayCounter}) { + for (DatabaseInterface::ColumnsRoles role : (isRadio() ? mFieldsForRadioTrack : mFieldsForClassicTrack)){ if (trackData.constFind(role) != trackData.constEnd()) { if (role == DatabaseInterface::RatingRole) { if (trackData[role].toInt() == 0) { @@ -419,10 +557,26 @@ &mDataLoader, &ModelDataLoader::loadDataByDatabaseId); connect(this, &TrackMetadataModel::needDataByFileName, &mDataLoader, &ModelDataLoader::loadDataByFileName); - connect(&mDataLoader, &ModelDataLoader::allTrackData, - this, &TrackMetadataModel::trackData); - connect(&mDataLoader, &ModelDataLoader::trackModified, - this, &TrackMetadataModel::trackData); + + if (isRadio()) { + connect(this, &TrackMetadataModel::saveRadioData, + &mDataLoader, &ModelDataLoader::updateRadioData); + connect(this, &TrackMetadataModel::deleteRadioData, + &mDataLoader, &ModelDataLoader::deleteRadioData); + connect(&mDataLoader, &ModelDataLoader::radioAdded, + this, &TrackMetadataModel::radioAdded); + connect(&mDataLoader, &ModelDataLoader::radioModified, + this, &TrackMetadataModel::radioModified); + connect(&mDataLoader, &ModelDataLoader::radioRemoved, + this, &TrackMetadataModel::radioRemoved); + connect(&mDataLoader, &ModelDataLoader::allRadioData, + this, &TrackMetadataModel::trackData); + } else { + connect(&mDataLoader, &ModelDataLoader::trackModified, + this, &TrackMetadataModel::trackData); + connect(&mDataLoader, &ModelDataLoader::allTrackData, + this, &TrackMetadataModel::trackData); + } } void TrackMetadataModel::fetchLyrics() @@ -447,7 +601,41 @@ Q_EMIT lyricsChanged(); - Q_EMIT needDataByDatabaseId(ElisaUtils::Track, databaseId); + Q_EMIT needDataByDatabaseId((isRadio() ? ElisaUtils::Radio : ElisaUtils::Track), databaseId); +} + +void TrackMetadataModel::initializeForNewRadio() +{ + mFullData.clear(); + mTrackData.clear(); + + fillDataForNewRadio(); +} + +void TrackMetadataModel::fillDataForNewRadio() +{ + beginResetModel(); + mTrackData.clear(); + mTrackKeys.clear(); + + for (auto role : { + DatabaseInterface::TitleRole, + DatabaseInterface::ResourceRole, + DatabaseInterface::CommentRole, + DatabaseInterface::DatabaseIdRole + + }) { + mTrackKeys.push_back(role); + if (role == DatabaseInterface::DatabaseIdRole) { + mTrackData[role] = -1; + Q_EMIT hideDeleteButton(); + } else { + mTrackData[role] = QStringLiteral(""); + } + + } + filterDataFromTrackData(); + endResetModel(); } void TrackMetadataModel::initializeByTrackFileName(const QString &fileName) @@ -467,10 +655,43 @@ initialize(newManager, nullptr); } +void TrackMetadataModel::setIsRadio(bool isRadio){ + this->mIsRadio = isRadio; +} + void TrackMetadataModel::setDatabase(DatabaseInterface *trackDatabase) { initialize(nullptr, trackDatabase); } +bool TrackMetadataModel::isRadio(){ + return this->mIsRadio; +} + +void TrackMetadataModel::saveData() +{ + Q_EMIT saveRadioData(mTrackData); +} + +void TrackMetadataModel::deleteRadio() +{ + if (mTrackData[DatabaseInterface::DatabaseIdRole]>=0) { + Q_EMIT deleteRadioData(mTrackData[DatabaseInterface::DatabaseIdRole].toULongLong()); + } +} + +void TrackMetadataModel::radioAdded(TrackDataType radiosData){ + mTrackData[DatabaseInterface::DatabaseIdRole] = radiosData[DatabaseInterface::DatabaseIdRole]; + Q_EMIT showDeleteButton(); + radioModified(); +} + +void TrackMetadataModel::radioModified(){ + Q_EMIT disableApplyButton(); +} + +void TrackMetadataModel::radioRemoved(){ + Q_EMIT closeWindow(); +} #include "moc_trackmetadatamodel.cpp" diff --git a/src/models/viewsmodel.cpp b/src/models/viewsmodel.cpp --- a/src/models/viewsmodel.cpp +++ b/src/models/viewsmodel.cpp @@ -39,25 +39,28 @@ mTypes = {ViewManager::Context, ViewManager::RecentlyPlayedTracks, ViewManager::FrequentlyPlayedTracks, ViewManager::AllAlbums, ViewManager::AllArtists, ViewManager::AllTracks, - ViewManager::AllGenres, ViewManager::FilesBrowser}; + ViewManager::AllGenres, ViewManager::FilesBrowser, + ViewManager::RadiosBrowser}; mNames = {{ViewManager::Context, {i18nc("Title of the view of the playlist", "Now Playing")}}, {ViewManager::RecentlyPlayedTracks, {i18nc("Title of the view of recently played tracks", "Recently Played")}}, {ViewManager::FrequentlyPlayedTracks, {i18nc("Title of the view of frequently played tracks", "Frequently Played")}}, {ViewManager::AllAlbums, {i18nc("Title of the view of all albums", "Albums")}}, {ViewManager::AllArtists, {i18nc("Title of the view of all artists", "Artists")}}, {ViewManager::AllTracks, {i18nc("Title of the view of all tracks", "Tracks")}}, {ViewManager::AllGenres, {i18nc("Title of the view of all genres", "Genres")}}, - {ViewManager::FilesBrowser, {i18nc("Title of the file browser view", "Files")}}}; + {ViewManager::FilesBrowser, {i18nc("Title of the file browser view", "Files")}}, + {ViewManager::RadiosBrowser, {i18nc("Title of the file radios browser view", "Radios")}}}; mIcons = {{ViewManager::Context, QUrl{QStringLiteral("image://icon/view-media-lyrics")}}, {ViewManager::RecentlyPlayedTracks, QUrl{QStringLiteral("image://icon/media-playlist-play")}}, {ViewManager::FrequentlyPlayedTracks, QUrl{QStringLiteral("image://icon/view-media-playcount")}}, {ViewManager::AllAlbums, QUrl{QStringLiteral("image://icon/view-media-album-cover")}}, {ViewManager::AllArtists, QUrl{QStringLiteral("image://icon/view-media-artist")}}, {ViewManager::AllTracks, QUrl{QStringLiteral("image://icon/view-media-track")}}, {ViewManager::AllGenres, QUrl{QStringLiteral("image://icon/view-media-genre")}}, - {ViewManager::FilesBrowser, QUrl{QStringLiteral("image://icon/document-open-folder")}}}; + {ViewManager::FilesBrowser, QUrl{QStringLiteral("image://icon/document-open-folder")}}, + {ViewManager::RadiosBrowser, QUrl{QStringLiteral("image://icon/radio")}}}; } }; @@ -167,6 +170,8 @@ return 6; case ViewManager::FilesBrowser: return 7; + case ViewManager::RadiosBrowser: + return 8; case ViewManager::OneAlbum: case ViewManager::OneArtist: case ViewManager::OneAlbumFromArtist: diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -107,6 +107,7 @@ displaySingleAlbum: displaySingleAlbum, showSection: showDiscHeaders, opacity: 0, + radioCase: radioCase }) } @@ -140,7 +141,6 @@ }) } - onPopOneView: { if (browseStackView.depth > 2) { browseStackView.pop() } @@ -363,7 +363,7 @@ } Component { - id: albumContext + id: albumContext ContextView { StackView.onActivated: viewManager.viewIsLoaded(viewType) diff --git a/src/qml/DataListView.qml b/src/qml/DataListView.qml --- a/src/qml/DataListView.qml +++ b/src/qml/DataListView.qml @@ -36,6 +36,12 @@ property alias sortRole: proxyModel.sortRole property var sortAscending property bool displaySingleAlbum: false + property alias radioCase: listView.showCreateRadioButton + + function openMetaDataView(databaseId){ + metadataLoader.setSource("MediaTrackMetadataView.qml", {"databaseId": databaseId, "isRadio": viewHeader.radioCase}); + metadataLoader.active = true + } DataModel { id: realModel @@ -49,6 +55,12 @@ onEntriesToEnqueue: elisa.mediaPlayList.enqueue(newEntries, databaseIdType, enqueueMode, triggerPlay) } + Loader { + id: metadataLoader + active: false + onLoaded: item.show() + } + Component { id: singleAlbumDelegate @@ -65,7 +77,7 @@ artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' - duration: model.duration + duration: model.duration ? model.duration : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 @@ -91,6 +103,10 @@ listView.currentIndex = index } } + + onCallOpenMetaDataView: { + openMetaDataView(databaseId) + } } } @@ -110,7 +126,7 @@ artist: model.artist ? model.artist : '' album: model.album ? model.album : '' albumArtist: model.albumArtist ? model.albumArtist : '' - duration: model.duration + duration: model.duration ? model.duration : '' imageUrl: model.imageUrl ? model.imageUrl : '' trackNumber: model.trackNumber ? model.trackNumber : -1 discNumber: model.discNumber ? model.discNumber : -1 @@ -131,6 +147,10 @@ listView.currentIndex = index entry.forceActiveFocus() } + + onCallOpenMetaDataView: { + openMetaDataView(databaseId) + } } } @@ -177,6 +197,14 @@ modelType) } + Connections { + target: listView.navigationBar + + onCreateRadio: { + openMetaDataView(-1) + } + } + Component.onCompleted: { if (elisa.musicManager) { realModel.initialize(elisa.musicManager, elisa.musicManager.viewDatabase, modelType, filterType, mainTitle, secondaryTitle, databaseId) diff --git a/src/qml/EditableMetaDataDelegate.qml b/src/qml/EditableMetaDataDelegate.qml new file mode 100644 --- /dev/null +++ b/src/qml/EditableMetaDataDelegate.qml @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.2 + +import org.kde.elisa 1.0 + +RowLayout { + id: delegateRow + spacing: 0 + + signal radioEdited() + + TextMetrics { + id: metaDataLabelMetric + + text: 'Metadata Name' + } + + Label { + id: metaDataLabels + + text: { + if (model.name !== undefined) { + return i18nc("Label for a piece of metadata, e.g. 'Album Artist:'", "%1:", model.name) + } + return "" + } + + font.weight: Font.Bold + + horizontalAlignment: Text.AlignRight + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 0.8 * elisaTheme.coverImageSize + Layout.rightMargin: !LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 + Layout.leftMargin: LayoutMirroring.enabled ? elisaTheme.layoutHorizontalMargin / 2 : 0 + } + + Loader { + id: textDisplayLoader + + active: model.type === TrackMetadataModel.TextEntry || model.type === TrackMetadataModel.IntegerEntry + visible: model.type === TrackMetadataModel.TextEntry || model.type === TrackMetadataModel.IntegerEntry + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + sourceComponent: TextField { + text: model.display + + horizontalAlignment: Text.AlignLeft + + anchors.fill: parent + + onEditingFinished: { + if (model.display !== text) { + model.display = text + delegateRow.radioEdited() + } + } + } + } +} diff --git a/src/qml/ListBrowserDelegate.qml b/src/qml/ListBrowserDelegate.qml --- a/src/qml/ListBrowserDelegate.qml +++ b/src/qml/ListBrowserDelegate.qml @@ -44,6 +44,7 @@ signal clicked() signal enqueue(var databaseId, var name) signal replaceAndPlay(var databaseId, var name) + signal callOpenMetaDataView(var databaseId) Accessible.role: Accessible.ListItem Accessible.name: title @@ -61,13 +62,7 @@ text: i18nc("Show track metadata", "View Details") icon.name: "help-about" onTriggered: { - if (metadataLoader.active === false) { - metadataLoader.active = true - } - else { - metadataLoader.item.close(); - metadataLoader.active = false - } + callOpenMetaDataView(databaseId) } } @@ -81,17 +76,6 @@ Keys.onReturnPressed: enqueue(databaseId, title) Keys.onEnterPressed: enqueue(databaseId, title) - Loader { - id: metadataLoader - active: false - onLoaded: item.show() - - sourceComponent: MediaTrackMetadataView { - databaseId: mediaTrack.databaseId - onRejected: metadataLoader.active = false; - } - } - Rectangle { id: rowRoot @@ -219,7 +203,7 @@ id: mainLabelDetailed text: { - if (trackNumber !== 0) { + if (trackNumber >= 0) { return i18nc("%1: track number. %2: track title", "%1 - %2", trackNumber.toLocaleString(Qt.locale(), 'f', 0), title); } else { @@ -255,7 +239,7 @@ } if (album !== '') { labelText += ' - ' + album - if (!isSingleDiscAlbum) { + if (!isSingleDiscAlbum && discNumber !== -1) { labelText += ' - CD ' + discNumber } } diff --git a/src/qml/ListBrowserView.qml b/src/qml/ListBrowserView.qml --- a/src/qml/ListBrowserView.qml +++ b/src/qml/ListBrowserView.qml @@ -42,6 +42,9 @@ property alias currentIndex: contentDirectoryView.currentIndex property alias enableSorting: navigationBar.enableSorting property var stackView + property bool showEnqueueButton: true + property bool showCreateRadioButton: false + property alias navigationBar: navigationBar signal goBack() signal showArtist(var name) @@ -67,6 +70,9 @@ Layout.fillWidth: true + showEnqueueButton: listView.showEnqueueButton + showCreateRadioButton: listView.showCreateRadioButton + Binding { target: contentModel property: 'filterText' diff --git a/src/qml/MediaTrackMetadataView.qml b/src/qml/MediaTrackMetadataView.qml --- a/src/qml/MediaTrackMetadataView.qml +++ b/src/qml/MediaTrackMetadataView.qml @@ -29,13 +29,20 @@ property int databaseId: 0 property string fileName + property alias isRadio: realModel.isRadio signal rejected() LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true - title: i18nc("Window title for track metadata", "View Details") + title: { + if (trackMetadata.isRadio && databaseId === -1) { + return i18nc("Window title for track metadata", "Create a Radio") + } + + return i18nc("Window title for track metadata", "View Details") + } TrackMetadataModel { id: realModel @@ -80,6 +87,8 @@ Layout.minimumWidth: elisaTheme.coverImageSize Layout.maximumHeight: elisaTheme.coverImageSize Layout.maximumWidth: elisaTheme.coverImageSize + + visible: !trackMetadata.isRadio } ListView { @@ -104,9 +113,24 @@ } model: realModel - delegate: MetaDataDelegate { - width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width + Component { + id: metaDataDelegate + + MetaDataDelegate { + width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width + } + } + + Component { + id: editableMetaDataDelegate + + EditableMetaDataDelegate { + width: scrollBar.visible ? (!LayoutMirroring.enabled ? trackData.width - scrollBar.width : trackData.width) : trackData.width + onRadioEdited: applyButton.enabled = true + } } + + delegate: trackMetadata.isRadio ? editableMetaDataDelegate: metaDataDelegate } } @@ -136,35 +160,92 @@ elide: Text.ElideRight } + + visible: !trackMetadata.isRadio + } + + RowLayout{ + spacing: elisaTheme.layoutVerticalMargin + + DialogButtonBox { + Layout.minimumHeight: implicitHeight + alignment: Qt.AlignLeft + + Button { + id: deleteButton + text: qsTr("Delete") + DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole + onClicked: realModel.deleteRadio() + } + } + + DialogButtonBox { + id: buttons + + Layout.fillWidth: true + Layout.minimumHeight: implicitHeight + alignment: Qt.AlignRight + + Button { + id: applyButton + + text: qsTr("Apply") + DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole + onClicked: realModel.saveData() + enabled: false + } + Button { + text: qsTr("Close") + DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole + onClicked: trackMetadata.close() + } + } + + visible: trackMetadata.isRadio } DialogButtonBox { - id: buttons + id: buttonsTrack Layout.fillWidth: true Layout.minimumHeight: implicitHeight standardButtons: DialogButtonBox.Close alignment: Qt.AlignRight - onRejected: trackMetadata.rejected() + onRejected: trackMetadata.close() + + visible: !trackMetadata.isRadio } } Connections { target: elisa onMusicManagerChanged: { - if (databaseId !== 0) { + if (databaseId === -1) { + realModel.initializeForNewRadio() + } else if (databaseId !== 0) { realModel.initializeByTrackId(databaseId) } else { realModel.initializeByTrackFileName(fileName) } } } + Connections{ + target: realModel + + onDisableApplyButton: applyButton.enabled = false + onShowDeleteButton: deleteButton.visible = true + onHideDeleteButton: deleteButton.visible = false + onCloseWindow: trackMetadata.close() + } + Component.onCompleted: { if (elisa.musicManager) { - if (databaseId !== 0) { + if (databaseId === -1) { + realModel.initializeForNewRadio() + } else if (databaseId !== 0) { realModel.initializeByTrackId(databaseId) } else { realModel.initializeByTrackFileName(fileName) diff --git a/src/qml/MetaDataDelegate.qml b/src/qml/MetaDataDelegate.qml --- a/src/qml/MetaDataDelegate.qml +++ b/src/qml/MetaDataDelegate.qml @@ -27,7 +27,6 @@ height: (model.type === TrackMetadataModel.LongTextEntry ? longTextDisplayLoader.height : (metaDataLabelMetric.boundingRect.height + elisaTheme.layoutVerticalMargin / 2)) - TextMetrics { id: metaDataLabelMetric @@ -58,6 +57,7 @@ Layout.fillWidth: true Layout.alignment: Qt.AlignTop + sourceComponent: LabelWithToolTip { text: model.display diff --git a/src/qml/NavigationActionBar.qml b/src/qml/NavigationActionBar.qml --- a/src/qml/NavigationActionBar.qml +++ b/src/qml/NavigationActionBar.qml @@ -30,6 +30,8 @@ property string secondaryTitle property url image property bool allowArtistNavigation: false + property bool showEnqueueButton: true + property bool showCreateRadioButton property string labelText property bool showRating: true @@ -43,6 +45,7 @@ signal enqueue(); signal replaceAndPlay(); + signal createRadio(); signal goBack(); signal showArtist(); signal sort(var order); @@ -69,6 +72,13 @@ onTriggered: enqueue() } + Action { + id: createRadioAction + text: i18nc("Create a new radio", "Create a radio") + icon.name: "media-track-add-amarok" + onTriggered: createRadio() + } + Action { id: showFilterAction text: !navigationBar.expandedFilterView ? i18nc("Show filters in the navigation bar", "Show Search Options") : i18nc("Hide filters in the navigation bar", "Hide Search Options") @@ -160,46 +170,63 @@ visible: secondaryTitle !== "" } }, + FlatButtonWithToolTip { + action: createRadioAction + objectName: 'createRadioButton' + + icon.height: elisaTheme.smallControlButtonSize + icon.width: elisaTheme.smallControlButtonSize + + focus: true + + visible: showCreateRadioButton + }, FlatButtonWithToolTip { action: enqueueAction objectName: 'enqueueButton' icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize focus: true + + visible: !showCreateRadioButton }, FlatButtonWithToolTip { action: replaceAndPlayAction objectName: 'replaceAndPlayButton' icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize + + visible: !showCreateRadioButton }, FlatButtonWithToolTip { action: showArtistAction objectName: 'showArtistButton' icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize - visible: allowArtistNavigation + visible: allowArtistNavigation && !showCreateRadioButton }, FlatButtonWithToolTip { action: sortAction objectName: 'sortAscendingButton' icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize - visible: enableSorting + visible: enableSorting && !showCreateRadioButton }, FlatButtonWithToolTip { action: showFilterAction objectName: 'showFilterButton' icon.height: elisaTheme.smallControlButtonSize icon.width: elisaTheme.smallControlButtonSize + + visible: !showCreateRadioButton } ] } diff --git a/src/resources.qrc b/src/resources.qrc --- a/src/resources.qrc +++ b/src/resources.qrc @@ -34,6 +34,7 @@ qml/PlayListAlbumHeader.qml qml/BasicPlayListAlbumHeader.qml qml/MetaDataDelegate.qml + qml/EditableMetaDataDelegate.qml qml/ViewSelectorDelegate.qml qml/HeaderFooterToolbar.qml qml/TracksDiscHeader.qml diff --git a/src/trackslistener.cpp b/src/trackslistener.cpp --- a/src/trackslistener.cpp +++ b/src/trackslistener.cpp @@ -34,6 +34,8 @@ QSet mTracksByIdSet; + QSet mRadiosByIdSet; + QList> mTracksByNameSet; QList mTracksByFileNameSet; @@ -220,6 +222,16 @@ } 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; diff --git a/src/viewmanager.h b/src/viewmanager.h --- a/src/viewmanager.h +++ b/src/viewmanager.h @@ -44,7 +44,8 @@ FrequentlyPlayedTracks, RecentlyPlayedTracks, FilesBrowser, - Context + Context, + RadiosBrowser }; Q_ENUM(ViewsType) @@ -67,6 +68,13 @@ Q_ENUM(AlbumViewStyle) + enum RadioSpecificStyle { + IsTrack, + IsRadio, + }; + + Q_ENUM(RadioSpecificStyle) + explicit ViewManager(QObject *parent = nullptr); Q_SIGNALS: @@ -80,7 +88,8 @@ void openListView(ViewManager::ViewsType viewType, ElisaUtils::FilterType filterType, int expectedDepth, const QString &mainTitle, const QString &secondaryTitle, qulonglong databaseId, const QUrl &imageUrl, ElisaUtils::PlayListEntryType dataType, QVariant sortRole, - ViewManager::SortOrder sortOrder, bool displaySingleAlbum, ViewManager::AlbumViewStyle showDiscHeaders); + ViewManager::SortOrder sortOrder, bool displaySingleAlbum, ViewManager::AlbumViewStyle showDiscHeaders, + ViewManager::RadioSpecificStyle radioCase); void switchFilesBrowserView(ViewManager::ViewsType viewType, int expectedDepth, const QString &mainTitle, const QUrl &imageUrl); @@ -122,6 +131,8 @@ void openContextView(const QString &mainTitle, const QUrl &imageUrl); + void openRadiosBrowser(const QString &mainTitle, const QUrl &imageUrl); + void openOneAlbum(const QString &albumTitle, const QString &albumAuthor, const QUrl &albumCover, qulonglong albumDatabaseId, ViewManager::AlbumViewStyle albumDiscHeader); @@ -151,6 +162,8 @@ void contextViewIsLoaded(); + void radiosBrowserViewIsLoaded(); + QString mCurrentAlbumTitle; QString mCurrentAlbumAuthor; QString mCurrentArtistName; diff --git a/src/viewmanager.cpp b/src/viewmanager.cpp --- a/src/viewmanager.cpp +++ b/src/viewmanager.cpp @@ -51,6 +51,9 @@ case Context: openContextView(mainTitle, mainImage); break; + case RadiosBrowser: + openRadiosBrowser(mainTitle, mainImage); + break; case OneAlbum: case OneArtist: case OneAlbumFromArtist: @@ -80,6 +83,7 @@ case ElisaUtils::FileName: case ElisaUtils::Lyricist: case ElisaUtils::Composer: + case ElisaUtils::Radio: case ElisaUtils::Unknown: break; } @@ -131,6 +135,9 @@ case ViewsType::Context: contextViewIsLoaded(); break; + case ViewsType::RadiosBrowser: + radiosBrowserViewIsLoaded(); + break; } } @@ -141,7 +148,7 @@ if (mCurrentView != mTargetView) { Q_EMIT openListView(mTargetView, ElisaUtils::FilterByRecentlyPlayed, 1, mainTitle, {}, 0, imageUrl, ElisaUtils::Track, DatabaseInterface::LastPlayDate, - SortOrder::SortDescending, MultipleAlbum, NoDiscHeaders); + SortOrder::SortDescending, MultipleAlbum, NoDiscHeaders, IsTrack); } } @@ -152,7 +159,7 @@ if (mCurrentView != mTargetView) { Q_EMIT openListView(mTargetView, ElisaUtils::FilterByFrequentlyPlayed, 1, mainTitle, {}, 0, imageUrl, ElisaUtils::Track, DatabaseInterface::PlayFrequency, - SortOrder::SortDescending, MultipleAlbum, NoDiscHeaders); + SortOrder::SortDescending, MultipleAlbum, NoDiscHeaders, IsTrack); } } @@ -180,20 +187,20 @@ mTargetView = ViewsType::OneAlbum; Q_EMIT openListView(mTargetView, ElisaUtils::FilterById, 2, mTargetAlbumTitle, mTargetAlbumAuthor, mTargetDatabaseId, mTargetImageUrl, ElisaUtils::Track, {}, - SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader); + SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader, IsTrack); } else if (mCurrentView == ViewsType::OneArtist && mCurrentArtistName == mTargetAlbumAuthor) { mTargetView = ViewsType::OneAlbumFromArtist; Q_EMIT openListView(mTargetView, ElisaUtils::FilterById, 3, mTargetAlbumTitle, mTargetAlbumAuthor, mTargetDatabaseId, mTargetImageUrl, ElisaUtils::Track, {}, - SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader); + SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader, IsTrack); } else if (mCurrentView == ViewsType::OneArtist && mCurrentArtistName != mTargetAlbumAuthor) { mTargetView = ViewsType::OneAlbumFromArtist; Q_EMIT popOneView(); } else if (mCurrentView == ViewsType::OneArtistFromGenre) { mTargetView = ViewsType::OneAlbumFromArtistAndGenre; Q_EMIT openListView(mTargetView, ElisaUtils::FilterById, 4, mTargetAlbumTitle, mTargetAlbumAuthor, mTargetDatabaseId, mTargetImageUrl, ElisaUtils::Track, {}, - SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader); + SortOrder::NoSort, SingleAlbum, mAlbumDiscHeader, IsTrack); } else { mTargetView = ViewsType::OneAlbum; Q_EMIT openGridView(ViewsType::AllAlbums, ElisaUtils::NoFilter, 1, {}, {}, {}, ElisaUtils::Album, @@ -249,7 +256,7 @@ if (mCurrentView != mTargetView) { Q_EMIT openListView(mTargetView, ElisaUtils::NoFilter, 1, mainTitle, {}, 0, imageUrl, ElisaUtils::Track, Qt::DisplayRole, - SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders); + SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders, IsTrack); } } @@ -291,7 +298,16 @@ if (mCurrentView != mTargetView) { Q_EMIT switchContextView(mTargetView, 1, mainTitle, imageUrl); } +} +void ViewManager::openRadiosBrowser(const QString &mainTitle, const QUrl &imageUrl) +{ + mTargetView = ViewsType::RadiosBrowser; + if (mCurrentView != mTargetView) { + Q_EMIT openListView(mTargetView, ElisaUtils::NoFilter, 1, mainTitle, {}, + 0, imageUrl, ElisaUtils::Radio, Qt::DisplayRole, + SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders, IsRadio); + } } void ViewManager::recentlyPlayedTracksIsLoaded() @@ -310,7 +326,7 @@ if (mTargetView == ViewsType::OneAlbum) { Q_EMIT openListView(mTargetView, ElisaUtils::FilterById, 2, mTargetAlbumTitle, mTargetAlbumAuthor, mTargetDatabaseId, mTargetImageUrl, ElisaUtils::Track, Qt::DisplayRole, - SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders); + SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders, IsTrack); } } @@ -353,7 +369,7 @@ Q_EMIT openListView(mTargetView, ElisaUtils::FilterById, 3, mTargetAlbumTitle, mTargetAlbumAuthor, mTargetDatabaseId, mTargetImageUrl, ElisaUtils::Track, Qt::DisplayRole, - SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders); + SortOrder::SortAscending, MultipleAlbum, NoDiscHeaders, IsTrack); } } @@ -383,6 +399,11 @@ mCurrentView = ViewsType::Context; } +void ViewManager::radiosBrowserViewIsLoaded() +{ + mCurrentView = ViewsType::RadiosBrowser; +} + void ViewManager::goBack() { Q_EMIT popOneView();