Commit c3cb65b7 authored by Matthieu Gallien's avatar Matthieu Gallien

Implement new context view with metadata

Summary:
enlarge the context view to fit new content

allow ManageHeaderBar to export a databaseId property for current track

in the detailed metadata view compose the property name with i18nc

partial upgrade of the database to better handle some missing metadata

do not always put a value for disc number when the metadata is not here

add back indexes when recreating Tracks table

allow comment of tracks to be null

store some metadata only if they are really defined

only really return data on tracks if they are not null in database

allow to inherit from TrackMetadataModel to filter properties

remove obsolete methods from TrackMetadataModel

introduce TrackContextMetadataModel to display metadata in context view

fix TrackMetadataModel to not create multiple connections

provide new context view with metadata from currently playing track

remove useless debug prints

fix size of the cover in context view

when showing ContextView, fix width to not overflow

add the filename at bottom of context view

fine tune the display of context view to be with regular spacing

Test Plan: context view mostly look like the design done by @januz

Reviewers: januz, ngraham, #vdg

Reviewed By: ngraham, #vdg

Subscribers: januz

Maniphest Tasks: T10330

Differential Revision: https://phabricator.kde.org/D18679
parents 358086c2 32396432
......@@ -4600,6 +4600,19 @@ private Q_SLOTS:
musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$5")), QDateTime::fromSecsSinceEpoch(1536689));
musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$13")), QDateTime::fromSecsSinceEpoch(1537689));
QCOMPARE(musicDb.allAlbumsData().count(), 5);
QCOMPARE(musicDb.allArtistsData().count(), 7);
QCOMPARE(musicDb.allTracksData().count(), 22);
QCOMPARE(musicDbArtistAddedSpy.count(), 1);
QCOMPARE(musicDbAlbumAddedSpy.count(), 1);
QCOMPARE(musicDbTrackAddedSpy.count(), 1);
QCOMPARE(musicDbArtistRemovedSpy.count(), 0);
QCOMPARE(musicDbAlbumRemovedSpy.count(), 0);
QCOMPARE(musicDbTrackRemovedSpy.count(), 0);
QCOMPARE(musicDbAlbumModifiedSpy.count(), 0);
QCOMPARE(musicDbTrackModifiedSpy.count(), 10);
QCOMPARE(musicDbDatabaseErrorSpy.count(), 0);
auto recentlyPlayedTracksData = musicDb.recentlyPlayedTracksData(10);
QCOMPARE(recentlyPlayedTracksData.count(), 10);
......
......@@ -16,13 +16,15 @@
*/
#include "models/trackmetadatamodel.h"
#include "qabstractitemmodeltester.h"
#include "databasetestdata.h"
#include <QDebug>
#include <QtTest>
class TrackMetadataModelTests: public QObject
class TrackMetadataModelTests: public QObject, public DatabaseTestData
{
Q_OBJECT
......@@ -67,6 +69,77 @@ private Q_SLOTS:
QCOMPARE(endRemovedRowsSpy.count(), 0);
QCOMPARE(myModel.rowCount(), 1);
}
void modifyTrackInDatabase()
{
QTemporaryFile databaseFile;
databaseFile.open();
qDebug() << "addOneTrackWithoutAlbumArtist" << databaseFile.fileName();
DatabaseInterface musicDb;
musicDb.init(QStringLiteral("testDb"), databaseFile.fileName());
musicDb.insertTracksList(mNewTracks, mNewCovers);
TrackMetadataModel myModel;
QAbstractItemModelTester testModel(&myModel);
QSignalSpy beginResetSpy(&myModel, &TrackMetadataModel::modelAboutToBeReset);
QSignalSpy endResetSpy(&myModel, &TrackMetadataModel::modelReset);
QSignalSpy beginInsertRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeInserted);
QSignalSpy endInsertRowsSpy(&myModel, &TrackMetadataModel::rowsInserted);
QSignalSpy dataChangedSpy(&myModel, &TrackMetadataModel::dataChanged);
QSignalSpy beginRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsAboutToBeRemoved);
QSignalSpy endRemovedRowsSpy(&myModel, &TrackMetadataModel::rowsRemoved);
QCOMPARE(beginResetSpy.count(), 0);
QCOMPARE(endResetSpy.count(), 0);
QCOMPARE(beginInsertRowsSpy.count(), 0);
QCOMPARE(endInsertRowsSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(beginRemovedRowsSpy.count(), 0);
QCOMPARE(endRemovedRowsSpy.count(), 0);
QCOMPARE(myModel.rowCount(), 0);
myModel.setDatabase(&musicDb);
auto trackId = musicDb.trackIdFromFileName(QUrl::fromLocalFile(QStringLiteral("/$1")));
myModel.initializeByTrackId(trackId);
QCOMPARE(beginResetSpy.count(), 1);
QCOMPARE(endResetSpy.count(), 1);
QCOMPARE(beginInsertRowsSpy.count(), 0);
QCOMPARE(endInsertRowsSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(beginRemovedRowsSpy.count(), 0);
QCOMPARE(endRemovedRowsSpy.count(), 0);
QCOMPARE(myModel.rowCount(), 11);
musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$2")), QDateTime::currentDateTime());
QCOMPARE(beginResetSpy.count(), 1);
QCOMPARE(endResetSpy.count(), 1);
QCOMPARE(beginInsertRowsSpy.count(), 0);
QCOMPARE(endInsertRowsSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(beginRemovedRowsSpy.count(), 0);
QCOMPARE(endRemovedRowsSpy.count(), 0);
QCOMPARE(myModel.rowCount(), 11);
musicDb.trackHasStartedPlaying(QUrl::fromLocalFile(QStringLiteral("/$1")), QDateTime::currentDateTime());
QCOMPARE(beginResetSpy.count(), 2);
QCOMPARE(endResetSpy.count(), 2);
QCOMPARE(beginInsertRowsSpy.count(), 0);
QCOMPARE(endInsertRowsSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(beginRemovedRowsSpy.count(), 0);
QCOMPARE(endRemovedRowsSpy.count(), 0);
QCOMPARE(myModel.rowCount(), 12);
}
};
QTEST_GUILESS_MAIN(TrackMetadataModelTests)
......
......@@ -28,6 +28,7 @@ set(elisaLib_SOURCES
models/alltracksproxymodel.cpp
models/singlealbumproxymodel.cpp
models/trackmetadatamodel.cpp
models/trackcontextmetadatamodel.cpp
models/viewsmodel.cpp
)
......@@ -360,6 +361,7 @@ if (Qt5Quick_FOUND AND Qt5Widgets_FOUND)
qml/NavigationActionBar.qml
qml/MediaPlayerControl.qml
qml/ContextView.qml
qml/ContextViewLyrics.qml
qml/ContentView.qml
qml/ViewSelector.qml
qml/DataGridView.qml
......@@ -376,6 +378,7 @@ if (Qt5Quick_FOUND AND Qt5Widgets_FOUND)
qml/PlayListAlbumHeader.qml
qml/BasicPlayListAlbumHeader.qml
qml/MetaDataDelegate.qml
qml/MediaTrackDelegate.qml
qml/MediaAlbumTrackDelegate.qml
qml/MediaTrackMetadataView.qml
......
......@@ -779,6 +779,10 @@ void DatabaseInterface::trackHasStartedPlaying(const QUrl &fileName, const QDate
}
updateTrackStatistics(fileName, time);
auto trackId = internalTrackIdFromFileName(fileName);
if (trackId != 0) {
Q_EMIT trackModified(internalOneTrackPartialData(trackId));
}
transactionResult = finishTransaction();
if (!transactionResult) {
......
......@@ -85,6 +85,7 @@ public:
PlayCounter,
PlayFrequency,
ElementTypeRole,
LyricsRole,
};
Q_ENUM(ColumnsRoles)
......@@ -186,6 +187,11 @@ public:
return operator[](key_type::LyricistRole).toString();
}
QString lyrics() const
{
return operator[](key_type::LyricsRole).toString();
}
QString comment() const
{
return operator[](key_type::CommentRole).toString();
......
......@@ -387,7 +387,9 @@ void ElisaApplication::initializePlayer()
d->mManageHeaderBar->setAlbumRole(MediaPlayList::AlbumRole);
d->mManageHeaderBar->setAlbumArtistRole(MediaPlayList::AlbumArtistRole);
d->mManageHeaderBar->setArtistRole(MediaPlayList::ArtistRole);
d->mManageHeaderBar->setFileNameRole(MediaPlayList::ResourceRole);
d->mManageHeaderBar->setImageRole(MediaPlayList::ImageUrlRole);
d->mManageHeaderBar->setDatabaseIdRole(MediaPlayList::DatabaseIdRole);
d->mManageHeaderBar->setAlbumIdRole(MediaPlayList::AlbumIdRole);
d->mManageHeaderBar->setIsValidRole(MediaPlayList::IsValidRole);
d->mManageHeaderBar->setPlayListModel(d->mMediaPlayList.get());
......
......@@ -47,6 +47,7 @@
#include "databaseinterface.h"
#include "models/datamodel.h"
#include "models/trackmetadatamodel.h"
#include "models/trackcontextmetadatamodel.h"
#include "models/viewsmodel.h"
#include "models/gridviewproxymodel.h"
#include "models/alltracksproxymodel.h"
......@@ -123,6 +124,7 @@ void ElisaQmlTestPlugin::registerTypes(const char *uri)
qmlRegisterType<ViewManager>(uri, 1, 0, "ViewManager");
qmlRegisterType<DataModel>(uri, 1, 0, "DataModel");
qmlRegisterType<TrackMetadataModel>(uri, 1, 0, "TrackMetadataModel");
qmlRegisterType<TrackContextMetaDataModel>(uri, 1, 0, "TrackContextMetaDataModel");
qmlRegisterType<ViewsModel>(uri, 1, 0, "ViewsModel");
qmlRegisterType<GridViewProxyModel>(uri, 1, 0, "GridViewProxyModel");
qmlRegisterType<AllTracksProxyModel>(uri, 1, 0, "AllTracksProxyModel");
......
......@@ -123,6 +123,7 @@ void FileScanner::scanProperties(const QString &localFileName, MusicAudioTrack &
auto yearProperty = d->mAllProperties.find(KFileMetaData::Property::ReleaseYear);
auto composerProperty = d->mAllProperties.find(KFileMetaData::Property::Composer);
auto lyricistProperty = d->mAllProperties.find(KFileMetaData::Property::Lyricist);
auto lyricsProperty = d->mAllProperties.find(KFileMetaData::Property::Lyrics);
auto channelsProperty = d->mAllProperties.find(KFileMetaData::Property::Channels);
auto bitRateProperty = d->mAllProperties.find(KFileMetaData::Property::BitRate);
auto sampleRateProperty = d->mAllProperties.find(KFileMetaData::Property::SampleRate);
......@@ -154,8 +155,6 @@ void FileScanner::scanProperties(const QString &localFileName, MusicAudioTrack &
if (discNumberProperty != d->mAllProperties.end()) {
trackData.setDiscNumber(discNumberProperty->toInt());
} else {
trackData.setDiscNumber(1);
}
if (albumArtistProperty != d->mAllProperties.end()) {
......@@ -190,6 +189,10 @@ void FileScanner::scanProperties(const QString &localFileName, MusicAudioTrack &
trackData.setLyricist(lyricistProperty->toStringList().join(QStringLiteral(", ")));
}
if (lyricsProperty != d->mAllProperties.end()) {
trackData.setLyrics(lyricsProperty->toString());
}
if (trackData.artist().isEmpty()) {
trackData.setArtist(trackData.albumArtist());
}
......
......@@ -18,6 +18,8 @@
#ifndef FILESCANNER_H
#define FILESCANNER_H
#include "elisaLib_export.h"
#include "musicaudiotrack.h"
#include <QUrl>
......@@ -40,7 +42,7 @@ class File;
class FileScannerPrivate;
class FileScanner
class ELISALIB_EXPORT FileScanner
{
public:
......
......@@ -62,6 +62,12 @@ void ManageHeaderBar::setAlbumArtistRole(int value)
Q_EMIT albumArtistRoleChanged();
}
void ManageHeaderBar::setFileNameRole(int value)
{
mFileNameRole = value;
Q_EMIT fileNameRoleChanged();
}
int ManageHeaderBar::albumRole() const
{
return mAlbumRole;
......@@ -72,17 +78,33 @@ int ManageHeaderBar::albumArtistRole() const
return mAlbumArtistRole;
}
int ManageHeaderBar::fileNameRole() const
{
return mFileNameRole;
}
void ManageHeaderBar::setImageRole(int value)
{
mImageRole = value;
Q_EMIT imageRoleChanged();
}
void ManageHeaderBar::setDatabaseIdRole(int databaseIdRole)
{
mDatabaseIdRole = databaseIdRole;
Q_EMIT databaseIdRoleChanged();
}
int ManageHeaderBar::imageRole() const
{
return mImageRole;
}
int ManageHeaderBar::databaseIdRole() const
{
return mDatabaseIdRole;
}
void ManageHeaderBar::setAlbumIdRole(int albumIdRole)
{
mAlbumIdRole = albumIdRole;
......@@ -112,6 +134,24 @@ QVariant ManageHeaderBar::albumArtist() const
return mCurrentTrack.data(mAlbumArtistRole);
}
QString ManageHeaderBar::fileName() const
{
QString result;
if (!mCurrentTrack.isValid()) {
return result;
}
auto fileNameUrl = mCurrentTrack.data(mFileNameRole).toUrl();
if (fileNameUrl.isLocalFile()) {
result = fileNameUrl.toLocalFile();
} else {
result = fileNameUrl.toString();
}
return result;
}
QVariant ManageHeaderBar::title() const
{
if (!mCurrentTrack.isValid()) {
......@@ -139,6 +179,15 @@ QUrl ManageHeaderBar::image() const
return mCurrentTrack.data(mImageRole).toUrl();
}
qulonglong ManageHeaderBar::databaseId() const
{
if (!mCurrentTrack.isValid()) {
return 0;
}
return mCurrentTrack.data(mDatabaseIdRole).toULongLong();
}
qulonglong ManageHeaderBar::albumId() const
{
if (!mCurrentTrack.isValid()) {
......@@ -221,7 +270,9 @@ void ManageHeaderBar::tracksDataChanged(const QModelIndex &topLeft, const QModel
notifyTitleProperty();
notifyAlbumProperty();
notifyAlbumArtistProperty();
notifyFileNameProperty();
notifyImageProperty();
notifyDatabaseIdProperty();
notifyAlbumIdProperty();
notifyIsValidProperty();
} else {
......@@ -238,9 +289,15 @@ void ManageHeaderBar::tracksDataChanged(const QModelIndex &topLeft, const QModel
if (oneRole == mAlbumArtistRole) {
notifyAlbumArtistProperty();
}
if (oneRole == mFileNameRole) {
notifyFileNameProperty();
}
if (oneRole == mImageRole) {
notifyImageProperty();
}
if (oneRole == mDatabaseIdRole) {
notifyDatabaseIdProperty();
}
if (oneRole == mAlbumIdRole) {
notifyAlbumIdProperty();
}
......@@ -287,7 +344,9 @@ void ManageHeaderBar::tracksRemoved(const QModelIndex &parent, int first, int la
notifyTitleProperty();
notifyAlbumProperty();
notifyAlbumArtistProperty();
notifyFileNameProperty();
notifyImageProperty();
notifyDatabaseIdProperty();
notifyAlbumIdProperty();
notifyIsValidProperty();
notifyRemainingTracksProperty();
......@@ -338,6 +397,16 @@ void ManageHeaderBar::notifyAlbumArtistProperty()
}
}
void ManageHeaderBar::notifyFileNameProperty()
{
auto newFileNameValue = mCurrentTrack.data(mFileNameRole);
if (mOldFileName != newFileNameValue) {
Q_EMIT fileNameChanged();
mOldFileName = newFileNameValue;
}
}
void ManageHeaderBar::notifyImageProperty()
{
auto newImageValue = mCurrentTrack.data(mImageRole);
......@@ -348,6 +417,21 @@ void ManageHeaderBar::notifyImageProperty()
}
}
void ManageHeaderBar::notifyDatabaseIdProperty()
{
bool conversionOk;
auto newDatabaseIdValue = mCurrentTrack.data(mDatabaseIdRole).toULongLong(&conversionOk);
if (conversionOk && mOldDatabaseId != newDatabaseIdValue) {
Q_EMIT databaseIdChanged();
mOldDatabaseId = newDatabaseIdValue;
} else if (!conversionOk && mOldDatabaseId != 0) {
Q_EMIT databaseIdChanged();
mOldDatabaseId = 0;
}
}
void ManageHeaderBar::notifyAlbumIdProperty()
{
bool conversionOk;
......@@ -409,7 +493,9 @@ void ManageHeaderBar::setCurrentTrack(const QPersistentModelIndex &currentTrack)
notifyTitleProperty();
notifyAlbumProperty();
notifyAlbumArtistProperty();
notifyFileNameProperty();
notifyImageProperty();
notifyDatabaseIdProperty();
notifyAlbumIdProperty();
notifyIsValidProperty();
}
......
......@@ -61,11 +61,21 @@ class ELISALIB_EXPORT ManageHeaderBar : public QObject
WRITE setAlbumArtistRole
NOTIFY albumArtistRoleChanged)
Q_PROPERTY(int fileNameRole
READ fileNameRole
WRITE setFileNameRole
NOTIFY fileNameRoleChanged)
Q_PROPERTY(int imageRole
READ imageRole
WRITE setImageRole
NOTIFY imageRoleChanged)
Q_PROPERTY(int databaseIdRole
READ databaseIdRole
WRITE setDatabaseIdRole
NOTIFY databaseIdRoleChanged)
Q_PROPERTY(int albumIdRole
READ albumIdRole
WRITE setAlbumIdRole
......@@ -92,10 +102,18 @@ class ELISALIB_EXPORT ManageHeaderBar : public QObject
READ albumArtist
NOTIFY albumArtistChanged)
Q_PROPERTY(QString fileName
READ fileName
NOTIFY fileNameChanged)
Q_PROPERTY(QUrl image
READ image
NOTIFY imageChanged)
Q_PROPERTY(qulonglong databaseId
READ databaseId
NOTIFY databaseIdChanged)
Q_PROPERTY(qulonglong albumId
READ albumId
NOTIFY albumIdChanged)
......@@ -124,8 +142,12 @@ public:
int albumArtistRole() const;
int fileNameRole() const;
int imageRole() const;
int databaseIdRole() const;
int albumIdRole() const;
int isValidRole() const;
......@@ -138,8 +160,12 @@ public:
QVariant albumArtist() const;
QString fileName() const;
QUrl image() const;
qulonglong databaseId() const;
qulonglong albumId() const;
bool isValid() const;
......@@ -160,8 +186,12 @@ Q_SIGNALS:
void albumArtistRoleChanged();
void fileNameRoleChanged();
void imageRoleChanged();
void databaseIdRoleChanged();
void albumIdRoleChanged();
void isValidRoleChanged();
......@@ -174,10 +204,14 @@ Q_SIGNALS:
void albumArtistChanged();
void fileNameChanged();
void imageChanged();
void remainingTracksChanged();
void databaseIdChanged();
void albumIdChanged();
void isValidChanged();
......@@ -196,8 +230,12 @@ public Q_SLOTS:
void setAlbumArtistRole(int value);
void setFileNameRole(int value);
void setImageRole(int value);
void setDatabaseIdRole(int databaseIdRole);
void setAlbumIdRole(int albumIdRole);
void setIsValidRole(int isValidRole);
......@@ -224,8 +262,12 @@ private:
void notifyAlbumArtistProperty();
void notifyFileNameProperty();
void notifyImageProperty();
void notifyDatabaseIdProperty();
void notifyAlbumIdProperty();
void notifyIsValidProperty();
......@@ -244,8 +286,12 @@ private:
int mAlbumArtistRole = Qt::DisplayRole;
int mFileNameRole = Qt::DisplayRole;
int mImageRole = Qt::DisplayRole;
int mDatabaseIdRole = Qt::DisplayRole;
int mAlbumIdRole = Qt::DisplayRole;
int mIsValidRole = Qt::DisplayRole;
......@@ -258,8 +304,12 @@ private:
QVariant mOldAlbumArtist;
QVariant mOldFileName;
QVariant mOldImage;
qulonglong mOldDatabaseId = 0;
qulonglong mOldAlbumId = 0;
bool mOldIsValid = false;
......
/*
* Copyright 2019 Matthieu Gallien <matthieu_gallien@yahoo.fr>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "trackcontextmetadatamodel.h"
TrackContextMetaDataModel::TrackContextMetaDataModel(QObject *parent) : TrackMetadataModel(parent)
{
}
void TrackContextMetaDataModel::filterDataFromTrackData()
{
removeMetaData(DatabaseInterface::TitleRole);
removeMetaData(DatabaseInterface::ArtistRole);
removeMetaData(DatabaseInterface::AlbumRole);
if (dataFromType(DatabaseInterface::IsSingleDiscAlbumRole).toBool() &&
dataFromType(DatabaseInterface::DiscNumberRole).toInt() == 1) {
removeMetaData(DatabaseInterface::DiscNumberRole);
}
}
void TrackContextMetaDataModel::fillLyricsDataFromTrack()
{
}
#include "moc_trackcontextmetadatamodel.cpp"
/*
* Copyright 2019 Matthieu Gallien <matthieu_gallien@yahoo.fr>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef TRACKCONTEXTMETADATAMODEL_H
#define TRACKCONTEXTMETADATAMODEL_H
#include "elisaLib_export.h"
#include "trackmetadatamodel.h"
class ELISALIB_EXPORT TrackContextMetaDataModel : public TrackMetadataModel
{
Q_OBJECT
public:
TrackContextMetaDataModel(QObject *parent = nullptr);
protected:
void filterDataFromTrackData() override;
void fillLyricsDataFromTrack() override;
};
#endif // TRACKCONTEXTMETADATAMODEL_H
This diff is collapsed.
......@@ -23,9 +23,12 @@
#include "elisautils.h"
#include "databaseinterface.h"
#include "modeldataloader.h"
#include "filescanner.h"
#include <QUrl>
#include <QAbstractListModel>
#include <QMimeDatabase>
#include <QFutureWatcher>
class MusicListenersManager;