Deduplicate embedded images, ref #628

This commit is contained in:
emeric
2025-03-15 14:48:37 +01:00
parent 744abefc8b
commit 05000d8a1a
65 changed files with 1307 additions and 319 deletions
+2
View File
@@ -25,6 +25,8 @@ add_library(lmsdatabase STATIC
impl/SqlQuery.cpp
impl/Track.cpp
impl/TrackBookmark.cpp
impl/TrackEmbeddedImage.cpp
impl/TrackEmbeddedImageLink.cpp
impl/TrackLyrics.cpp
impl/Types.cpp
impl/UIState.cpp
+1 -1
View File
@@ -29,9 +29,9 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "SqlQuery.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -24,9 +24,9 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -27,10 +27,10 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "IdTypeTraits.hpp"
#include "SqlQuery.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -23,9 +23,9 @@
#include "database/Session.hpp"
#include "database/Types.hpp"
#include "IdTypeTraits.hpp"
#include "PathTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PathTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -26,9 +26,9 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "IdTypeTraits.hpp"
#include "PathTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PathTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -22,9 +22,9 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "SqlQuery.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+3 -3
View File
@@ -23,10 +23,10 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "IdTypeTraits.hpp"
#include "PathTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PathTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+34 -2
View File
@@ -35,7 +35,7 @@ namespace lms::db
{
namespace
{
static constexpr Version LMS_DATABASE_VERSION{ 83 };
static constexpr Version LMS_DATABASE_VERSION{ 84 };
}
VersionInfo::VersionInfo()
@@ -1101,11 +1101,42 @@ FROM tracklist)");
void migrateFromV82(Session& session)
{
// new setting to display inline artist relationships in the release view
// New setting to display inline artist relationships in the release view
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD COLUMN ui_enable_inline_artist_relationships BOOLEAN NOT NULL DEFAULT(false)");
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD COLUMN ui_inline_artist_relationships BIGINT NOT NULL DEFAULT(68)"); // Composer + Performer
}
void migrateFromV83(Session& session)
{
// New embedded track image handling
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN has_cover");
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_embedded_image" (
"id" integer primary key autoincrement,
"version" integer not null,
"hash" text not null,
"size" integer not null,
"width" integer not null,
"height" integer not null,
"mime_type" text not null
))");
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_embedded_image_link" (
"id" integer primary key autoincrement,
"version" integer not null,
"index" integer not null,
"is_preferred" boolean not null,
"type" integer not null,
"description" text not null,
"track_id" bigint,
"track_embedded_image_id" bigint,
constraint "fk_track_embedded_image_link_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
constraint "fk_track_embedded_image_link_track_embedded_image" foreign key ("track_embedded_image_id") references "track_embedded_image" ("id") on delete cascade deferrable initially deferred
))");
// Just increment the scan version of the settings to make the next scan rescan everything
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
}
bool doDbMigration(Session& session)
{
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -1165,6 +1196,7 @@ FROM tracklist)");
{ 80, migrateFromV80 },
{ 81, migrateFromV81 },
{ 82, migrateFromV82 },
{ 83, migrateFromV83 },
};
bool migrationPerformed{};
+3 -3
View File
@@ -28,10 +28,10 @@
#include "database/TrackList.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "PathTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PathTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -28,9 +28,9 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+4 -4
View File
@@ -31,12 +31,12 @@
#include "database/Types.hpp"
#include "database/User.hpp"
#include "EnumSetTraits.hpp"
#include "IdTypeTraits.hpp"
#include "PartialDateTimeTraits.hpp"
#include "SqlQuery.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/EnumSetTraits.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PartialDateTimeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+15 -3
View File
@@ -43,6 +43,8 @@
#include "database/Track.hpp"
#include "database/TrackArtistLink.hpp"
#include "database/TrackBookmark.hpp"
#include "database/TrackEmbeddedImage.hpp"
#include "database/TrackEmbeddedImageLink.hpp"
#include "database/TrackFeatures.hpp"
#include "database/TrackList.hpp"
#include "database/TrackLyrics.hpp"
@@ -50,11 +52,12 @@
#include "database/UIState.hpp"
#include "database/User.hpp"
#include "EnumSetTraits.hpp"
#include "Migration.hpp"
#include "PartialDateTimeTraits.hpp"
#include "PathTraits.hpp"
#include "Utils.hpp"
#include "traits/EnumSetTraits.hpp"
#include "traits/ImageHashTypeTraits.hpp"
#include "traits/PartialDateTimeTraits.hpp"
#include "traits/PathTraits.hpp"
namespace lms::db
{
@@ -122,6 +125,8 @@ namespace lms::db
_session.mapClass<Track>("track");
_session.mapClass<TrackBookmark>("track_bookmark");
_session.mapClass<TrackArtistLink>("track_artist_link");
_session.mapClass<TrackEmbeddedImage>("track_embedded_image");
_session.mapClass<TrackEmbeddedImageLink>("track_embedded_image_link");
_session.mapClass<TrackFeatures>("track_features");
_session.mapClass<TrackList>("tracklist");
_session.mapClass<TrackListEntry>("tracklist_entry");
@@ -209,6 +214,13 @@ namespace lms::db
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_media_library_idx ON directory(media_library_id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_name_idx ON directory(name COLLATE NOCASE)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_id_idx ON track_embedded_image(id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_hash_idx ON track_embedded_image(hash)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_id_idx ON track_embedded_image_link(track_id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_embedded_image_id_track_id_idx ON track_embedded_image_link(track_embedded_image_id, track_id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_is_preferred_track_id_track_embedded_image_id_idx ON track_embedded_image_link(is_preferred, track_id, track_embedded_image_id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_path_idx ON image(absolute_file_path)");
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -25,8 +25,8 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+22 -7
View File
@@ -29,16 +29,18 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "database/TrackEmbeddedImage.hpp"
#include "database/TrackEmbeddedImageLink.hpp"
#include "database/TrackFeatures.hpp"
#include "database/TrackLyrics.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "PartialDateTimeTraits.hpp"
#include "PathTraits.hpp"
#include "SqlQuery.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PartialDateTimeTraits.hpp"
#include "traits/PathTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
@@ -177,12 +179,15 @@ namespace lms::db
if (params.directory.isValid())
query.where("t.directory_id = ?").bind(params.directory);
if (params.hasEmbeddedImage.has_value())
query.where("t.has_cover = ?").bind(params.hasEmbeddedImage.value());
if (params.fileSize.has_value())
query.where("t.file_size = ?").bind(static_cast<long long>(params.fileSize.value()));
if (params.embeddedImageId.isValid())
{
query.join("track_embedded_image_link t_e_i_l ON t_e_i_l.track_id = t.id");
query.where("t_e_i_l.track_embedded_image_id = ?").bind(params.embeddedImageId);
}
switch (params.sortMethod)
{
case TrackSortMethod::None:
@@ -469,6 +474,16 @@ namespace lms::db
_trackLyrics.insert(getDboPtr(lyrics));
}
void Track::clearEmbeddedImageLinks()
{
_embeddedImageLinks.clear();
}
void Track::addEmbeddedImageLink(const ObjectPtr<TrackEmbeddedImageLink>& image)
{
_embeddedImageLinks.insert(getDboPtr(image));
}
std::optional<int> Track::getYear() const
{
return _date.getYear();
+1 -1
View File
@@ -23,8 +23,8 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+1 -1
View File
@@ -23,8 +23,8 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2025 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "database/Directory.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/TrackEmbeddedImage.hpp"
#include "database/Types.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/ImageHashTypeTraits.hpp"
namespace lms::db
{
namespace
{
Wt::Dbo::Query<Wt::Dbo::ptr<TrackEmbeddedImage>> createQuery(Session& session, const TrackEmbeddedImage::FindParameters& params)
{
session.checkReadTransaction();
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<TrackEmbeddedImage>>("SELECT t_e_i FROM track_embedded_image t_e_i") };
if (params.isPreferred
|| params.track.isValid()
|| params.release.isValid()
|| params.trackList.isValid()
|| params.sortMethod == TrackEmbeddedImageSortMethod::FrontCoverAndSize)
{
query.join("track_embedded_image_link t_e_i_l ON t_e_i_l.track_embedded_image_id = t_e_i.id");
if (params.isPreferred)
query.where("t_e_i_l.is_preferred = ?").bind(params.isPreferred.value());
if (params.track.isValid())
query.where("t_e_i_l.track_id = ?").bind(params.track);
if (params.release.isValid())
{
query.join("track t ON t_e_i_l.track_id = t.id");
query.where("t.release_id = ?").bind(params.release);
}
if (params.trackList.isValid())
{
query.join("tracklist_entry t_l_e ON t_l_e.track_id = t_e_i_l.track_id");
query.where("t_l_e.tracklist_id = ?").bind(params.trackList);
}
}
switch (params.sortMethod)
{
case TrackEmbeddedImageSortMethod::None:
break;
case TrackEmbeddedImageSortMethod::FrontCoverAndSize:
query.orderBy("CASE WHEN t_e_i_l.type = ? THEN 0 ELSE 1 END, t_e_i.size").bind(ImageType::FrontCover);
break;
}
return query;
}
} // namespace
TrackEmbeddedImage::pointer TrackEmbeddedImage::create(Session& session)
{
return session.getDboSession()->add(std::unique_ptr<TrackEmbeddedImage>{ new TrackEmbeddedImage{} });
}
std::size_t TrackEmbeddedImage::getCount(Session& session)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM track_embedded_image"));
}
TrackEmbeddedImage::pointer TrackEmbeddedImage::find(Session& session, TrackEmbeddedImageId id)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<TrackEmbeddedImage>().where("id = ?").bind(id));
}
void TrackEmbeddedImage::find(Session& session, const FindParameters& params, const std::function<void(const pointer&)>& func)
{
session.checkReadTransaction();
auto query{ createQuery(session, params) };
utils::forEachQueryRangeResult(query, params.range, func);
}
TrackEmbeddedImage::pointer TrackEmbeddedImage::find(Session& session, std::size_t size, ImageHashType hash)
{
session.checkReadTransaction();
auto query{ session.getDboSession()->find<TrackEmbeddedImage>().where("size = ?").bind(static_cast<int>(size)).where("hash = ?").bind(hash) };
return utils::fetchQuerySingleResult(query);
}
RangeResults<TrackEmbeddedImageId> TrackEmbeddedImage::findOrphanIds(Session& session, std::optional<Range> range)
{
session.checkReadTransaction();
auto query{ session.getDboSession()->query<TrackEmbeddedImageId>("SELECT t_e_i.id FROM track_embedded_image t_e_i LEFT JOIN track_embedded_image_link t_e_i_l ON t_e_i.id = t_e_i_l.track_embedded_image_id WHERE t_e_i_l.track_embedded_image_id IS NULL") };
return utils::execRangeQuery<TrackEmbeddedImageId>(query, range);
}
} // namespace lms::db
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2025 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "database/TrackEmbeddedImageLink.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/TrackEmbeddedImage.hpp"
#include "database/Types.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
TrackEmbeddedImageLink::TrackEmbeddedImageLink(ObjectPtr<Track> track, ObjectPtr<TrackEmbeddedImage> image)
: _track{ getDboPtr(track) }
, _image{ getDboPtr(image) }
{
}
TrackEmbeddedImageLink::pointer TrackEmbeddedImageLink::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackEmbeddedImage> image)
{
return session.getDboSession()->add(std::unique_ptr<TrackEmbeddedImageLink>{ new TrackEmbeddedImageLink{ track, image } });
}
std::size_t TrackEmbeddedImageLink::getCount(Session& session)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM track_embedded_image_link"));
}
TrackEmbeddedImageLink::pointer TrackEmbeddedImageLink::find(Session& session, TrackEmbeddedImageLinkId id)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<TrackEmbeddedImageLink>().where("id = ?").bind(id));
}
ObjectPtr<Track> TrackEmbeddedImageLink::getTrack() const
{
return _track;
}
ObjectPtr<TrackEmbeddedImage> TrackEmbeddedImageLink::getImage() const
{
return _image;
}
} // namespace lms::db
+1 -1
View File
@@ -27,8 +27,8 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -26,10 +26,10 @@
#include "database/Track.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "SqlQuery.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+2 -2
View File
@@ -28,9 +28,9 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "IdTypeTraits.hpp"
#include "PathTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/PathTraits.hpp"
namespace lms::db
{
+2 -3
View File
@@ -19,13 +19,12 @@
#include "database/UIState.hpp"
#include "core/ILogger.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
+3 -3
View File
@@ -27,10 +27,10 @@
#include "database/Track.hpp"
#include "database/UIState.hpp"
#include "EnumSetTraits.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
#include "traits/EnumSetTraits.hpp"
#include "traits/IdTypeTraits.hpp"
#include "traits/StringViewTraits.hpp"
namespace lms::db
{
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2025 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Wt/Dbo/StdSqlTraits.h>
#include "core/String.hpp"
#include "database/Types.hpp"
namespace Wt::Dbo
{
template<>
struct sql_value_traits<lms::db::ImageHashType>
{
static const bool specialized = true;
// Uses an underlying string to encode this big value
static std::string type(SqlConnection* conn, int size)
{
return sql_value_traits<std::string, void>::type(conn, size);
}
static void bind(const lms::db::ImageHashType& v, SqlStatement* statement, int column, int size)
{
std::string valueAsStr{ std::to_string(v.value()) };
sql_value_traits<std::string>::bind(valueAsStr, statement, column, size);
}
static bool read(lms::db::ImageHashType& v, SqlStatement* statement, int column, int size)
{
std::string valueAsStr;
if (sql_value_traits<std::string>::read(valueAsStr, statement, column, size))
{
if (const auto parsedValue{ lms::core::stringUtils::readAs<lms::db::ImageHashType::underlying_type>(valueAsStr) })
{
v = lms::db::ImageHashType{ *parsedValue };
return true;
}
}
v = lms::db::ImageHashType{};
return false;
}
};
} // namespace Wt::Dbo
+12 -10
View File
@@ -41,6 +41,7 @@
#include "database/MediaLibraryId.hpp"
#include "database/Object.hpp"
#include "database/ReleaseId.hpp"
#include "database/TrackEmbeddedImageId.hpp"
#include "database/TrackId.hpp"
#include "database/TrackListId.hpp"
#include "database/Types.hpp"
@@ -52,6 +53,7 @@ namespace lms::db
class Cluster;
class ClusterType;
class Directory;
class TrackEmbeddedImageLink;
class MediaLibrary;
class Release;
class Session;
@@ -85,8 +87,8 @@ namespace lms::db
std::optional<int> trackNumber; // matching this track number
std::optional<int> discNumber; // matching this disc number
DirectoryId directory; // if set, tracks in this directory
std::optional<bool> hasEmbeddedImage; // if set, tracks that have or not embedded images
std::optional<std::size_t> fileSize; // if set, tracks that match this file size
TrackEmbeddedImageId embeddedImageId; // if set, tracks that have this embedded image
FindParameters& setFilters(const Filters& _filters)
{
@@ -182,16 +184,16 @@ namespace lms::db
directory = _directory;
return *this;
}
FindParameters& setHasEmbeddedImage(std::optional<bool> _hasEmbeddedImage)
{
hasEmbeddedImage = _hasEmbeddedImage;
return *this;
}
FindParameters& setFileSize(std::optional<std::size_t> _fileSize)
{
fileSize = _fileSize;
return *this;
}
FindParameters& setEmbeddedImage(TrackEmbeddedImageId _embeddedImageId)
{
embeddedImageId = _embeddedImageId;
return *this;
}
};
Track() = default;
@@ -232,7 +234,6 @@ namespace lms::db
void setSampleRate(std::size_t channelCount) { _sampleRate = channelCount; }
void setDate(const core::PartialDateTime& date) { _date = date; }
void setOriginalDate(const core::PartialDateTime& date) { _originalDate = date; }
void setHasCover(bool hasCover) { _hasCover = hasCover; }
void setTrackMBID(const std::optional<core::UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
void setRecordingMBID(const std::optional<core::UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
void setCopyright(std::string_view copyright);
@@ -249,6 +250,8 @@ namespace lms::db
void clearLyrics();
void clearEmbeddedLyrics();
void addLyrics(const ObjectPtr<TrackLyrics>& lyrics);
void clearEmbeddedImageLinks();
void addEmbeddedImageLink(const ObjectPtr<TrackEmbeddedImageLink>& link);
void setMediaLibrary(ObjectPtr<MediaLibrary> mediaLibrary) { _mediaLibrary = getDboPtr(mediaLibrary); }
void setDirectory(ObjectPtr<Directory> directory) { _directory = getDboPtr(directory); }
@@ -273,7 +276,6 @@ namespace lms::db
std::optional<int> getOriginalYear() const;
const Wt::WDateTime& getLastWriteTime() const { return _fileLastWrite; }
const Wt::WDateTime& getAddedTime() const { return _fileAdded; }
bool hasCover() const { return _hasCover; }
bool hasLyrics() const;
std::optional<core::UUID> getTrackMBID() const { return core::UUID::fromString(_trackMBID); }
std::optional<core::UUID> getRecordingMBID() const { return core::UUID::fromString(_recordingMBID); }
@@ -321,7 +323,6 @@ namespace lms::db
Wt::Dbo::field(a, _fileSize, "file_size");
Wt::Dbo::field(a, _fileLastWrite, "file_last_write");
Wt::Dbo::field(a, _fileAdded, "file_added");
Wt::Dbo::field(a, _hasCover, "has_cover");
Wt::Dbo::field(a, _trackMBID, "mbid");
Wt::Dbo::field(a, _recordingMBID, "recording_mbid");
Wt::Dbo::field(a, _copyright, "copyright");
@@ -338,6 +339,7 @@ namespace lms::db
Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _trackLyrics, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasMany(a, _embeddedImageLinks, Wt::Dbo::ManyToOne, "track");
}
private:
@@ -368,7 +370,6 @@ namespace lms::db
long long _fileSize{};
Wt::WDateTime _fileLastWrite;
Wt::WDateTime _fileAdded;
bool _hasCover{};
std::string _trackMBID;
std::string _recordingMBID;
std::string _copyright;
@@ -384,6 +385,7 @@ namespace lms::db
Wt::Dbo::collection<Wt::Dbo::ptr<TrackArtistLink>> _trackArtistLinks;
Wt::Dbo::collection<Wt::Dbo::ptr<Cluster>> _clusters;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackLyrics>> _trackLyrics;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackEmbeddedImageLink>> _embeddedImageLinks;
};
namespace Debug
@@ -0,0 +1,133 @@
/*
* Copyright (C) 2024 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <optional>
#include <string>
#include <string_view>
#include <Wt/Dbo/Dbo.h>
#include "database/Object.hpp"
#include "database/ReleaseId.hpp"
#include "database/TrackEmbeddedImageId.hpp"
#include "database/TrackId.hpp"
#include "database/TrackListId.hpp"
#include "database/Types.hpp"
namespace lms::db
{
class TrackEmbeddedImageLink;
class Session;
class Track;
class TrackEmbeddedImage final : public Object<TrackEmbeddedImage, TrackEmbeddedImageId>
{
public:
TrackEmbeddedImage() = default;
struct FindParameters
{
std::optional<Range> range;
TrackId track;
ReleaseId release;
TrackListId trackList;
std::optional<bool> isPreferred;
TrackEmbeddedImageSortMethod sortMethod{ TrackEmbeddedImageSortMethod::None };
FindParameters& setRange(std::optional<Range> _range)
{
range = _range;
return *this;
}
FindParameters& setTrack(TrackId _track)
{
track = _track;
return *this;
}
FindParameters& setRelease(ReleaseId _release)
{
release = _release;
return *this;
}
FindParameters& setTrackList(TrackListId _trackList)
{
trackList = _trackList;
return *this;
}
FindParameters& setIsPreferred(std::optional<bool> _isPreferred)
{
isPreferred = _isPreferred;
return *this;
}
FindParameters& setSortMethod(TrackEmbeddedImageSortMethod _sortMethod)
{
sortMethod = _sortMethod;
return *this;
}
};
// find
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackEmbeddedImageId id);
static void find(Session& session, const FindParameters& params, const std::function<void(const pointer&)>& func);
static pointer find(Session& session, std::size_t size, ImageHashType hash);
static RangeResults<TrackEmbeddedImageId> findOrphanIds(Session& session, std::optional<Range> range);
// getters
ImageHashType getHash() const { return _hash; }
std::size_t getSize() const { return _size; }
std::size_t getWidth() const { return _width; }
std::size_t getHeight() const { return _height; }
std::string_view getMimeType() const { return _mimeType; }
// setters
void setHash(ImageHashType hash) { _hash = hash; }
void setSize(std::size_t size) { _size = static_cast<int>(size); }
void setWidth(std::size_t width) { _width = static_cast<int>(width); }
void setHeight(std::size_t height) { _height = static_cast<int>(height); }
void setMimeType(std::string_view mimeType) { _mimeType = mimeType; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _hash, "hash");
Wt::Dbo::field(a, _size, "size");
Wt::Dbo::field(a, _width, "width");
Wt::Dbo::field(a, _height, "height");
Wt::Dbo::field(a, _mimeType, "mime_type");
Wt::Dbo::hasMany(a, _links, Wt::Dbo::ManyToOne, "track_embedded_image");
}
private:
friend class Session;
static pointer create(Session& session);
ImageHashType _hash;
int _size{};
int _width{};
int _height{};
std::string _mimeType;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackEmbeddedImageLink>> _links;
};
} // namespace lms::db
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2022 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(TrackEmbeddedImageId)
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2024 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <string_view>
#include <Wt/Dbo/Dbo.h>
#include "database/Object.hpp"
#include "database/TrackEmbeddedImageLinkId.hpp"
#include "database/Types.hpp"
namespace lms::db
{
class TrackEmbeddedImage;
class Session;
class Track;
class TrackEmbeddedImageLink final : public Object<TrackEmbeddedImageLink, TrackEmbeddedImageLinkId>
{
public:
TrackEmbeddedImageLink() = default;
using HashType = std::uint64_t;
// find
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackEmbeddedImageLinkId id);
// getters
ObjectPtr<Track> getTrack() const;
ObjectPtr<TrackEmbeddedImage> getImage() const;
std::size_t getIndex() const { return _index; }
bool isPreferred() const { return _isPreferred; }
ImageType getType() const { return _type; }
std::string_view getDescription() const { return _description; }
// setters
void setIndex(std::size_t index) { _index = static_cast<int>(index); }
void setIsPreferred(bool isPreferred) { _isPreferred = isPreferred; }
void setType(ImageType type) { _type = type; }
void setDescription(std::string_view description) { _description = description; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _index, "index");
Wt::Dbo::field(a, _isPreferred, "is_preferred");
Wt::Dbo::field(a, _type, "type");
Wt::Dbo::field(a, _description, "description");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _image, "track_embedded_image", Wt::Dbo::OnDeleteCascade);
}
private:
TrackEmbeddedImageLink(ObjectPtr<Track> track, ObjectPtr<TrackEmbeddedImage> image);
friend class Session;
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackEmbeddedImage> image);
int _index{}; // index within the track
bool _isPreferred{};
ImageType _type{ ImageType::Unknown };
std::string _description;
Wt::Dbo::ptr<Track> _track;
Wt::Dbo::ptr<TrackEmbeddedImage> _image;
};
} // namespace lms::db
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2022 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(TrackEmbeddedImageLinkId)
@@ -26,6 +26,7 @@
#include <Wt/WDate.h>
#include "core/Exception.hpp"
#include "core/TaggedType.hpp"
namespace lms::db
{
@@ -136,6 +137,8 @@ namespace lms::db
Name,
};
using ImageHashType = core::TaggedType<class ImageHash, std::uint64_t>;
enum class LabelSortMethod
{
None,
@@ -165,6 +168,12 @@ namespace lms::db
Name,
};
enum class TrackEmbeddedImageSortMethod
{
None,
FrontCoverAndSize,
};
enum class TrackListSortMethod
{
None,
@@ -194,6 +203,32 @@ namespace lms::db
EmbeddedFirst,
};
enum class ImageType
{
Unknown = 0,
Other = 1,
FileIcon = 2,
OtherFileIcon = 3,
FrontCover = 4,
BackCover = 5,
LeafletPage = 6,
Media = 7,
LeadArtist = 8,
Artist = 9,
Conductor = 10,
Band = 11,
Composer = 12,
Lyricist = 13,
RecordingLocation = 14,
DuringRecording = 15,
DuringPerformance = 16,
MovieScreenCapture = 17,
ColouredFish = 18,
Illustration = 19,
BandLogo = 20,
PublisherLogo = 21
};
enum class TrackArtistLinkType
{
Artist = 0, // regular track artist
+1
View File
@@ -19,6 +19,7 @@ add_executable(test-database
StarredTrack.cpp
Track.cpp
TrackBookmark.cpp
TrackEmbeddedImage.cpp
TrackFeatures.cpp
TrackList.cpp
TrackLyrics.cpp
+4
View File
@@ -32,6 +32,8 @@
#include "database/StarredArtist.hpp"
#include "database/StarredRelease.hpp"
#include "database/StarredTrack.hpp"
#include "database/TrackEmbeddedImage.hpp"
#include "database/TrackEmbeddedImageLink.hpp"
#include "database/TrackLyrics.hpp"
#include "database/UIState.hpp"
#include "database/User.hpp"
@@ -344,6 +346,8 @@ VALUES
EXPECT_FALSE(Cluster::find(session, ClusterId{}));
EXPECT_FALSE(ClusterType::find(session, ClusterTypeId{}));
EXPECT_FALSE(Directory::find(session, DirectoryId{}));
EXPECT_FALSE(TrackEmbeddedImage::find(session, TrackEmbeddedImageId{}));
EXPECT_FALSE(TrackEmbeddedImageLink::find(session, TrackEmbeddedImageLinkId{}));
EXPECT_FALSE(Image::find(session, ImageId{}));
EXPECT_FALSE(Label::find(session, LabelId{}));
EXPECT_FALSE(Listen::find(session, ListenId{}));
@@ -0,0 +1,283 @@
/*
* Copyright (C) 2024 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits>
#include "database/TrackEmbeddedImage.hpp"
#include "database/TrackEmbeddedImageLink.hpp"
#include "database/Types.hpp"
#include "Common.hpp"
namespace lms::db::tests
{
using ScopedTrackEmbeddedImage = ScopedEntity<db::TrackEmbeddedImage>;
using ScopedTrackEmbeddedImageLink = ScopedEntity<db::TrackEmbeddedImageLink>;
TEST_F(DatabaseFixture, TrackEmbeddedImage)
{
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackEmbeddedImage::getCount(session), 0);
}
ScopedTrackEmbeddedImage image{ session };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackEmbeddedImage::getCount(session), 1);
const TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, image.getId()) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
EXPECT_EQ(img->getHash(), db::ImageHashType{});
EXPECT_EQ(img->getSize(), 0);
EXPECT_EQ(img->getWidth(), 0);
EXPECT_EQ(img->getHeight(), 0);
EXPECT_EQ(img->getMimeType(), "");
}
{
auto transaction{ session.createWriteTransaction() };
TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, image.getId()) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
img.modify()->setHash(db::ImageHashType{ std::numeric_limits<std::uint64_t>::max() });
img.modify()->setSize(1024 * 1024);
img.modify()->setWidth(640);
img.modify()->setHeight(480);
img.modify()->setMimeType("image/jpeg");
}
{
auto transaction{ session.createReadTransaction() };
const TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, image.getId()) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
EXPECT_EQ(img->getHash(), db::ImageHashType{ std::numeric_limits<std::uint64_t>::max() });
EXPECT_EQ(img->getSize(), 1024 * 1024);
EXPECT_EQ(img->getWidth(), 640);
EXPECT_EQ(img->getHeight(), 480);
EXPECT_EQ(img->getMimeType(), "image/jpeg");
}
}
TEST_F(DatabaseFixture, TrackEmbeddedImage_findByHash)
{
ScopedTrackEmbeddedImage image{ session };
constexpr std::size_t size{ 1024 };
constexpr db::ImageHashType hash{ 42 };
{
auto transaction{ session.createWriteTransaction() };
TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, image.getId()) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
img.modify()->setHash(hash);
img.modify()->setSize(size);
}
{
auto transaction{ session.createReadTransaction() };
const TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, size, hash) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
EXPECT_EQ(image.getId(), img->getId());
}
{
auto transaction{ session.createReadTransaction() };
const TrackEmbeddedImage::pointer img{ TrackEmbeddedImage::find(session, size + 1, hash) };
EXPECT_EQ(img, TrackEmbeddedImage::pointer{});
}
}
TEST_F(DatabaseFixture, TrackEmbeddedImage_findByParams)
{
ScopedTrackEmbeddedImage image{ session };
ScopedTrack track{ session };
ScopedRelease release{ session, "MyRelease" };
ScopedTrackEmbeddedImageLink link{ session, track.lockAndGet(), image.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
TrackEmbeddedImage::FindParameters params;
params.setIsPreferred(true);
bool visited{};
TrackEmbeddedImage::find(session, params, [&](const auto&) { visited = true; });
EXPECT_FALSE(visited);
}
{
auto transaction{ session.createWriteTransaction() };
link.get().modify()->setIsPreferred(true);
}
{
auto transaction{ session.createReadTransaction() };
TrackEmbeddedImage::FindParameters params;
params.setIsPreferred(true);
bool visited{};
TrackEmbeddedImage::find(session, params, [&](const auto&) { visited = true; });
EXPECT_TRUE(visited);
}
{
auto transaction{ session.createReadTransaction() };
TrackEmbeddedImage::FindParameters params;
params.setIsPreferred(true);
params.setRelease(release.getId());
bool visited{};
TrackEmbeddedImage::find(session, params, [&](const auto&) { visited = true; });
EXPECT_FALSE(visited);
}
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createReadTransaction() };
TrackEmbeddedImage::FindParameters params;
params.setIsPreferred(true);
params.setRelease(release.getId());
params.setSortMethod(TrackEmbeddedImageSortMethod::FrontCoverAndSize);
bool visited{};
TrackEmbeddedImage::find(session, params, [&](const auto&) { visited = true; });
EXPECT_TRUE(visited);
}
{
auto transaction{ session.createReadTransaction() };
TrackEmbeddedImage::FindParameters params;
params.setIsPreferred(true);
params.setTrack(track.getId());
params.setSortMethod(TrackEmbeddedImageSortMethod::FrontCoverAndSize);
bool visited{};
TrackEmbeddedImage::find(session, params, [&](const auto&) { visited = true; });
EXPECT_TRUE(visited);
}
}
TEST_F(DatabaseFixture, Track_findByEmbeddedImage)
{
ScopedTrackEmbeddedImage image{ session };
ScopedTrack track{ session };
ScopedTrackEmbeddedImageLink link{ session, track.lockAndGet(), image.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
Track::FindParameters params;
params.setEmbeddedImage(image->getId());
bool visited{};
Track::find(session, params, [&](const auto&) { visited = true; });
EXPECT_TRUE(visited);
}
}
TEST_F(DatabaseFixture, TrackEmbeddedImage_findOrphans)
{
ScopedTrackEmbeddedImage image{ session };
{
auto transaction{ session.createReadTransaction() };
auto orphans{ TrackEmbeddedImage::findOrphanIds(session, std::nullopt) };
ASSERT_EQ(orphans.results.size(), 1);
EXPECT_EQ(orphans.results[0], image.getId());
}
{
ScopedTrack track{ session };
ScopedTrackEmbeddedImageLink link{ session, track.lockAndGet(), image.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
auto orphans{ TrackEmbeddedImage::findOrphanIds(session, std::nullopt) };
ASSERT_EQ(orphans.results.size(), 0);
}
}
{
auto transaction{ session.createReadTransaction() };
auto orphans{ TrackEmbeddedImage::findOrphanIds(session, std::nullopt) };
ASSERT_EQ(orphans.results.size(), 1);
EXPECT_EQ(orphans.results[0], image.getId());
}
}
TEST_F(DatabaseFixture, TrackEmbeddedImageLink)
{
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackEmbeddedImage::getCount(session), 0);
}
ScopedTrack track{ session };
ScopedTrackEmbeddedImage image{ session };
ScopedTrackEmbeddedImageLink imageLink{ session, track.lockAndGet(), image.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackEmbeddedImageLink::getCount(session), 1);
const TrackEmbeddedImageLink::pointer link{ TrackEmbeddedImageLink::find(session, imageLink.getId()) };
ASSERT_NE(link, TrackEmbeddedImageLink::pointer{});
EXPECT_EQ(link->getIndex(), 0);
EXPECT_EQ(link->getType(), ImageType::Unknown);
EXPECT_EQ(link->getDescription(), "");
EXPECT_EQ(link->getTrack(), track.get());
EXPECT_EQ(link->getImage(), image.get());
}
{
auto transaction{ session.createWriteTransaction() };
TrackEmbeddedImageLink::pointer link{ TrackEmbeddedImageLink::find(session, imageLink.getId()) };
ASSERT_NE(link, TrackEmbeddedImage::pointer{});
link.modify()->setIndex(2);
link.modify()->setType(ImageType::FrontCover);
link.modify()->setDescription("MyDesc");
}
{
auto transaction{ session.createReadTransaction() };
const TrackEmbeddedImageLink::pointer img{ TrackEmbeddedImageLink::find(session, imageLink.getId()) };
ASSERT_NE(img, TrackEmbeddedImage::pointer{});
EXPECT_EQ(img->getIndex(), 2);
EXPECT_EQ(img->getType(), ImageType::FrontCover);
EXPECT_EQ(img->getDescription(), "MyDesc");
}
}
} // namespace lms::db::tests