Commit 5c4b45a0 authored by Alexander Stippich's avatar Alexander Stippich
Browse files

prepare metadata writing

parent f5078aa7
......@@ -18,7 +18,7 @@ find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core Network Qml Quick T
find_package(Qt5Core ${REQUIRED_QT_VERSION} CONFIG REQUIRED Private)
set(REQUIRED_KF5_VERSION "5.60.0")
set(REQUIRED_KF5_VERSION "5.64.0")
find_package(ECM ${REQUIRED_KF5_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
......
......@@ -212,3 +212,12 @@ ecm_add_test(${filescannerTest_SOURCES}
TEST_NAME "filescannerTest"
LINK_LIBRARIES Qt5::Test elisaLib
)
set(filewriterTest_SOURCES
filewritertest.cpp
)
ecm_add_test(${filewriterTest_SOURCES}
TEST_NAME "filewriterTest"
LINK_LIBRARIES Qt5::Test elisaLib
)
/*
* Copyright 2019-2020 Alexander Stippich <a.stippich@gmx.net>
*
* 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 "filescanner.h"
#include "filewriter.h"
#include "config-upnp-qt.h"
#include <QObject>
#include <QList>
#include <QUrl>
#include <QFile>
#include <QtTest>
class FileWriterTest: public QObject
{
Q_OBJECT
public:
explicit FileWriterTest(QObject *aParent = nullptr) : QObject(aParent)
{
}
private Q_SLOTS:
void initTestCase()
{
}
void testFileAllMetaDataWrite()
{
const auto testFileName = QStringLiteral("writerTest.ogg");
const auto testFileUrl = QUrl::fromLocalFile(testFileName);
QFile::copy(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/test.ogg"), testFileName);
FileWriter fileWriter;
FileScanner fileScanner;
auto scannedTrackBefore = fileScanner.scanOneFile(testFileUrl);
QCOMPARE(scannedTrackBefore.title(), QStringLiteral("Title"));
QCOMPARE(scannedTrackBefore.genre(), QStringLiteral("Genre"));
QCOMPARE(scannedTrackBefore.album(), QStringLiteral("Test"));
QCOMPARE(scannedTrackBefore.artist(), QStringLiteral("Artist"));
QCOMPARE(scannedTrackBefore.year(), 2015);
fileWriter.writeAllMetaDataToFile(testFileUrl, {{DataTypes::AlbumRole, QStringLiteral("testAlbum")},
{DataTypes::GenreRole, QStringLiteral("testGenre")},
{DataTypes::YearRole, 1999},
{DataTypes::TitleRole, QStringLiteral("testTitle")},
{DataTypes::ArtistRole, QStringLiteral("testArtist")}});
auto scannedTrackAfter = fileScanner.scanOneFile(testFileUrl);
QCOMPARE(scannedTrackAfter.title(), QStringLiteral("testTitle"));
QCOMPARE(scannedTrackAfter.genre(), QStringLiteral("testGenre"));
QCOMPARE(scannedTrackAfter.album(), QStringLiteral("testAlbum"));
QCOMPARE(scannedTrackAfter.artist(), QStringLiteral("testArtist"));
QCOMPARE(scannedTrackAfter.year(), 1999);
QFile::remove(testFileName);
}
void testFileSingleMetaDataWrite()
{
const auto testFileName = QStringLiteral("writerTest.ogg");
const auto testFileUrl = QUrl::fromLocalFile(testFileName);
QFile::copy(QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music/test.ogg"), testFileName);
FileWriter fileWriter;
FileScanner fileScanner;
auto scannedTrackBefore = fileScanner.scanOneFile(testFileUrl);
QCOMPARE(scannedTrackBefore.title(), QStringLiteral("Title"));
QCOMPARE(scannedTrackBefore.genre(), QStringLiteral("Genre"));
QCOMPARE(scannedTrackBefore.album(), QStringLiteral("Test"));
QCOMPARE(scannedTrackBefore.artist(), QStringLiteral("Artist"));
QCOMPARE(scannedTrackBefore.year(), 2015);
fileWriter.writeSingleMetaDataToFile(testFileUrl, DataTypes::AlbumRole, QStringLiteral("testAlbum"));
auto scannedTrackAfter = fileScanner.scanOneFile(testFileUrl);
QCOMPARE(scannedTrackAfter.title(), QStringLiteral("Title"));
QCOMPARE(scannedTrackAfter.genre(), QStringLiteral("Genre"));
QCOMPARE(scannedTrackAfter.album(), QStringLiteral("testAlbum"));
QCOMPARE(scannedTrackAfter.artist(), QStringLiteral("Artist"));
QCOMPARE(scannedTrackAfter.year(), 2015);
QFile::remove(testFileName);
}
};
QTEST_GUILESS_MAIN(FileWriterTest)
#include "filewritertest.moc"
......@@ -17,6 +17,7 @@ set(elisaLib_SOURCES
abstractfile/abstractfilelistener.cpp
abstractfile/abstractfilelisting.cpp
filescanner.cpp
filewriter.cpp
viewmanager.cpp
powermanagementinterface.cpp
file/filelistener.cpp
......
/*
* Copyright 2018 Matthieu Gallien <matthieu_gallien@yahoo.fr>
* Copyright 2019-2020 Alexander Stippich <a.stippich@gmx.net>
*
* 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 "filewriter.h"
#include "config-upnp-qt.h"
#include <QMimeDatabase>
#if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND
#include <KFileMetaData/WriterCollection>
#include <KFileMetaData/Writer>
#include <KFileMetaData/UserMetaData>
#include <KFileMetaData/Properties>
#include <KFileMetaData/WriteData>
#include <QHash>
static const QHash<DataTypes::ColumnsRoles, KFileMetaData::Property::Property> propertyTranslation = {
{DataTypes::ColumnsRoles::ArtistRole, KFileMetaData::Property::Artist},
{DataTypes::ColumnsRoles::AlbumArtistRole, KFileMetaData::Property::AlbumArtist},
{DataTypes::ColumnsRoles::GenreRole, KFileMetaData::Property::Genre},
{DataTypes::ColumnsRoles::ComposerRole, KFileMetaData::Property::Composer},
{DataTypes::ColumnsRoles::LyricistRole, KFileMetaData::Property::Lyricist},
{DataTypes::ColumnsRoles::TitleRole, KFileMetaData::Property::Title},
{DataTypes::ColumnsRoles::AlbumRole, KFileMetaData::Property::Album},
{DataTypes::ColumnsRoles::TrackNumberRole, KFileMetaData::Property::TrackNumber},
{DataTypes::ColumnsRoles::DiscNumberRole, KFileMetaData::Property::DiscNumber},
{DataTypes::ColumnsRoles::YearRole, KFileMetaData::Property::ReleaseYear},
{DataTypes::ColumnsRoles::LyricsRole, KFileMetaData::Property::Lyrics},
{DataTypes::ColumnsRoles::CommentRole, KFileMetaData::Property::Comment},
{DataTypes::ColumnsRoles::RatingRole, KFileMetaData::Property::Rating},
};
#endif
class FileWriterPrivate
{
public:
#if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND
KFileMetaData::WriterCollection mAllWriters;
#endif
QMimeDatabase mMimeDb;
};
FileWriter::FileWriter() : d(std::make_unique<FileWriterPrivate>())
{
}
FileWriter::~FileWriter() = default;
bool FileWriter::writeSingleMetaDataToFile(const QUrl &url, const DataTypes::ColumnsRoles role, const QVariant data)
{
#if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND
if (!url.isLocalFile()) {
return false;
}
const auto &localFileName = url.toLocalFile();
const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(localFileName);
if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) {
return false;
}
const auto &mimetype = fileMimeType.name();
const QList<KFileMetaData::Writer*> &writerList = d->mAllWriters.fetchWriters(mimetype);
if (writerList.isEmpty()) {
return false;
}
KFileMetaData::Writer *writer = writerList.first();
KFileMetaData::WriteData writeData(localFileName, mimetype);
auto translatedKey = propertyTranslation.find(role);
if (translatedKey != propertyTranslation.end()) {
writeData.add(translatedKey.value(), data);
}
writer->write(writeData);
#if !defined Q_OS_ANDROID && !defined Q_OS_WIN
auto fileData = KFileMetaData::UserMetaData(localFileName);
if (role == DataTypes::RatingRole) {
fileData.setRating(data.toInt());
}
if (role == DataTypes::CommentRole) {
fileData.setUserComment(data.toString());
}
#endif
return true;
#else
Q_UNUSED(url)
Q_UNUSED(role)
Q_UNUSED(data)
return false;
#endif
}
bool FileWriter::writeAllMetaDataToFile(const QUrl &url, const DataTypes::TrackDataType &data)
{
#if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND
if (!url.isLocalFile()) {
return false;
}
const auto &localFileName = url.toLocalFile();
const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(localFileName);
if (!fileMimeType.name().startsWith(QLatin1String("audio/"))) {
return false;
}
KFileMetaData::UserMetaData md(localFileName);
md.setUserComment(data.value(DataTypes::ColumnsRoles::CommentRole).toString());
md.setRating(data.value(DataTypes::ColumnsRoles::RatingRole).toInt());
const auto &mimetype = fileMimeType.name();
const QList<KFileMetaData::Writer*> &writerList = d->mAllWriters.fetchWriters(mimetype);
if (writerList.isEmpty()) {
return false;
}
KFileMetaData::Writer *writer = writerList.first();
KFileMetaData::WriteData writeData(localFileName, mimetype);
auto rangeBegin = data.constKeyValueBegin();
while (rangeBegin != data.constKeyValueEnd()) {
auto key = (*rangeBegin).first;
auto translatedKey = propertyTranslation.find(key);
if (translatedKey != propertyTranslation.end()) {
writeData.add(translatedKey.value(), (*rangeBegin).second);
}
rangeBegin++;
}
writer->write(writeData);
return true;
#else
Q_UNUSED(url)
Q_UNUSED(data)
return false;
#endif
}
/*
* Copyright 2018 Matthieu Gallien <matthieu_gallien@yahoo.fr>
* Copyright 2019-2020 Alexander Stippich <a.stippich@gmx.net>
*
* 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 FILEWRITER_H
#define FILEWRITER_H
#include "elisaLib_export.h"
#include "datatypes.h"
#include <memory>
class FileWriterPrivate;
class ELISALIB_EXPORT FileWriter
{
public:
FileWriter();
~FileWriter();
bool writeSingleMetaDataToFile(const QUrl &url, const DataTypes::ColumnsRoles role, const QVariant data);
bool writeAllMetaDataToFile(const QUrl &url, const DataTypes::TrackDataType &data);
private:
std::unique_ptr<FileWriterPrivate> d;
};
#endif // FILEWRITER_H
......@@ -18,6 +18,7 @@
#include "modeldataloader.h"
#include "filescanner.h"
#include "filewriter.h"
class ModelDataLoaderPrivate
{
......@@ -41,6 +42,7 @@ public:
FileScanner mFileScanner;
FileWriter mFileWriter;
};
ModelDataLoader::ModelDataLoader(QObject *parent) : QObject(parent), d(std::make_unique<ModelDataLoaderPrivate>())
......@@ -446,4 +448,14 @@ void ModelDataLoader::databaseAlbumsAdded(const ListAlbumDataType &newData)
}
}
void ModelDataLoader::updateFileMetaData(DataTypes::TrackDataType trackDataType, const QUrl &url)
{
d->mFileWriter.writeAllMetaDataToFile(url, trackDataType);
}
void ModelDataLoader::updateSingleFileMetaData(const QUrl &url, DataTypes::ColumnsRoles role, QVariant data)
{
d->mFileWriter.writeSingleMetaDataToFile(url, role, data);
}
#include "moc_modeldataloader.cpp"
......@@ -125,6 +125,10 @@ public Q_SLOTS:
void loadFrequentlyPlayedData(ElisaUtils::PlayListEntryType dataType);
void updateFileMetaData(ModelDataLoader::TrackDataType trackDataType, const QUrl &url);
void updateSingleFileMetaData(const QUrl &url, DataTypes::ColumnsRoles role, QVariant data);
private Q_SLOTS:
void databaseTracksAdded(const ModelDataLoader::ListTrackDataType &newData);
......
......@@ -355,12 +355,6 @@ void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDa
for (DataTypes::ColumnsRoles role : fieldsForTrack) {
if (trackData.constFind(role) != trackData.constEnd()) {
if (role == DataTypes::RatingRole) {
if (trackData[role].toInt() == 0) {
continue;
}
}
mTrackKeys.push_back(role);
mTrackData[role] = trackData[role];
}
......@@ -449,10 +443,6 @@ void TrackMetadataModel::initialize(MusicListenersManager *newManager, DatabaseI
if (mManager) {
mDataLoader.setDatabase(mManager->viewDatabase());
connect(this, &TrackMetadataModel::needDataByUrl,
mManager->tracksListener(), &TracksListener::trackByFileNameInList);
connect(mManager->tracksListener(), &TracksListener::trackHasChanged,
this, &TrackMetadataModel::trackData);
} else if (trackDatabase) {
mDataLoader.setDatabase(trackDatabase);
}
......
......@@ -115,6 +115,8 @@ RowLayout {
readOnly: true
hoverWidgetOpacity: 1
anchors {
left: parent.left
top: parent.top
......
......@@ -30,8 +30,10 @@ Row {
property int hoveredRating: 0
signal ratingEdited()
spacing: 0
opacity: starRating > 0 ? 1 : (readOnly ? 0 : hoverWidgetOpacity)
opacity: starRating >= 2 ? 1 : hoverWidgetOpacity
Repeater {
model: 5
......@@ -75,11 +77,14 @@ Row {
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: if (starRating !== ratingThreshold) {
starRating = ratingThreshold
} else {
starRating = 0
}
onClicked: {
if (starRating !== ratingThreshold) {
starRating = ratingThreshold
} else {
starRating = 0
}
ratingEdited()
}
onEntered: hoveredRating = ratingThreshold
onExited: hoveredRating = 0
......
......@@ -21,6 +21,7 @@
#include "databaseinterface.h"
#include "datatypes.h"
#include "filescanner.h"
#include "filewriter.h"
#include <QSet>
#include <QList>
......@@ -44,6 +45,7 @@ public:
FileScanner mFileScanner;
FileWriter mFileWriter;
};
TracksListener::TracksListener(DatabaseInterface *database, QObject *parent) : QObject(parent), d(std::make_unique<TracksListenerPrivate>())
......@@ -317,4 +319,9 @@ void TracksListener::newGenreInList(qulonglong newDatabaseId, const QString &ent
Q_EMIT tracksListAdded(newDatabaseId, entryTitle, ElisaUtils::Genre, newTracks);
}
void TracksListener::updateSingleFileMetaData(const QUrl &url, DataTypes::ColumnsRoles role, QVariant data)
{
d->mFileWriter.writeSingleMetaDataToFile(url, role, data);
}
#include "moc_trackslistener.cpp"
......@@ -74,6 +74,8 @@ public Q_SLOTS:
void newUrlInList(const QUrl &entryUrl,
ElisaUtils::PlayListEntryType databaseIdType);
void updateSingleFileMetaData(const QUrl &url, DataTypes::ColumnsRoles role, QVariant data);
private:
void newArtistInList(qulonglong newDatabaseId, const QString &artist);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment