Added an option to select how to sort albums in the artist view, fixes #498

This commit is contained in:
emeric
2024-10-04 17:39:08 +02:00
parent 0f626f7716
commit dc63e61a29
14 changed files with 188 additions and 28 deletions
+7
View File
@@ -277,18 +277,24 @@
<message id="Lms.PlayHistory.playhistory">Play History</message> <message id="Lms.PlayHistory.playhistory">Play History</message>
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.artist-release-sort-method">Method to sort artist's albums</message>
<message id="Lms.Settings.audio">Audio</message> <message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">These audio settings are local to your browser!</message> <message id="Lms.Settings.audio-settings-are-local">These audio settings are local to your browser!</message>
<message id="Lms.Settings.backend.internal">Internal</message> <message id="Lms.Settings.backend.internal">Internal</message>
<message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message>
<message id="Lms.Settings.backend.listenbrainz-token">ListenBrainz API token</message> <message id="Lms.Settings.backend.listenbrainz-token">ListenBrainz API token</message>
<message id="Lms.Settings.change-password">Change password</message> <message id="Lms.Settings.change-password">Change password</message>
<message id="Lms.Settings.date-asc">Release Date (Ascending)</message>
<message id="Lms.Settings.date-desc">Release Date (Descending)</message>
<message id="Lms.Settings.default-transcoding-output-bitrate">Default transcoding output bitrate</message> <message id="Lms.Settings.default-transcoding-output-bitrate">Default transcoding output bitrate</message>
<message id="Lms.Settings.default-transcoding-output-format">Default transcoding output format</message> <message id="Lms.Settings.default-transcoding-output-format">Default transcoding output format</message>
<message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message> <message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message>
<message id="Lms.Settings.enable-transcoding-by-default">Enable transcoding by default</message> <message id="Lms.Settings.enable-transcoding-by-default">Enable transcoding by default</message>
<message id="Lms.Settings.feedback">Feedback</message> <message id="Lms.Settings.feedback">Feedback</message>
<message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Settings</message> <message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Settings</message>
<message id="Lms.Settings.name">Album Name</message>
<message id="Lms.Settings.original-date-asc">Original Release Date (Ascending)</message>
<message id="Lms.Settings.original-date-desc">Original Release Date (Descending)</message>
<message id="Lms.Settings.password-bad">Bad password</message> <message id="Lms.Settings.password-bad">Bad password</message>
<message id="Lms.Settings.password-must-fill-old-password">Old password must be filled in</message> <message id="Lms.Settings.password-must-fill-old-password">Old password must be filled in</message>
<message id="Lms.Settings.replaygain-mode">ReplayGain mode</message> <message id="Lms.Settings.replaygain-mode">ReplayGain mode</message>
@@ -306,6 +312,7 @@
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Album artists</message> <message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Album artists</message>
<message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Track artists</message> <message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Track artists</message>
<message id="Lms.Settings.subsonic-api">Subsonic API</message> <message id="Lms.Settings.subsonic-api">Subsonic API</message>
<message id="Lms.Settings.user-interface">User Interface</message>
<message id="Lms.Settings.transcoding">Transcoding</message> <message id="Lms.Settings.transcoding">Transcoding</message>
<message id="Lms.Settings.transcoding-output-bitrate">Transcoding output bitrate</message> <message id="Lms.Settings.transcoding-output-bitrate">Transcoding output bitrate</message>
<message id="Lms.Settings.transcoding-output-format">Transcoding output format</message> <message id="Lms.Settings.transcoding-output-format">Transcoding output format</message>
+7
View File
@@ -277,18 +277,24 @@
<message id="Lms.PlayHistory.playhistory">Historique de lecture</message> <message id="Lms.PlayHistory.playhistory">Historique de lecture</message>
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.artist-release-sort-method">Méthode pour trier les albums de l'artiste</message>
<message id="Lms.Settings.audio">Audio</message> <message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">Ces paramètres audio sont locaux à votre navigateur !</message> <message id="Lms.Settings.audio-settings-are-local">Ces paramètres audio sont locaux à votre navigateur !</message>
<message id="Lms.Settings.backend.internal">Interne</message> <message id="Lms.Settings.backend.internal">Interne</message>
<message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message>
<message id="Lms.Settings.backend.listenbrainz-token">Jeton d'API ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz-token">Jeton d'API ListenBrainz</message>
<message id="Lms.Settings.change-password">Changement de mot de passe</message> <message id="Lms.Settings.change-password">Changement de mot de passe</message>
<message id="Lms.Settings.date-asc">Date de sortie (Croissant)</message>
<message id="Lms.Settings.date-desc">Date de sortie (Décroissant)</message>
<message id="Lms.Settings.default-transcoding-output-bitrate">Bitrate par défaut du transcodage</message> <message id="Lms.Settings.default-transcoding-output-bitrate">Bitrate par défaut du transcodage</message>
<message id="Lms.Settings.default-transcoding-output-format">Format par défaut du transcodage</message> <message id="Lms.Settings.default-transcoding-output-format">Format par défaut du transcodage</message>
<message id="Lms.Settings.demo-cannot-save">Impossible de sauvegarder en utilisant un compte de démo !</message> <message id="Lms.Settings.demo-cannot-save">Impossible de sauvegarder en utilisant un compte de démo !</message>
<message id="Lms.Settings.enable-transcoding-by-default">Activer par défaut le transcodage</message> <message id="Lms.Settings.enable-transcoding-by-default">Activer par défaut le transcodage</message>
<message id="Lms.Settings.feedback">Feedback</message> <message id="Lms.Settings.feedback">Feedback</message>
<message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Paramètres</message> <message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Paramètres</message>
<message id="Lms.Settings.name">Nom de l'album</message>
<message id="Lms.Settings.original-date-asc">Date de sortie originale (Croissant)</message>
<message id="Lms.Settings.original-date-desc">Date de sortie originale (Décroissant)</message>
<message id="Lms.Settings.password-bad">Mauvais mot de passe</message> <message id="Lms.Settings.password-bad">Mauvais mot de passe</message>
<message id="Lms.Settings.password-must-fill-old-password">L'ancien mot de passe doit être renseigné</message> <message id="Lms.Settings.password-must-fill-old-password">L'ancien mot de passe doit être renseigné</message>
<message id="Lms.Settings.replaygain-mode">Mode ReplayGain</message> <message id="Lms.Settings.replaygain-mode">Mode ReplayGain</message>
@@ -306,6 +312,7 @@
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Tous les artistes d'album</message> <message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Tous les artistes d'album</message>
<message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Tous les artistes de piste</message> <message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Tous les artistes de piste</message>
<message id="Lms.Settings.subsonic-api">API Subsonic</message> <message id="Lms.Settings.subsonic-api">API Subsonic</message>
<message id="Lms.Settings.user-interface">Interface utilisateur</message>
<message id="Lms.Settings.transcoding">Transcodage</message> <message id="Lms.Settings.transcoding">Transcodage</message>
<message id="Lms.Settings.transcoding-output-bitrate">Bitrate du transcodage</message> <message id="Lms.Settings.transcoding-output-bitrate">Bitrate du transcodage</message>
<message id="Lms.Settings.transcoding-output-format">Format du transcodage</message> <message id="Lms.Settings.transcoding-output-format">Format du transcodage</message>
+7
View File
@@ -277,18 +277,24 @@
<message id="Lms.PlayHistory.playhistory">Storico riproduzione</message> <message id="Lms.PlayHistory.playhistory">Storico riproduzione</message>
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.artist-release-sort-method">Metodo per ordinare gli album dell'artista</message>
<message id="Lms.Settings.audio">Audio</message> <message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">Queste impostazioni sono salvate localmente su questo dispositivo!</message> <message id="Lms.Settings.audio-settings-are-local">Queste impostazioni sono salvate localmente su questo dispositivo!</message>
<message id="Lms.Settings.backend.internal">Interno</message> <message id="Lms.Settings.backend.internal">Interno</message>
<message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message>
<message id="Lms.Settings.backend.listenbrainz-token">Token API ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz-token">Token API ListenBrainz</message>
<message id="Lms.Settings.change-password">Cambia password</message> <message id="Lms.Settings.change-password">Cambia password</message>
<message id="Lms.Settings.date-asc">Data di uscita (Crescente)</message>
<message id="Lms.Settings.date-desc">Data di uscita (Decrescente)</message>
<message id="Lms.Settings.default-transcoding-output-bitrate">Bitrate di default per la transcodifica</message> <message id="Lms.Settings.default-transcoding-output-bitrate">Bitrate di default per la transcodifica</message>
<message id="Lms.Settings.default-transcoding-output-format">Formato di default per la transcodifica</message> <message id="Lms.Settings.default-transcoding-output-format">Formato di default per la transcodifica</message>
<message id="Lms.Settings.demo-cannot-save">Non posso salvare le impostazioni utilizzando l'account demo!</message> <message id="Lms.Settings.demo-cannot-save">Non posso salvare le impostazioni utilizzando l'account demo!</message>
<message id="Lms.Settings.enable-transcoding-by-default">Abilita transcodifica di default</message> <message id="Lms.Settings.enable-transcoding-by-default">Abilita transcodifica di default</message>
<message id="Lms.Settings.feedback">Feedback</message> <message id="Lms.Settings.feedback">Feedback</message>
<message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Impostazioni</message> <message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Impostazioni</message>
<message id="Lms.Settings.name">Nome dell'album</message>
<message id="Lms.Settings.original-date-asc">Data di uscita originale (Crescente)</message>
<message id="Lms.Settings.original-date-desc">Data di uscita originale (Decrescente)</message>
<message id="Lms.Settings.password-bad">Password errata</message> <message id="Lms.Settings.password-bad">Password errata</message>
<message id="Lms.Settings.password-must-fill-old-password">Devi inserire la password attuale</message> <message id="Lms.Settings.password-must-fill-old-password">Devi inserire la password attuale</message>
<message id="Lms.Settings.replaygain-mode">Modalità ReplayGain</message> <message id="Lms.Settings.replaygain-mode">Modalità ReplayGain</message>
@@ -306,6 +312,7 @@
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artisti album</message> <message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artisti album</message>
<message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Artisti tracce</message> <message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Artisti tracce</message>
<message id="Lms.Settings.subsonic-api">Subsonic API</message> <message id="Lms.Settings.subsonic-api">Subsonic API</message>
<message id="Lms.Settings.user-interface">Interfaccia utente</message>
<message id="Lms.Settings.transcoding">Transcodifica</message> <message id="Lms.Settings.transcoding">Transcodifica</message>
<message id="Lms.Settings.transcoding-output-bitrate">Bitrate transcodifica</message> <message id="Lms.Settings.transcoding-output-bitrate">Bitrate transcodifica</message>
<message id="Lms.Settings.transcoding-output-format">Formato di transcodifica</message> <message id="Lms.Settings.transcoding-output-format">Formato di transcodifica</message>
+7
View File
@@ -303,18 +303,24 @@
<message id="Lms.PlayHistory.playhistory">Historia odtwarzania</message> <message id="Lms.PlayHistory.playhistory">Historia odtwarzania</message>
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.artist-release-sort-method">Metoda sortowania albumów artysty</message>
<message id="Lms.Settings.audio">Dźwięk</message> <message id="Lms.Settings.audio">Dźwięk</message>
<message id="Lms.Settings.audio-settings-are-local">Te ustawienia dotyczą wyłącznie tej przeglądarki!</message> <message id="Lms.Settings.audio-settings-are-local">Te ustawienia dotyczą wyłącznie tej przeglądarki!</message>
<message id="Lms.Settings.backend.internal">Wewnętrzny</message> <message id="Lms.Settings.backend.internal">Wewnętrzny</message>
<message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message> <message id="Lms.Settings.backend.listenbrainz">ListenBrainz</message>
<message id="Lms.Settings.backend.listenbrainz-token">ListenBrainz API token</message> <message id="Lms.Settings.backend.listenbrainz-token">ListenBrainz API token</message>
<message id="Lms.Settings.change-password">Zmień hasło</message> <message id="Lms.Settings.change-password">Zmień hasło</message>
<message id="Lms.Settings.date-asc">Data wydania (Rosnąco)</message>
<message id="Lms.Settings.date-desc">Data wydania (Malejąco)</message>
<message id="Lms.Settings.default-transcoding-output-bitrate">Domyślna przepływność transkodowania</message> <message id="Lms.Settings.default-transcoding-output-bitrate">Domyślna przepływność transkodowania</message>
<message id="Lms.Settings.default-transcoding-output-format">Domyślny format wyjściowy transkodowania</message> <message id="Lms.Settings.default-transcoding-output-format">Domyślny format wyjściowy transkodowania</message>
<message id="Lms.Settings.demo-cannot-save">Nie można zapisywać na koncie demonstracyjnym!</message> <message id="Lms.Settings.demo-cannot-save">Nie można zapisywać na koncie demonstracyjnym!</message>
<message id="Lms.Settings.enable-transcoding-by-default">Aktywuj domyślnie transkodowanie</message> <message id="Lms.Settings.enable-transcoding-by-default">Aktywuj domyślnie transkodowanie</message>
<message id="Lms.Settings.feedback">Feedback</message> <message id="Lms.Settings.feedback">Feedback</message>
<message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Ustawienia</message> <message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> Ustawienia</message>
<message id="Lms.Settings.name">Nazwa albumu</message>
<message id="Lms.Settings.original-date-asc">Oryginalna data wydania (Rosnąco)</message>
<message id="Lms.Settings.original-date-desc">Oryginalna data wydania (Malejąco)</message>
<message id="Lms.Settings.password-bad">Błędne hasło</message> <message id="Lms.Settings.password-bad">Błędne hasło</message>
<message id="Lms.Settings.password-must-fill-old-password">Należy podać stare hasło</message> <message id="Lms.Settings.password-must-fill-old-password">Należy podać stare hasło</message>
<message id="Lms.Settings.replaygain-mode">Tryb ReplayGain</message> <message id="Lms.Settings.replaygain-mode">Tryb ReplayGain</message>
@@ -332,6 +338,7 @@
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artyści z albumów</message> <message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artyści z albumów</message>
<message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Artyści ze ścieżek</message> <message id="Lms.Settings.subsonic-artist-list-mode.track-artists">Artyści ze ścieżek</message>
<message id="Lms.Settings.subsonic-api">Subsonic API</message> <message id="Lms.Settings.subsonic-api">Subsonic API</message>
<message id="Lms.Settings.user-interface">Interfejs użytkownika</message>
<message id="Lms.Settings.transcoding">Transkodowanie</message> <message id="Lms.Settings.transcoding">Transkodowanie</message>
<message id="Lms.Settings.transcoding-output-bitrate">Przepływność transkodowania</message> <message id="Lms.Settings.transcoding-output-bitrate">Przepływność transkodowania</message>
<message id="Lms.Settings.transcoding-output-format">Format wyjściowy transkodowania</message> <message id="Lms.Settings.transcoding-output-format">Format wyjściowy transkodowania</message>
+10 -2
View File
@@ -80,6 +80,7 @@
<message id="Lms.Admin.Database.update-period">更新周期</message> <message id="Lms.Admin.Database.update-period">更新周期</message>
<message id="Lms.Admin.Database.update-start-time">更新开始时间</message> <message id="Lms.Admin.Database.update-start-time">更新开始时间</message>
<message id="Lms.Admin.Database.weekly">每周</message> <message id="Lms.Admin.Database.weekly">每周</message>
@@ -110,11 +111,11 @@
<message id="Lms.Admin.ScannerController.status-scheduled">计划于 {1}</message> <message id="Lms.Admin.ScannerController.status-scheduled">计划于 {1}</message>
<message id="Lms.Admin.ScannerController.status-in-progress">扫描中: 阶段 {1}/{2}</message> <message id="Lms.Admin.ScannerController.status-in-progress">扫描中: 阶段 {1}/{2}</message>
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">检查文件中... {1}%</message> <message id="Lms.Admin.ScannerController.step-checking-for-removed-files">检查文件中... {1}%</message>
<message id="Lms.Admin.ScannerController.step-discovering-files">检索文件中: {1} 文件</message> <message id="Lms.Admin.ScannerController.step-discovering-files">检索文件中: {1} 文件</message>
<message id="Lms.Admin.ScannerController.step-fetching-track-features">从 AcousticBrainz 获取音轨特征: {1}/{2} 音轨 ({3}%)...</message> <message id="Lms.Admin.ScannerController.step-fetching-track-features">从 AcousticBrainz 获取音轨特征: {1}/{2} 音轨 ({3}%)...</message>
@@ -277,6 +278,7 @@
<message id="Lms.PlayHistory.playhistory">播放历史</message> <message id="Lms.PlayHistory.playhistory">播放历史</message>
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.audio">音频</message> <message id="Lms.Settings.audio">音频</message>
<message id="Lms.Settings.audio-settings-are-local">这些音频设置仅保存在您的本地浏览器!</message> <message id="Lms.Settings.audio-settings-are-local">这些音频设置仅保存在您的本地浏览器!</message>
@@ -285,10 +287,15 @@
<message id="Lms.Settings.change-password">更改密码</message> <message id="Lms.Settings.change-password">更改密码</message>
<message id="Lms.Settings.demo-cannot-save">使用演示账号时不能保存!</message> <message id="Lms.Settings.demo-cannot-save">使用演示账号时不能保存!</message>
<message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> 设置</message> <message id="Lms.Settings.menu-settings"><i class="fa fa-fw fa-cog" aria-hidden="true"></i> 设置</message>
<message id="Lms.Settings.password-bad">无效密码</message> <message id="Lms.Settings.password-bad">无效密码</message>
<message id="Lms.Settings.password-must-fill-old-password">必须填写旧密码</message> <message id="Lms.Settings.password-must-fill-old-password">必须填写旧密码</message>
<message id="Lms.Settings.replaygain-mode">ReplayGain 模式</message> <message id="Lms.Settings.replaygain-mode">ReplayGain 模式</message>
@@ -306,6 +313,7 @@
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">专辑歌手</message> <message id="Lms.Settings.subsonic-artist-list-mode.release-artists">专辑歌手</message>
<message id="Lms.Settings.subsonic-artist-list-mode.track-artists">音轨歌手</message> <message id="Lms.Settings.subsonic-artist-list-mode.track-artists">音轨歌手</message>
<message id="Lms.Settings.subsonic-api">Subsonic 应用程序接口</message> <message id="Lms.Settings.subsonic-api">Subsonic 应用程序接口</message>
<message id="Lms.Settings.transcoding">转码</message> <message id="Lms.Settings.transcoding">转码</message>
<message id="Lms.Settings.transcoding-output-bitrate">转码比特率</message> <message id="Lms.Settings.transcoding-output-bitrate">转码比特率</message>
<message id="Lms.Settings.transcoding-output-format">转码格式</message> <message id="Lms.Settings.transcoding-output-format">转码格式</message>
+14 -3
View File
@@ -5,10 +5,21 @@
<message id="Lms.Settings.template"> <message id="Lms.Settings.template">
<form class="row g-3"> <form class="row g-3">
<legend>${tr:Lms.Settings.audio}</legend> <legend>${tr:Lms.Settings.user-interface}</legend>
<div class="alert alert-info"> <div class="col-12">
${tr:Lms.Settings.audio-settings-are-local} <label class="form-label" for="${id:artist-release-sort-method}">
${tr:Lms.Settings.artist-release-sort-method}
</label>
${artist-release-sort-method class="form-control"}
<div class="invalid-feedback">
${artist-release-sort-method-info}
</div>
</div> </div>
<legend>${tr:Lms.Settings.audio}
<button type="button" class="btn btn-sm p-0" data-bs-toggle="tooltip" data-bs-placement="right" title="${tr:Lms.Settings.audio-settings-are-local}">
<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i>
</button>
</legend>
<div class="col-lg-4"> <div class="col-lg-4">
<label class="form-label" for="${id:transcoding-mode}"> <label class="form-label" for="${id:transcoding-mode}">
${tr:Lms.Settings.transcoding-mode} ${tr:Lms.Settings.transcoding-mode}
+8 -1
View File
@@ -35,7 +35,7 @@ namespace lms::db
{ {
namespace namespace
{ {
static constexpr Version LMS_DATABASE_VERSION{ 69 }; static constexpr Version LMS_DATABASE_VERSION{ 70 };
} }
VersionInfo::VersionInfo() VersionInfo::VersionInfo()
@@ -872,6 +872,12 @@ SELECT
session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1"); session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1");
} }
void migrateFromV69(Session& session)
{
// Add a field in UI settings
session.getDboSession()->execute("ALTER TABLE user ADD COLUMN ui_artist_release_sort_method NOT NULL DEFAULT 7"); // 7 = ReleaseSortMethod::OriginalDateDesc
}
bool doDbMigration(Session& session) bool doDbMigration(Session& session)
{ {
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" }; constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -917,6 +923,7 @@ SELECT
{ 66, migrateFromV66 }, { 66, migrateFromV66 },
{ 67, migrateFromV67 }, { 67, migrateFromV67 },
{ 68, migrateFromV68 }, { 68, migrateFromV68 },
{ 69, migrateFromV69 },
}; };
bool migrationPerformed{}; bool migrationPerformed{};
+5 -1
View File
@@ -75,6 +75,7 @@ namespace lms::db
static inline constexpr TranscodingOutputFormat defaultSubsonicTranscodingOutputFormat{ TranscodingOutputFormat::OGG_OPUS }; static inline constexpr TranscodingOutputFormat defaultSubsonicTranscodingOutputFormat{ TranscodingOutputFormat::OGG_OPUS };
static inline constexpr Bitrate defaultSubsonicTranscodingOutputBitrate{ 128000 }; static inline constexpr Bitrate defaultSubsonicTranscodingOutputBitrate{ 128000 };
static inline constexpr UITheme defaultUITheme{ UITheme::Dark }; static inline constexpr UITheme defaultUITheme{ UITheme::Dark };
static inline constexpr ReleaseSortMethod _defaultUIArtistReleaseSortMethod{ ReleaseSortMethod::OriginalDateDesc };
static inline constexpr SubsonicArtistListMode defaultSubsonicArtistListMode{ SubsonicArtistListMode::AllArtists }; static inline constexpr SubsonicArtistListMode defaultSubsonicArtistListMode{ SubsonicArtistListMode::AllArtists };
static inline constexpr ScrobblingBackend defaultScrobblingBackend{ ScrobblingBackend::Internal }; static inline constexpr ScrobblingBackend defaultScrobblingBackend{ ScrobblingBackend::Internal };
static inline constexpr FeedbackBackend defaultFeedbackBackend{ FeedbackBackend::Internal }; static inline constexpr FeedbackBackend defaultFeedbackBackend{ FeedbackBackend::Internal };
@@ -106,6 +107,7 @@ namespace lms::db
void setSubsonicDefaultTranscodintOutputFormat(TranscodingOutputFormat encoding) { _subsonicDefaultTranscodingOutputFormat = encoding; } void setSubsonicDefaultTranscodintOutputFormat(TranscodingOutputFormat encoding) { _subsonicDefaultTranscodingOutputFormat = encoding; }
void setSubsonicDefaultTranscodingOutputBitrate(Bitrate bitrate); void setSubsonicDefaultTranscodingOutputBitrate(Bitrate bitrate);
void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; } void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; }
void setUIArtistReleaseSortMethod(ReleaseSortMethod method) { _uiArtistReleaseSortMethod = method; }
void clearAuthTokens(); void clearAuthTokens();
void setSubsonicArtistListMode(SubsonicArtistListMode mode) { _subsonicArtistListMode = mode; } void setSubsonicArtistListMode(SubsonicArtistListMode mode) { _subsonicArtistListMode = mode; }
void setFeedbackBackend(FeedbackBackend feedbackBackend) { _feedbackBackend = feedbackBackend; } void setFeedbackBackend(FeedbackBackend feedbackBackend) { _feedbackBackend = feedbackBackend; }
@@ -120,6 +122,7 @@ namespace lms::db
TranscodingOutputFormat getSubsonicDefaultTranscodingOutputFormat() const { return _subsonicDefaultTranscodingOutputFormat; } TranscodingOutputFormat getSubsonicDefaultTranscodingOutputFormat() const { return _subsonicDefaultTranscodingOutputFormat; }
Bitrate getSubsonicDefaultTranscodingOutputBitrate() const { return _subsonicDefaultTranscodingOutputBitrate; } Bitrate getSubsonicDefaultTranscodingOutputBitrate() const { return _subsonicDefaultTranscodingOutputBitrate; }
UITheme getUITheme() const { return _uiTheme; } UITheme getUITheme() const { return _uiTheme; }
ReleaseSortMethod getUIArtistReleaseSortMethod() const { return _uiArtistReleaseSortMethod; }
SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; } SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; }
FeedbackBackend getFeedbackBackend() const { return _feedbackBackend; } FeedbackBackend getFeedbackBackend() const { return _feedbackBackend; }
ScrobblingBackend getScrobblingBackend() const { return _scrobblingBackend; } ScrobblingBackend getScrobblingBackend() const { return _scrobblingBackend; }
@@ -138,6 +141,7 @@ namespace lms::db
Wt::Dbo::field(a, _subsonicDefaultTranscodingOutputBitrate, "subsonic_default_transcode_bitrate"); Wt::Dbo::field(a, _subsonicDefaultTranscodingOutputBitrate, "subsonic_default_transcode_bitrate");
Wt::Dbo::field(a, _subsonicArtistListMode, "subsonic_artist_list_mode"); Wt::Dbo::field(a, _subsonicArtistListMode, "subsonic_artist_list_mode");
Wt::Dbo::field(a, _uiTheme, "ui_theme"); Wt::Dbo::field(a, _uiTheme, "ui_theme");
Wt::Dbo::field(a, _uiArtistReleaseSortMethod, "ui_artist_release_sort_method");
Wt::Dbo::field(a, _feedbackBackend, "feedback_backend"); Wt::Dbo::field(a, _feedbackBackend, "feedback_backend");
Wt::Dbo::field(a, _scrobblingBackend, "scrobbling_backend"); Wt::Dbo::field(a, _scrobblingBackend, "scrobbling_backend");
Wt::Dbo::field(a, _listenbrainzToken, "listenbrainz_token"); Wt::Dbo::field(a, _listenbrainzToken, "listenbrainz_token");
@@ -156,6 +160,7 @@ namespace lms::db
std::string _passwordHash; std::string _passwordHash;
Wt::WDateTime _lastLogin; Wt::WDateTime _lastLogin;
UITheme _uiTheme{ defaultUITheme }; UITheme _uiTheme{ defaultUITheme };
ReleaseSortMethod _uiArtistReleaseSortMethod{ _defaultUIArtistReleaseSortMethod };
FeedbackBackend _feedbackBackend{ defaultFeedbackBackend }; FeedbackBackend _feedbackBackend{ defaultFeedbackBackend };
ScrobblingBackend _scrobblingBackend{ defaultScrobblingBackend }; ScrobblingBackend _scrobblingBackend{ defaultScrobblingBackend };
std::string _listenbrainzToken; // Musicbrainz Identifier std::string _listenbrainzToken; // Musicbrainz Identifier
@@ -172,5 +177,4 @@ namespace lms::db
Wt::Dbo::collection<Wt::Dbo::ptr<AuthToken>> _authTokens; Wt::Dbo::collection<Wt::Dbo::ptr<AuthToken>> _authTokens;
Wt::Dbo::collection<Wt::Dbo::ptr<UIState>> _uiStates; Wt::Dbo::collection<Wt::Dbo::ptr<UIState>> _uiStates;
}; };
} // namespace lms::db } // namespace lms::db
+1
View File
@@ -12,6 +12,7 @@ add_executable(lms
ui/PlayQueue.cpp ui/PlayQueue.cpp
ui/SettingsView.cpp ui/SettingsView.cpp
ui/State.cpp ui/State.cpp
ui/Tooltip.cpp
ui/Utils.cpp ui/Utils.cpp
ui/admin/InitWizardView.cpp ui/admin/InitWizardView.cpp
ui/admin/MediaLibrariesView.cpp ui/admin/MediaLibrariesView.cpp
+50 -18
View File
@@ -37,6 +37,7 @@
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "MediaPlayer.hpp" #include "MediaPlayer.hpp"
#include "Tooltip.hpp"
#include "common/DoubleValidator.hpp" #include "common/DoubleValidator.hpp"
#include "common/MandatoryValidator.hpp" #include "common/MandatoryValidator.hpp"
#include "common/PasswordValidator.hpp" #include "common/PasswordValidator.hpp"
@@ -51,6 +52,7 @@ namespace lms::ui
{ {
public: public:
// Associate each field with a unique string literal. // Associate each field with a unique string literal.
static inline const Field ArtistReleaseSortMethodField{ "artist-release-sort-method" };
static inline const Field TranscodingModeField{ "transcoding-mode" }; static inline const Field TranscodingModeField{ "transcoding-mode" };
static inline const Field TranscodeFormatField{ "transcoding-output-format" }; static inline const Field TranscodeFormatField{ "transcoding-output-format" };
static inline const Field TranscodeBitrateField{ "transcoding-output-bitrate" }; static inline const Field TranscodeBitrateField{ "transcoding-output-bitrate" };
@@ -68,10 +70,11 @@ namespace lms::ui
static inline const Field PasswordField{ "password" }; static inline const Field PasswordField{ "password" };
static inline const Field PasswordConfirmField{ "password-confirm" }; static inline const Field PasswordConfirmField{ "password-confirm" };
using ArtistReleaseSortMethodModel = ValueStringModel<db::ReleaseSortMethod>;
using TranscodingModeModel = ValueStringModel<MediaPlayer::Settings::Transcoding::Mode>; using TranscodingModeModel = ValueStringModel<MediaPlayer::Settings::Transcoding::Mode>;
using ReplayGainModeModel = ValueStringModel<MediaPlayer::Settings::ReplayGain::Mode>; using ReplayGainModeModel = ValueStringModel<MediaPlayer::Settings::ReplayGain::Mode>;
using FeedbackBackendModel = ValueStringModel<FeedbackBackend>; using FeedbackBackendModel = ValueStringModel<db::FeedbackBackend>;
using ScrobblingBackendModel = ValueStringModel<ScrobblingBackend>; using ScrobblingBackendModel = ValueStringModel<db::ScrobblingBackend>;
SettingsModel(auth::IPasswordService* authPasswordService, bool withOldPassword) SettingsModel(auth::IPasswordService* authPasswordService, bool withOldPassword)
: _authPasswordService{ authPasswordService } : _authPasswordService{ authPasswordService }
@@ -79,6 +82,7 @@ namespace lms::ui
{ {
initializeModels(); initializeModels();
addField(ArtistReleaseSortMethodField);
addField(TranscodingModeField); addField(TranscodingModeField);
addField(TranscodeBitrateField); addField(TranscodeBitrateField);
addField(TranscodeFormatField); addField(TranscodeFormatField);
@@ -106,6 +110,7 @@ namespace lms::ui
addField(PasswordConfirmField); addField(PasswordConfirmField);
} }
setValidator(ArtistReleaseSortMethodField, createMandatoryValidator());
setValidator(TranscodingModeField, createMandatoryValidator()); setValidator(TranscodingModeField, createMandatoryValidator());
setValidator(TranscodeBitrateField, createMandatoryValidator()); setValidator(TranscodeBitrateField, createMandatoryValidator());
setValidator(TranscodeFormatField, createMandatoryValidator()); setValidator(TranscodeFormatField, createMandatoryValidator());
@@ -122,6 +127,7 @@ namespace lms::ui
loadData(); loadData();
} }
std::shared_ptr<ArtistReleaseSortMethodModel> getArtistReleaseSortMethodModel() { return _artistReleaseSortMethodModel; }
std::shared_ptr<TranscodingModeModel> getTranscodingModeModel() { return _transcodingModeModeModel; } std::shared_ptr<TranscodingModeModel> getTranscodingModeModel() { return _transcodingModeModeModel; }
std::shared_ptr<Wt::WAbstractItemModel> getTranscodingOutputBitrateModel() { return _transcodingOutputBitrateModel; } std::shared_ptr<Wt::WAbstractItemModel> getTranscodingOutputBitrateModel() { return _transcodingOutputBitrateModel; }
std::shared_ptr<Wt::WAbstractItemModel> getTranscodingOutputFormatModel() { return _transcodingOutputFormatModel; } std::shared_ptr<Wt::WAbstractItemModel> getTranscodingOutputFormatModel() { return _transcodingOutputFormatModel; }
@@ -136,6 +142,12 @@ namespace lms::ui
User::pointer user{ LmsApp->getUser() }; User::pointer user{ LmsApp->getUser() };
{
auto artistReleaseSortMethodRow{ _artistReleaseSortMethodModel->getRowFromString(valueText(ArtistReleaseSortMethodField)) };
if (artistReleaseSortMethodRow)
user.modify()->setUIArtistReleaseSortMethod(_artistReleaseSortMethodModel->getValue(*artistReleaseSortMethodRow));
}
{ {
MediaPlayer::Settings settings; MediaPlayer::Settings settings;
@@ -204,6 +216,12 @@ namespace lms::ui
User::pointer user{ LmsApp->getUser() }; User::pointer user{ LmsApp->getUser() };
{
auto artistReleaseSortMethodRow{ _artistReleaseSortMethodModel->getRowFromValue(user->getUIArtistReleaseSortMethod()) };
if (artistReleaseSortMethodRow)
setValue(ArtistReleaseSortMethodField, _artistReleaseSortMethodModel->getString(*artistReleaseSortMethodRow));
}
{ {
const auto settings{ *LmsApp->getMediaPlayer().getSettings() }; const auto settings{ *LmsApp->getMediaPlayer().getSettings() };
@@ -315,6 +333,13 @@ namespace lms::ui
private: private:
void initializeModels() void initializeModels()
{ {
_artistReleaseSortMethodModel = std::make_shared<ArtistReleaseSortMethodModel>();
_artistReleaseSortMethodModel->add(Wt::WString::tr("Lms.Settings.date-asc"), db::ReleaseSortMethod::DateAsc);
_artistReleaseSortMethodModel->add(Wt::WString::tr("Lms.Settings.date-desc"), db::ReleaseSortMethod::DateDesc);
_artistReleaseSortMethodModel->add(Wt::WString::tr("Lms.Settings.original-date-asc"), db::ReleaseSortMethod::OriginalDate);
_artistReleaseSortMethodModel->add(Wt::WString::tr("Lms.Settings.original-date-desc"), db::ReleaseSortMethod::OriginalDateDesc);
_artistReleaseSortMethodModel->add(Wt::WString::tr("Lms.Settings.name"), db::ReleaseSortMethod::Name);
_transcodingModeModeModel = std::make_shared<TranscodingModeModel>(); _transcodingModeModeModel = std::make_shared<TranscodingModeModel>();
_transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcoding-mode.always"), MediaPlayer::Settings::Transcoding::Mode::Always); _transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcoding-mode.always"), MediaPlayer::Settings::Transcoding::Mode::Always);
_transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcoding-mode.never"), MediaPlayer::Settings::Transcoding::Mode::Never); _transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcoding-mode.never"), MediaPlayer::Settings::Transcoding::Mode::Never);
@@ -355,6 +380,7 @@ namespace lms::ui
auth::IPasswordService* _authPasswordService{}; auth::IPasswordService* _authPasswordService{};
bool _withOldPassword{}; bool _withOldPassword{};
std::shared_ptr<ArtistReleaseSortMethodModel> _artistReleaseSortMethodModel;
std::shared_ptr<TranscodingModeModel> _transcodingModeModeModel; std::shared_ptr<TranscodingModeModel> _transcodingModeModeModel;
std::shared_ptr<ValueStringModel<Bitrate>> _transcodingOutputBitrateModel; std::shared_ptr<ValueStringModel<Bitrate>> _transcodingOutputBitrateModel;
std::shared_ptr<ValueStringModel<TranscodingOutputFormat>> _transcodingOutputFormatModel; std::shared_ptr<ValueStringModel<TranscodingOutputFormat>> _transcodingOutputFormatModel;
@@ -425,12 +451,25 @@ namespace lms::ui
t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm)); t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
} }
// User interface
{
auto artistReleaseSortMethod{ std::make_unique<Wt::WComboBox>() };
artistReleaseSortMethod->setModel(model->getArtistReleaseSortMethodModel());
t->setFormWidget(SettingsModel::ArtistReleaseSortMethodField, std::move(artistReleaseSortMethod));
}
// Audio // Audio
{ {
// Transcode // Transcode
auto transcodingMode{ std::make_unique<Wt::WComboBox>() }; auto transcodingMode{ std::make_unique<Wt::WComboBox>() };
auto* transcodingModeRaw{ transcodingMode.get() };
transcodingMode->setModel(model->getTranscodingModeModel()); transcodingMode->setModel(model->getTranscodingModeModel());
transcodingMode->activated().connect([=](int row) {
const bool enable{ model->getTranscodingModeModel()->getValue(row) != MediaPlayer::Settings::Transcoding::Mode::Never };
model->setReadOnly(SettingsModel::TranscodeFormatField, !enable);
model->setReadOnly(SettingsModel::TranscodeBitrateField, !enable);
t->updateModel(model.get());
t->updateView(model.get());
});
t->setFormWidget(SettingsModel::TranscodingModeField, std::move(transcodingMode)); t->setFormWidget(SettingsModel::TranscodingModeField, std::move(transcodingMode));
// Format // Format
@@ -443,17 +482,15 @@ namespace lms::ui
transcodingOutputBitrate->setModel(model->getTranscodingOutputBitrateModel()); transcodingOutputBitrate->setModel(model->getTranscodingOutputBitrateModel());
t->setFormWidget(SettingsModel::TranscodeBitrateField, std::move(transcodingOutputBitrate)); t->setFormWidget(SettingsModel::TranscodeBitrateField, std::move(transcodingOutputBitrate));
transcodingModeRaw->activated().connect([=](int row) { // Replay gain mode
const bool enable{ model->getTranscodingModeModel()->getValue(row) != MediaPlayer::Settings::Transcoding::Mode::Never }; auto replayGainMode{ std::make_unique<Wt::WComboBox>() };
model->setReadOnly(SettingsModel::TranscodeFormatField, !enable); replayGainMode->activated().connect([=](int row) {
model->setReadOnly(SettingsModel::TranscodeBitrateField, !enable); const bool enable{ model->getReplayGainModeModel()->getValue(row) != MediaPlayer::Settings::ReplayGain::Mode::None };
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, !enable);
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainIfNoInfoField, !enable);
t->updateModel(model.get()); t->updateModel(model.get());
t->updateView(model.get()); t->updateView(model.get());
}); });
// Replay gain mode
auto replayGainMode{ std::make_unique<Wt::WComboBox>() };
auto* replayGainModeRaw{ replayGainMode.get() };
replayGainMode->setModel(model->getReplayGainModeModel()); replayGainMode->setModel(model->getReplayGainModeModel());
t->setFormWidget(SettingsModel::ReplayGainModeField, std::move(replayGainMode)); t->setFormWidget(SettingsModel::ReplayGainModeField, std::move(replayGainMode));
@@ -467,13 +504,6 @@ namespace lms::ui
replayGainPreampGainIfNoInfo->setRange(MediaPlayer::Settings::ReplayGain::minPreAmpGain, MediaPlayer::Settings::ReplayGain::maxPreAmpGain); replayGainPreampGainIfNoInfo->setRange(MediaPlayer::Settings::ReplayGain::minPreAmpGain, MediaPlayer::Settings::ReplayGain::maxPreAmpGain);
t->setFormWidget(SettingsModel::ReplayGainPreAmpGainIfNoInfoField, std::move(replayGainPreampGainIfNoInfo)); t->setFormWidget(SettingsModel::ReplayGainPreAmpGainIfNoInfoField, std::move(replayGainPreampGainIfNoInfo));
replayGainModeRaw->activated().connect([=](int row) {
const bool enable{ model->getReplayGainModeModel()->getValue(row) != MediaPlayer::Settings::ReplayGain::Mode::None };
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, !enable);
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainIfNoInfoField, !enable);
t->updateModel(model.get());
t->updateView(model.get());
});
if (LmsApp->getMediaPlayer().getSettings()->replayGain.mode == MediaPlayer::Settings::ReplayGain::Mode::None) if (LmsApp->getMediaPlayer().getSettings()->replayGain.mode == MediaPlayer::Settings::ReplayGain::Mode::None)
{ {
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, true); model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, true);
@@ -573,6 +603,8 @@ namespace lms::ui
}); });
t->updateView(model.get()); t->updateView(model.get());
initTooltipsForWidgetTree(*t);
} }
} // namespace lms::ui } // namespace lms::ui
+39
View File
@@ -0,0 +1,39 @@
/*
* 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 "Tooltip.hpp"
#include "core/ILogger.hpp"
#include <Wt/WWebWidget.h>
namespace lms::ui
{
void initTooltipsForWidgetTree(Wt::WWebWidget& widget)
{
std::ostringstream oss;
oss << R"({const rootElement = document.getElementById(')" << widget.id() << R"(');)"
<< R"(const tooltipTriggerList = rootElement.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(tooltipTriggerEl => {
new bootstrap.Tooltip(tooltipTriggerEl);
});})";
LMS_LOG(UI, DEBUG, "Running JS '" << oss.str() << "'");
widget.doJavaScript(oss.str());
}
} // namespace lms::ui
+30
View File
@@ -0,0 +1,30 @@
/*
* 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
namespace Wt
{
class WWebWidget;
}
namespace lms::ui
{
void initTooltipsForWidgetTree(Wt::WWebWidget& widget);
}
+2 -2
View File
@@ -40,7 +40,7 @@ namespace lms::ui
return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display)); return Wt::cpp17::any_cast<Wt::WString>(data(index(static_cast<int>(row), 0), Wt::ItemDataRole::Display));
} }
std::optional<std::size_t> getRowFromString(const Wt::WString& value) std::optional<std::size_t> getRowFromString(const Wt::WString& value) const
{ {
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i) for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
{ {
@@ -51,7 +51,7 @@ namespace lms::ui
return std::nullopt; return std::nullopt;
} }
std::optional<std::size_t> getRowFromValue(const T& value) std::optional<std::size_t> getRowFromValue(const T& value) const
{ {
for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i) for (std::size_t i{}; i < static_cast<std::size_t>(rowCount()); ++i)
{ {
+1 -1
View File
@@ -197,7 +197,7 @@ namespace lms::ui
params.setClusters(_filters.getClusters()); params.setClusters(_filters.getClusters());
params.setMediaLibrary(_filters.getMediaLibrary()); params.setMediaLibrary(_filters.getMediaLibrary());
params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {}); params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {});
params.setSortMethod(ReleaseSortMethod::OriginalDateDesc); params.setSortMethod(LmsApp->getUser()->getUIArtistReleaseSortMethod());
const auto releases{ Release::findIds(LmsApp->getDbSession(), params) }; const auto releases{ Release::findIds(LmsApp->getDbSession(), params) };
if (!releases.results.empty()) if (!releases.results.empty())