diff --git a/approot/messages.xml b/approot/messages.xml
index 2cfb7606..c42f4514 100644
--- a/approot/messages.xml
+++ b/approot/messages.xml
@@ -110,6 +110,7 @@
Scheduled on {1}
Scanning: step {1}/{2}
Associating artist images: {1}%...
+Associating release images: {1}%...
Checking for duplicate files... {1} files
Checking for removed files... {1}%
Compacting database...
diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml
index 1f216c86..d160d292 100644
--- a/approot/messages_fr.xml
+++ b/approot/messages_fr.xml
@@ -110,6 +110,7 @@
Planifié le {1}
En cours de scan : étape {1}/{2}
Association des images des artistes: {1}%...
+Association des images des albums: {1}%...
Vérification des fichiers dupliqués... {1} fichiers
Vérification des fichiers supprimés... {1}%
Compactage de la base de données...
diff --git a/approot/messages_it.xml b/approot/messages_it.xml
index a1daeb59..30127191 100644
--- a/approot/messages_it.xml
+++ b/approot/messages_it.xml
@@ -110,6 +110,7 @@
Pianificato il {1}
Scansione: passo {1}/{2}
Associando immagini degli artisti: {1}%...
+Associando immagini degli album: {1}%...
Controllo duplicati... {1} files
Controllo file... {1}%
Compattazione del database...
diff --git a/approot/messages_pl.xml b/approot/messages_pl.xml
index a28bcb89..90c1c269 100644
--- a/approot/messages_pl.xml
+++ b/approot/messages_pl.xml
@@ -119,6 +119,7 @@
Zaplanowano na {1}
Skanowanie: krok {1}/{2}
Kojarzenie obrazów artystów: {1}%...
+Kojarzenie obrazów albumów: {1}%...
Sprawdzanie duplikatów... {1} plik
Sprawdzanie duplikatów... {1} pliki
diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml
index 97f1b500..62206fe4 100644
--- a/approot/messages_zh.xml
+++ b/approot/messages_zh.xml
@@ -114,6 +114,7 @@
+
检索文件中: {1} 文件
从 AcousticBrainz 获取音轨特征: {1}/{2} 音轨 ({3}%)...
diff --git a/conf/lms.conf b/conf/lms.conf
index 4fcca840..82b565ce 100644
--- a/conf/lms.conf
+++ b/conf/lms.conf
@@ -74,9 +74,6 @@ api-open-subsonic-disabled-clients = ("DSub");
# Turn on this option to allow the demo account creation/use
demo = false;
-# Max external cover file size in MBytes
-cover-max-file-size = 10;
-
# Max cover cache size in MBytes
cover-max-cache-size = 30;
@@ -84,7 +81,7 @@ cover-max-cache-size = 30;
cover-jpeg-quality = 75;
# Preferred file names for covers (order is important)
-cover-preferred-file-names = ("cover", "front");
+cover-preferred-file-names = ("cover", "front", "folder", "default");
# File names for artist images (order is important)
# Note: files whose name is the artist's MBID are always searched before the names in this list. You can place the MBID files anywhere in your libraries.
diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp
index 64d1a03c..cd49ff94 100644
--- a/src/libs/database/impl/Artist.cpp
+++ b/src/libs/database/impl/Artist.cpp
@@ -379,5 +379,4 @@ namespace lms::db
{
_image = getDboPtr(image);
}
-
} // namespace lms::db
diff --git a/src/libs/database/impl/Image.cpp b/src/libs/database/impl/Image.cpp
index 62db5af7..e2a0d02b 100644
--- a/src/libs/database/impl/Image.cpp
+++ b/src/libs/database/impl/Image.cpp
@@ -23,6 +23,7 @@
#include "database/Artist.hpp"
#include "database/Directory.hpp"
+#include "database/Release.hpp"
#include "database/Session.hpp"
#include "IdTypeTraits.hpp"
@@ -40,7 +41,7 @@ namespace lms::db
if (params.directory.isValid())
query.where("i.directory_id = ?").bind(params.directory);
if (!params.fileStem.empty())
- query.where("i.stem = ?").bind(params.fileStem);
+ query.where("i.stem = ? COLLATE NOCASE").bind(params.fileStem);
return query;
}
diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp
index b75f5c4b..5d869de5 100644
--- a/src/libs/database/impl/Migration.cpp
+++ b/src/libs/database/impl/Migration.cpp
@@ -35,7 +35,7 @@ namespace lms::db
{
namespace
{
- static constexpr Version LMS_DATABASE_VERSION{ 67 };
+ static constexpr Version LMS_DATABASE_VERSION{ 68 };
}
VersionInfo::VersionInfo()
@@ -761,6 +761,54 @@ SELECT
session.getDboSession()->execute("ALTER TABLE user DROP COLUMN cur_playing_track_pos");
}
+ void migrateFromV67(Session& session)
+ {
+ // Add a ref to release in image
+ session.getDboSession()->execute(R"(CREATE TABLE "image_backup" (
+ "id" integer primary key autoincrement,
+ "version" integer not null,
+ "absolute_file_path" text not null,
+ "stem" text not null,
+ "file_last_write" text,
+ "file_size" integer not null,
+ "width" integer not null,
+ "height" integer not null,
+ "artist_id" bigint,
+ "release_id" bigint,
+ "directory_id" bigint,
+ constraint "fk_image_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
+ constraint "fk_image_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
+ constraint "fk_image_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
+))");
+
+ // Migrate data, with the new release_id field set to null
+ session.getDboSession()->execute(R"(INSERT INTO image_backup
+SELECT
+ id,
+ version,
+ absolute_file_path,
+ stem,
+ file_last_write,
+ file_size,
+ width,
+ height,
+ artist_id,
+ NULL,
+ directory_id
+ FROM image
+ )");
+ session.getDboSession()->execute("DROP TABLE image");
+ session.getDboSession()->execute("ALTER TABLE image_backup RENAME TO image");
+
+ // Changed some indexes for the image table -> remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
+ std::vector indexeNames{ utils::fetchQueryResults(session.getDboSession()->query(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
+ for (const auto& indexName : indexeNames)
+ session.getDboSession()->execute("DROP INDEX " + indexName);
+
+ // Just increment the scan version of the settings to make the next scheduled scan rescan everything
+ session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1");
+ }
+
bool doDbMigration(Session& session)
{
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -804,6 +852,7 @@ SELECT
{ 64, migrateFromV64 },
{ 65, migrateFromV65 },
{ 66, migrateFromV66 },
+ { 67, migrateFromV67 },
};
bool migrationPerformed{};
diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp
index 16f723de..687a4353 100644
--- a/src/libs/database/impl/Release.cpp
+++ b/src/libs/database/impl/Release.cpp
@@ -24,6 +24,8 @@
#include "core/ILogger.hpp"
#include "database/Artist.hpp"
#include "database/Cluster.hpp"
+#include "database/Directory.hpp"
+#include "database/Image.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/User.hpp"
@@ -540,6 +542,11 @@ namespace lms::db
return utils::fetchQueryResults(query);
}
+ ObjectPtr Release::getImage() const
+ {
+ return ObjectPtr{ _image.lock() };
+ }
+
void Release::clearLabels()
{
_labels.clear();
@@ -560,6 +567,11 @@ namespace lms::db
_releaseTypes.insert(getDboPtr(releaseType));
}
+ void Release::setImage(ObjectPtr image)
+ {
+ _image = getDboPtr(image);
+ }
+
bool Release::hasVariousArtists() const
{
// TODO optimize
@@ -665,5 +677,4 @@ namespace lms::db
return res;
}
-
} // namespace lms::db
diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp
index 14106053..c3380bcb 100644
--- a/src/libs/database/impl/Session.cpp
+++ b/src/libs/database/impl/Session.cpp
@@ -196,10 +196,11 @@ namespace lms::db
_session.execute("CREATE INDEX IF NOT EXISTS directory_media_library_idx ON directory(media_library_id)");
_session.execute("CREATE INDEX IF NOT EXISTS image_artist_idx ON image(artist_id)");
- _session.execute("CREATE INDEX IF NOT EXISTS image_directory_idx ON image(directory_id)");
+ _session.execute("CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
_session.execute("CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
_session.execute("CREATE INDEX IF NOT EXISTS image_path_idx ON image(absolute_file_path)");
- _session.execute("CREATE INDEX IF NOT EXISTS image_stem_idx ON image(stem)");
+ _session.execute("CREATE INDEX IF NOT EXISTS image_release_idx ON image(release_id)");
+ _session.execute("CREATE INDEX IF NOT EXISTS image_stem_idx ON image(stem COLLATE NOCASE)");
_session.execute("CREATE INDEX IF NOT EXISTS label_name_idx ON label(name)");
diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp
index cefcb539..c60e946d 100644
--- a/src/libs/database/impl/Track.cpp
+++ b/src/libs/database/impl/Track.cpp
@@ -157,6 +157,9 @@ 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());
+
switch (params.sortMethod)
{
case TrackSortMethod::None:
diff --git a/src/libs/database/include/database/Image.hpp b/src/libs/database/include/database/Image.hpp
index d2081835..6985e6ab 100644
--- a/src/libs/database/include/database/Image.hpp
+++ b/src/libs/database/include/database/Image.hpp
@@ -25,7 +25,6 @@
#include
#include
-#include "database/ArtistId.hpp"
#include "database/DirectoryId.hpp"
#include "database/ImageId.hpp"
#include "database/Object.hpp"
@@ -35,6 +34,7 @@ namespace lms::db
{
class Artist;
class Directory;
+ class Release;
class Session;
class Image final : public Object
@@ -87,7 +87,16 @@ namespace lms::db
void setFileSize(std::size_t fileSize) { _fileSize = fileSize; }
void setWidth(std::size_t width) { _width = width; }
void setHeight(std::size_t height) { _height = height; }
- void setArtist(const ObjectPtr& artist) { _artist = getDboPtr(artist); }
+ void setArtist(const ObjectPtr& artist)
+ {
+ _artist = getDboPtr(artist);
+ _release = nullptr;
+ }
+ void setRelease(const ObjectPtr& release)
+ {
+ _release = getDboPtr(release);
+ _artist = nullptr;
+ }
void setDirectory(const ObjectPtr& directory) { _directory = getDboPtr(directory); }
template
@@ -102,6 +111,7 @@ namespace lms::db
Wt::Dbo::field(a, _height, "height");
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
+ Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _directory, "directory", Wt::Dbo::OnDeleteCascade);
}
@@ -118,6 +128,7 @@ namespace lms::db
int _height{};
Wt::Dbo::ptr _artist;
+ Wt::Dbo::ptr _release;
Wt::Dbo::ptr _directory;
};
} // namespace lms::db
diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/database/include/database/Release.hpp
index 45db9fbe..11a22069 100644
--- a/src/libs/database/include/database/Release.hpp
+++ b/src/libs/database/include/database/Release.hpp
@@ -47,6 +47,7 @@ namespace lms::db
class Artist;
class Cluster;
class ClusterType;
+ class Image;
class Release;
class Session;
class Track;
@@ -245,6 +246,7 @@ namespace lms::db
std::vector getLabelNames() const;
std::vector getReleaseTypeNames() const;
void visitLabels(const std::function& _func) const;
+ ObjectPtr getImage() const;
// Setters
void setName(std::string_view name) { _name = name; }
@@ -258,6 +260,7 @@ namespace lms::db
void clearReleaseTypes();
void addLabel(ObjectPtr