Subsonic API: try to recognize some formats in stream endpoint (mp3, opus, vorbis)

This commit is contained in:
emeric
2023-10-15 15:42:09 +02:00
parent 806599dedb
commit 8780bbdffa
14 changed files with 670 additions and 763 deletions
+2 -1
View File
@@ -241,6 +241,8 @@
<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.change-password">Change password</message> <message id="Lms.Settings.change-password">Change password</message>
<message id="Lms.Settings.default-transcode-bitrate">Default transcode bitrate</message>
<message id="Lms.Settings.default-transcode-format">Default transcode 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.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.password-bad">Bad password</message> <message id="Lms.Settings.password-bad">Bad password</message>
@@ -265,7 +267,6 @@
<message id="Lms.Settings.subsonic-api">Subsonic API</message> <message id="Lms.Settings.subsonic-api">Subsonic API</message>
<message id="Lms.Settings.transcode">Transcoding</message> <message id="Lms.Settings.transcode">Transcoding</message>
<message id="Lms.Settings.transcode-bitrate">Transcode bitrate</message> <message id="Lms.Settings.transcode-bitrate">Transcode bitrate</message>
<message id="Lms.Settings.transcode-enable">Enable transcoding</message>
<message id="Lms.Settings.transcode-format">Transcode format</message> <message id="Lms.Settings.transcode-format">Transcode format</message>
<message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message> <message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message>
<message id="Lms.Settings.transcode-format.mp3">MP3</message> <message id="Lms.Settings.transcode-format.mp3">MP3</message>
+2 -1
View File
@@ -241,6 +241,8 @@
<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.change-password">Changement de mot de passe</message> <message id="Lms.Settings.change-password">Changement de mot de passe</message>
<message id="Lms.Settings.default-transcode-bitrate">Bitrate par defaut du transcodage</message>
<message id="Lms.Settings.default-transcode-format">Format par defaut 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.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.password-bad">Mauvais mot de passe</message> <message id="Lms.Settings.password-bad">Mauvais mot de passe</message>
@@ -265,7 +267,6 @@
<message id="Lms.Settings.subsonic-api">API Subsonic</message> <message id="Lms.Settings.subsonic-api">API Subsonic</message>
<message id="Lms.Settings.transcode">Transcodage</message> <message id="Lms.Settings.transcode">Transcodage</message>
<message id="Lms.Settings.transcode-bitrate">Bitrate du transcodage</message> <message id="Lms.Settings.transcode-bitrate">Bitrate du transcodage</message>
<message id="Lms.Settings.transcode-enable">Activer le transcodage</message>
<message id="Lms.Settings.transcode-format">Format du transcodage</message> <message id="Lms.Settings.transcode-format">Format du transcodage</message>
<message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message> <message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message>
<message id="Lms.Settings.transcode-format.mp3">MP3</message> <message id="Lms.Settings.transcode-format.mp3">MP3</message>
+2 -1
View File
@@ -241,6 +241,8 @@
<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.change-password">Cambia password</message> <message id="Lms.Settings.change-password">Cambia password</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.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.password-bad">Password errata</message> <message id="Lms.Settings.password-bad">Password errata</message>
@@ -265,7 +267,6 @@
<message id="Lms.Settings.subsonic-api">Subsonic API</message> <message id="Lms.Settings.subsonic-api">Subsonic API</message>
<message id="Lms.Settings.transcode">Transcodifica</message> <message id="Lms.Settings.transcode">Transcodifica</message>
<message id="Lms.Settings.transcode-bitrate">Bitrate transcodifica</message> <message id="Lms.Settings.transcode-bitrate">Bitrate transcodifica</message>
<message id="Lms.Settings.transcode-enable">Abilita transcodifica</message>
<message id="Lms.Settings.transcode-format">Formato di transcodifica</message> <message id="Lms.Settings.transcode-format">Formato di transcodifica</message>
<message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message> <message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message>
<message id="Lms.Settings.transcode-format.mp3">MP3</message> <message id="Lms.Settings.transcode-format.mp3">MP3</message>
+2 -1
View File
@@ -241,6 +241,8 @@
<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>
<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>
@@ -265,7 +267,6 @@
<message id="Lms.Settings.subsonic-api">Subsonic 应用程序接口</message> <message id="Lms.Settings.subsonic-api">Subsonic 应用程序接口</message>
<message id="Lms.Settings.transcode">转码</message> <message id="Lms.Settings.transcode">转码</message>
<message id="Lms.Settings.transcode-bitrate">转码比特率</message> <message id="Lms.Settings.transcode-bitrate">转码比特率</message>
<message id="Lms.Settings.transcode-enable">开启转码</message>
<message id="Lms.Settings.transcode-format">转码格式</message> <message id="Lms.Settings.transcode-format">转码格式</message>
<message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message> <message id="Lms.Settings.transcode-format.matroska_opus">Matroska/Opus</message>
<message id="Lms.Settings.transcode-format.mp3">MP3</message> <message id="Lms.Settings.transcode-format.mp3">MP3</message>
+3 -14
View File
@@ -74,20 +74,9 @@
</div> </div>
${<if-has-subsonic-api>} ${<if-has-subsonic-api>}
<legend>${tr:Lms.Settings.subsonic-api}</legend> <legend>${tr:Lms.Settings.subsonic-api}</legend>
<div class="col-12">
<div class="form-check">
${subsonic-transcode-enable class="form-check-input"}
<label class="form-check-label" for="${id:subsonic-transcode-enable}">
${tr:Lms.Settings.transcode-enable class="form-control"}
</label>
<div class="invalid-feedback">
${subsonic-transcode-enable-info}
</div>
</div>
</div>
<div class="col-lg-6"> <div class="col-lg-6">
<label class="form-label" for="${id:subsonic-transcode-format}"> <label class="form-label" for="${id:subsonic-transcode-format}">
${tr:Lms.Settings.transcode-format} ${tr:Lms.Settings.default-transcode-format}
</label> </label>
${subsonic-transcode-format class="form-control"} ${subsonic-transcode-format class="form-control"}
<div class="invalid-feedback"> <div class="invalid-feedback">
@@ -95,8 +84,8 @@
</div> </div>
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<label class="col-lg-3 form-label" for="${id:subsonic-transcode-bitrate}"> <label class="form-label" for="${id:subsonic-transcode-bitrate}">
${tr:Lms.Settings.transcode-bitrate} ${tr:Lms.Settings.default-transcode-bitrate}
</label> </label>
<div class="input-group mb-3"> <div class="input-group mb-3">
${subsonic-transcode-bitrate class="form-control"} ${subsonic-transcode-bitrate class="form-control"}
+50 -122
View File
@@ -30,8 +30,7 @@
namespace Database namespace Database
{ {
VersionInfo::pointer VersionInfo::pointer VersionInfo::getOrCreate(Session& session)
VersionInfo::getOrCreate(Session& session)
{ {
session.checkUniqueLocked(); session.checkUniqueLocked();
@@ -42,14 +41,12 @@ namespace Database
return versionInfo; return versionInfo;
} }
VersionInfo::pointer VersionInfo::pointer VersionInfo::get(Session& session)
VersionInfo::get(Session& session)
{ {
session.checkSharedLocked(); session.checkSharedLocked();
return session.getDboSession().find<VersionInfo>(); return session.getDboSession().find<VersionInfo>();
} }
} }
namespace Database::Migration namespace Database::Migration
@@ -75,50 +72,38 @@ namespace Database::Migration
Db& _db; Db& _db;
}; };
static static std::string dateTimeToDbFormat(const Wt::WDateTime& dateTime)
std::string
dateTimeToDbFormat(const Wt::WDateTime& dateTime)
{ {
return dateTime.toString("yyyy'-'MM'-'dd'T'hh':'mm':'ss'.000'", false).toUTF8(); return dateTime.toString("yyyy'-'MM'-'dd'T'hh':'mm':'ss'.000'", false).toUTF8();
} }
static static void migrateFromV5(Session& session)
void
migrateFromV5(Session& session)
{ {
session.getDboSession().execute("DELETE FROM auth_token"); // format has changed session.getDboSession().execute("DELETE FROM auth_token"); // format has changed
} }
static static void migrateFromV6(Session& session)
void
migrateFromV6(Session& session)
{ {
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV7(Session& session)
void
migrateFromV7(Session& session)
{ {
session.getDboSession().execute("DROP TABLE similarity_settings"); session.getDboSession().execute("DROP TABLE similarity_settings");
session.getDboSession().execute("DROP TABLE similarity_settings_feature"); session.getDboSession().execute("DROP TABLE similarity_settings_feature");
session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(ScanSettings::RecommendationEngineType::Clusters)) + ")"); session.getDboSession().execute("ALTER TABLE scan_settings ADD similarity_engine_type INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(ScanSettings::RecommendationEngineType::Clusters)) + ")");
} }
static static void migrateFromV8(Session& session)
void
migrateFromV8(Session& session)
{ {
// Better cover handling, need to rescan the whole files // Better cover handling, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV9(Session& session)
void
migrateFromV9(Session& session)
{ {
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "track_bookmark" ( CREATE TABLE IF NOT EXISTS "track_bookmark" (
@@ -133,60 +118,46 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
);)"); );)");
} }
static static void migrateFromV10(Session& session)
void
migrateFromV10(Session& session)
{ {
ScanSettings::get(session).modify()->addAudioFileExtension(".m4b"); ScanSettings::get(session).modify()->addAudioFileExtension(".m4b");
ScanSettings::get(session).modify()->addAudioFileExtension(".alac"); ScanSettings::get(session).modify()->addAudioFileExtension(".alac");
} }
static static void migrateFromV11(Session& session)
void
migrateFromV11(Session& session)
{ {
// Sanitize bad MBID, need to rescan the whole files // Sanitize bad MBID, need to rescan the whole files
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV12(Session& session)
void
migrateFromV12(Session& session)
{ {
// Artist and release that have a badly parsed name but a MBID had no chance to updat the name // Artist and release that have a badly parsed name but a MBID had no chance to updat the name
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV13(Session& session)
void
migrateFromV13(Session& session)
{ {
// Always store UUID in lower case + better WMA parsing // Always store UUID in lower case + better WMA parsing
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV14(Session& session)
void
migrateFromV14(Session& session)
{ {
// SortName now set from metadata // SortName now set from metadata
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV15(Session& session)
void
migrateFromV15(Session& session)
{ {
session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")"); session.getDboSession().execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")");
} }
static static void migrateFromV16(Session& session)
void
migrateFromV16(Session& session)
{ {
session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)"); session.getDboSession().execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)");
session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)"); session.getDboSession().execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)");
@@ -195,9 +166,7 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV17(Session& session)
void
migrateFromV17(Session& session)
{ {
// Drop colums total_disc/total_track from release // Drop colums total_disc/total_track from release
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
@@ -215,10 +184,7 @@ CREATE TABLE "release_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static void migrateFromV18(Session& session)
static
void
migrateFromV18(Session& session)
{ {
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "subsonic_settings" ( CREATE TABLE IF NOT EXISTS "subsonic_settings" (
@@ -229,9 +195,7 @@ CREATE TABLE IF NOT EXISTS "subsonic_settings" (
))"); ))");
} }
static static void migrateFromV19(Session& session)
void
migrateFromV19(Session& session)
{ {
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
CREATE TABLE "user_backup" ( CREATE TABLE "user_backup" (
@@ -252,7 +216,7 @@ CREATE TABLE "user_backup" (
"radio" boolean not null "radio" boolean not null
))"); ))");
session.getDboSession().execute(std::string{ "INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, " } session.getDboSession().execute(std::string{ "INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, " }
+ (User::defaultSubsonicTranscodeEnable ? "1" : "0") + "1" // default enable transcode
+ ", " + std::to_string(static_cast<int>(User::defaultSubsonicTranscodeFormat)) + ", " + std::to_string(static_cast<int>(User::defaultSubsonicTranscodeFormat))
+ ", " + std::to_string(User::defaultSubsonicTranscodeBitrate) + ", " + std::to_string(User::defaultSubsonicTranscodeBitrate)
+ ", " + std::to_string(static_cast<int>(User::defaultSubsonicArtistListMode)) + ", " + std::to_string(static_cast<int>(User::defaultSubsonicArtistListMode))
@@ -261,16 +225,12 @@ CREATE TABLE "user_backup" (
session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user");
} }
static static void migrateFromV20(Session& session)
void
migrateFromV20(Session& session)
{ {
session.getDboSession().execute("DROP TABLE subsonic_settings"); session.getDboSession().execute("DROP TABLE subsonic_settings");
} }
static static void migrateFromV21(Session& session)
void
migrateFromV21(Session& session)
{ {
session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL"); session.getDboSession().execute("ALTER TABLE track ADD track_replay_gain REAL");
session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL"); session.getDboSession().execute("ALTER TABLE track ADD release_replay_gain REAL");
@@ -279,10 +239,7 @@ CREATE TABLE "user_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static void migrateFromV22(Session& session)
static
void
migrateFromV22(Session& session)
{ {
session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''"); session.getDboSession().execute("ALTER TABLE track ADD disc_subtitle TEXT NOT NULL DEFAULT ''");
@@ -290,54 +247,41 @@ CREATE TABLE "user_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static void migrateFromV23(Session& session)
static
void
migrateFromV23(Session& session)
{ {
// Better cover detection // Better cover detection
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV24(Session& session)
void
migrateFromV24(Session& session)
{ {
// User's AuthMode // User's AuthMode
session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultAuthMode*/0)) + ")"); session.getDboSession().execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultAuthMode*/0)) + ")");
} }
static static void migrateFromV25(Session& session)
void
migrateFromV25(Session& session)
{ {
// Better cover detection // Better cover detection
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV26(Session& session)
void
migrateFromV26(Session& session)
{ {
// Composer, mixer, etc. support // Composer, mixer, etc. support
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV27(Session& session)
void
migrateFromV27(Session& session)
{ {
// Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists // Composer, mixer, etc. support, now fallback on MBID tagged entries as there is no mean to provide MBID by tags for these kinf od artists
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV28(Session& session)
void
migrateFromV28(Session& session)
{ {
// Drop Auth mode // Drop Auth mode
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
@@ -363,9 +307,7 @@ CREATE TABLE "user_backup" (
session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user"); session.getDboSession().execute("ALTER TABLE user_backup RENAME TO user");
} }
static static void migrateFromV29(Session& session)
void
migrateFromV29(Session& session)
{ {
session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT"); session.getDboSession().execute("ALTER TABLE tracklist_entry ADD date_time TEXT");
session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT"); session.getDboSession().execute("ALTER TABLE user ADD listenbrainz_token TEXT");
@@ -379,9 +321,7 @@ CREATE TABLE "user_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV30(Session& session)
void
migrateFromV30(Session& session)
{ {
// drop "year" and "original_year" (rescan needed to convert them into dates) // drop "year" and "original_year" (rescan needed to convert them into dates)
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
@@ -413,9 +353,7 @@ CREATE TABLE "track_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV31(Session& session)
void
migrateFromV31(Session& session)
{ {
// new star system, using dedicated entries per scrobbler and date time // new star system, using dedicated entries per scrobbler and date time
session.getDboSession().execute(R"( session.getDboSession().execute(R"(
@@ -542,16 +480,12 @@ CREATE TABLE "listen" (
migrateListens("__scrobbler_listenbrainz_history__", Scrobbler::ListenBrainz); migrateListens("__scrobbler_listenbrainz_history__", Scrobbler::ListenBrainz);
} }
static static void migrateFromV32(Session& session)
void
migrateFromV32(Session& session)
{ {
ScanSettings::get(session).modify()->addAudioFileExtension(".wv"); ScanSettings::get(session).modify()->addAudioFileExtension(".wv");
} }
static static void migrateFromV33(Session& session)
void
migrateFromV33(Session& session)
{ {
// remove name from track_artist_link // remove name from track_artist_link
// Drop Auth mode // Drop Auth mode
@@ -571,9 +505,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
session.getDboSession().execute("ALTER TABLE track_artist_link_backup RENAME TO track_artist_link"); session.getDboSession().execute("ALTER TABLE track_artist_link_backup RENAME TO track_artist_link");
} }
static static void migrateFromV34(Session& session)
void
migrateFromV34(Session& session)
{ {
// Add scrobbling state // Add scrobbling state
// By default, everything needs to be sent // By default, everything needs to be sent
@@ -582,27 +514,21 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
session.getDboSession().execute("ALTER TABLE starred_track ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")"); session.getDboSession().execute("ALTER TABLE starred_track ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
} }
static static void migrateFromV35(Session& session)
void
migrateFromV35(Session& session)
{ {
// Add creattion/last modif date time for tracklists // Add creattion/last modif date time for tracklists
session.getDboSession().execute("ALTER TABLE tracklist ADD creation_date_time TEXT"); session.getDboSession().execute("ALTER TABLE tracklist ADD creation_date_time TEXT");
session.getDboSession().execute("ALTER TABLE tracklist ADD last_modified_date_time TEXT"); session.getDboSession().execute("ALTER TABLE tracklist ADD last_modified_date_time TEXT");
} }
static static void migrateFromV36(Session& session)
void
migrateFromV36(Session& session)
{ {
// Increased precision for track durations (now in milliseconds instead of secodns) // Increased precision for track durations (now in milliseconds instead of secodns)
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV37(Session& session)
void
migrateFromV37(Session& session)
{ {
// Support Performer tags (via subtypes) // Support Performer tags (via subtypes)
session.getDboSession().execute("ALTER TABLE track_artist_link ADD subtype TEXT"); session.getDboSession().execute("ALTER TABLE track_artist_link ADD subtype TEXT");
@@ -611,9 +537,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV38(Session& session)
void
migrateFromV38(Session& session)
{ {
// migrate release-specific tags from Track to Release // migrate release-specific tags from Track to Release
session.getDboSession().execute("ALTER TABLE release ADD total_disc INTEGER"); session.getDboSession().execute("ALTER TABLE release ADD total_disc INTEGER");
@@ -653,9 +577,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV39(Session& session)
void
migrateFromV39(Session& session)
{ {
// add release type // add release type
session.getDboSession().execute("ALTER TABLE release ADD primary_type INTEGER"); session.getDboSession().execute("ALTER TABLE release ADD primary_type INTEGER");
@@ -665,9 +587,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
static static void migrateFromV40(Session& session)
void
migrateFromV40(Session& session)
{ {
// add artist_display_name in Release and Track // add artist_display_name in Release and Track
session.getDboSession().execute("ALTER TABLE release ADD artist_display_name TEXT"); session.getDboSession().execute("ALTER TABLE release ADD artist_display_name TEXT");
@@ -677,8 +597,15 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
ScanSettings::get(session).modify()->incScanVersion(); ScanSettings::get(session).modify()->incScanVersion();
} }
void static void migrateFromV41(Session& session)
doDbMigration(Session& session) {
// add artist_display_name in Release and Track
session.getDboSession().execute("ALTER TABLE user RENAME COLUMN subsonic_transcode_format TO subsonic_default_transcode_format");
session.getDboSession().execute("ALTER TABLE user RENAME COLUMN subsonic_transcode_bitrate TO subsonic_default_transcode_bitrate");
session.getDboSession().execute("ALTER TABLE user DROP COLUMN subsonic_transcode_enable");
}
void doDbMigration(Session& session)
{ {
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" }; static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -724,6 +651,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
{38, migrateFromV38}, {38, migrateFromV38},
{39, migrateFromV39}, {39, migrateFromV39},
{40, migrateFromV40}, {40, migrateFromV40},
{41, migrateFromV41},
}; };
while (1) while (1)
@@ -26,7 +26,7 @@ namespace Database
class Session; class Session;
using Version = std::size_t; using Version = std::size_t;
static constexpr Version LMS_DATABASE_VERSION {41}; static constexpr Version LMS_DATABASE_VERSION{ 42 };
class VersionInfo class VersionInfo
{ {
public: public:
+9 -19
View File
@@ -35,22 +35,19 @@ User::User(std::string_view loginName)
{ {
} }
User::pointer User::pointer User::create(Session& session, std::string_view loginName)
User::create(Session& session, std::string_view loginName)
{ {
return session.getDboSession().add(std::unique_ptr<User> {new User{ loginName }}); return session.getDboSession().add(std::unique_ptr<User> {new User{ loginName }});
} }
std::size_t std::size_t User::getCount(Session& session)
User::getCount(Session& session)
{ {
session.checkSharedLocked(); session.checkSharedLocked();
return session.getDboSession().query<int>("SELECT COUNT(*) FROM user"); return session.getDboSession().query<int>("SELECT COUNT(*) FROM user");
} }
RangeResults<UserId> RangeResults<UserId> User::find(Session& session, const FindParameters& params)
User::find(Session& session, const FindParameters& params)
{ {
session.checkSharedLocked(); session.checkSharedLocked();
@@ -62,41 +59,34 @@ User::find(Session& session, const FindParameters& params)
return Utils::execQuery(query, params.range); return Utils::execQuery(query, params.range);
} }
User::pointer User::pointer User::findDemoUser(Session& session)
User::findDemoUser(Session& session)
{ {
session.checkSharedLocked(); session.checkSharedLocked();
return session.getDboSession().find<User>().where("type = ?").bind(UserType::DEMO).resultValue(); return session.getDboSession().find<User>().where("type = ?").bind(UserType::DEMO).resultValue();
} }
User::pointer User::pointer User::find(Session& session, UserId id)
User::find(Session& session, UserId id)
{ {
return session.getDboSession().find<User>().where("id = ?").bind(id).resultValue(); return session.getDboSession().find<User>().where("id = ?").bind(id).resultValue();
} }
User::pointer User::pointer User::find(Session& session, std::string_view name)
User::find(Session& session, std::string_view name)
{ {
return session.getDboSession().find<User>() return session.getDboSession().find<User>()
.where("login_name = ?").bind(name) .where("login_name = ?").bind(name)
.resultValue(); .resultValue();
} }
void void User::setSubsonicDefaultTranscodeBitrate(Bitrate bitrate)
User::setSubsonicTranscodeBitrate(Bitrate bitrate)
{ {
assert(isAudioBitrateAllowed(bitrate)); assert(isAudioBitrateAllowed(bitrate));
_subsonicTranscodeBitrate = bitrate; _subsonicDefaultTranscodeBitrate = bitrate;
} }
void void User::clearAuthTokens()
User::clearAuthTokens()
{ {
_authTokens.clear(); _authTokens.clear();
} }
} // namespace Database } // namespace Database
@@ -56,7 +56,6 @@ class User final : public Object<User, UserId>
static inline const std::size_t MinNameLength{ 3 }; static inline const std::size_t MinNameLength{ 3 };
static inline const std::size_t MaxNameLength{ 15 }; static inline const std::size_t MaxNameLength{ 15 };
static inline const bool defaultSubsonicTranscodeEnable {true};
static inline const AudioFormat defaultSubsonicTranscodeFormat{ AudioFormat::OGG_OPUS }; static inline const AudioFormat defaultSubsonicTranscodeFormat{ AudioFormat::OGG_OPUS };
static inline const Bitrate defaultSubsonicTranscodeBitrate{ 128000 }; static inline const Bitrate defaultSubsonicTranscodeBitrate{ 128000 };
static inline const UITheme defaultUITheme{ UITheme::Dark }; static inline const UITheme defaultUITheme{ UITheme::Dark };
@@ -81,9 +80,8 @@ class User final : public Object<User, UserId>
void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; } void setLastLogin(const Wt::WDateTime& dateTime) { _lastLogin = dateTime; }
void setPasswordHash(const PasswordHash& passwordHash) { _passwordSalt = passwordHash.salt; _passwordHash = passwordHash.hash; } void setPasswordHash(const PasswordHash& passwordHash) { _passwordSalt = passwordHash.salt; _passwordHash = passwordHash.hash; }
void setType(UserType type) { _type = type; } void setType(UserType type) { _type = type; }
void setSubsonicTranscodeEnable(bool value) { _subsonicTranscodeEnable = value; } void setSubsonicDefaultTranscodeFormat(AudioFormat encoding) { _subsonicDefaultTranscodeFormat = encoding; }
void setSubsonicTranscodeFormat(AudioFormat encoding) { _subsonicTranscodeFormat = encoding; } void setSubsonicDefaultTranscodeBitrate(Bitrate bitrate);
void setSubsonicTranscodeBitrate(Bitrate bitrate);
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
void setRadio(bool val) { _radio = val; } void setRadio(bool val) { _radio = val; }
void setRepeatAll(bool val) { _repeatAll = val; } void setRepeatAll(bool val) { _repeatAll = val; }
@@ -97,9 +95,8 @@ class User final : public Object<User, UserId>
bool isAdmin() const { return _type == UserType::ADMIN; } bool isAdmin() const { return _type == UserType::ADMIN; }
bool isDemo() const { return _type == UserType::DEMO; } bool isDemo() const { return _type == UserType::DEMO; }
UserType getType() const { return _type; } UserType getType() const { return _type; }
bool getSubsonicTranscodeEnable() const { return _subsonicTranscodeEnable; } AudioFormat getSubsonicDefaultTranscodeFormat() const { return _subsonicDefaultTranscodeFormat; }
AudioFormat getSubsonicTranscodeFormat() const { return _subsonicTranscodeFormat; } Bitrate getSubsonicDefaultTranscodeBitrate() const { return _subsonicDefaultTranscodeBitrate; }
Bitrate getSubsonicTranscodeBitrate() const { return _subsonicTranscodeBitrate; }
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
bool isRepeatAllSet() const { return _repeatAll; } bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; } bool isRadioSet() const { return _radio; }
@@ -116,9 +113,8 @@ class User final : public Object<User, UserId>
Wt::Dbo::field(a, _passwordSalt, "password_salt"); Wt::Dbo::field(a, _passwordSalt, "password_salt");
Wt::Dbo::field(a, _passwordHash, "password_hash"); Wt::Dbo::field(a, _passwordHash, "password_hash");
Wt::Dbo::field(a, _lastLogin, "last_login"); Wt::Dbo::field(a, _lastLogin, "last_login");
Wt::Dbo::field(a, _subsonicTranscodeEnable, "subsonic_transcode_enable"); Wt::Dbo::field(a, _subsonicDefaultTranscodeFormat, "subsonic_default_transcode_format");
Wt::Dbo::field(a, _subsonicTranscodeFormat, "subsonic_transcode_format"); Wt::Dbo::field(a, _subsonicDefaultTranscodeBitrate, "subsonic_default_transcode_bitrate");
Wt::Dbo::field(a, _subsonicTranscodeBitrate, "subsonic_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, _scrobbler, "scrobbler"); Wt::Dbo::field(a, _scrobbler, "scrobbler");
@@ -150,9 +146,8 @@ class User final : public Object<User, UserId>
// User defined settings // User defined settings
SubsonicArtistListMode _subsonicArtistListMode{ defaultSubsonicArtistListMode }; SubsonicArtistListMode _subsonicArtistListMode{ defaultSubsonicArtistListMode };
bool _subsonicTranscodeEnable {defaultSubsonicTranscodeEnable}; AudioFormat _subsonicDefaultTranscodeFormat{ defaultSubsonicTranscodeFormat };
AudioFormat _subsonicTranscodeFormat {defaultSubsonicTranscodeFormat}; int _subsonicDefaultTranscodeBitrate{ defaultSubsonicTranscodeBitrate };
int _subsonicTranscodeBitrate {defaultSubsonicTranscodeBitrate};
// User's dynamic data (UI) // User's dynamic data (UI)
int _curPlayingTrackPos{}; // Current track position in queue int _curPlayingTrackPos{}; // Current track position in queue
@@ -163,4 +158,3 @@ class User final : public Object<User, UserId>
}; };
} // namespace Databas' } // namespace Databas'
@@ -30,25 +30,40 @@
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
#include "utils/FileResourceHandlerCreator.hpp" #include "utils/FileResourceHandlerCreator.hpp"
#include "utils/Utils.hpp" #include "utils/Utils.hpp"
#include "utils/String.hpp"
#include "ParameterParsing.hpp" #include "ParameterParsing.hpp"
#include "SubsonicId.hpp" #include "SubsonicId.hpp"
using namespace Database;
namespace API::Subsonic namespace API::Subsonic
{ {
using namespace Database;
namespace { namespace {
std::optional<Av::Format> subsonicStreamFormatToAvFormat(std::string_view format)
{
for (const auto& [str, avFormat] : std::initializer_list<std::pair<std::string_view, Av::Format>>{
{"mp3", Av::Format::MP3},
{"opus", Av::Format::OGG_OPUS},
{"vorbis", Av::Format::OGG_VORBIS},
})
{
if (StringUtils::stringCaseInsensitiveEqual("str", format))
return avFormat;
}
return std::nullopt;
}
Av::Format userTranscodeFormatToAvFormat(AudioFormat format) Av::Format userTranscodeFormatToAvFormat(AudioFormat format)
{ {
switch (format) switch (format)
{ {
case AudioFormat::MP3: return Av::Format::MP3; case Database::AudioFormat::MP3: return Av::Format::MP3;
case AudioFormat::OGG_OPUS: return Av::Format::OGG_OPUS; case Database::AudioFormat::OGG_OPUS: return Av::Format::OGG_OPUS;
case AudioFormat::MATROSKA_OPUS: return Av::Format::MATROSKA_OPUS; case Database::AudioFormat::MATROSKA_OPUS: return Av::Format::MATROSKA_OPUS;
case AudioFormat::OGG_VORBIS: return Av::Format::OGG_VORBIS; case Database::AudioFormat::OGG_VORBIS: return Av::Format::OGG_VORBIS;
case AudioFormat::WEBM_VORBIS: return Av::Format::WEBM_VORBIS; case Database::AudioFormat::WEBM_VORBIS: return Av::Format::WEBM_VORBIS;
default: return Av::Format::OGG_OPUS;
} }
return Av::Format::OGG_OPUS;
} }
struct StreamParameters struct StreamParameters
@@ -64,9 +79,9 @@ namespace API::Subsonic
const TrackId id{ getMandatoryParameterAs<TrackId>(context.parameters, "id") }; const TrackId id{ getMandatoryParameterAs<TrackId>(context.parameters, "id") };
// Optional params // Optional params
std::optional<std::size_t> maxBitRate{ getParameterAs<std::size_t>(context.parameters, "maxBitRate") }; std::size_t maxBitRate{ getParameterAs<std::size_t>(context.parameters, "maxBitRate").value_or(0) }; // "If set to zero, no limit is imposed"
const std::optional<std::string> format{ getParameterAs<std::string>(context.parameters, "format") }; const std::string format{ getParameterAs<std::string>(context.parameters, "format").value_or("") };
const std::optional<std::size_t> timeOffset{ getParameterAs<std::size_t>(context.parameters, "timeOffset") }; std::size_t timeOffset{ getParameterAs<std::size_t>(context.parameters, "timeOffset").value_or(0) };
bool estimateContentLength{ getParameterAs<bool>(context.parameters, "estimateContentLength").value_or(false) }; bool estimateContentLength{ getParameterAs<bool>(context.parameters, "estimateContentLength").value_or(false) };
StreamParameters parameters; StreamParameters parameters;
@@ -84,30 +99,25 @@ namespace API::Subsonic
parameters.inputFileParameters.duration = track->getDuration(); parameters.inputFileParameters.duration = track->getDuration();
} }
if (format != "raw") // raw => no transcode
{ {
const User::pointer user{ User::find(context.dbSession, context.userId) }; const User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user) if (!user)
throw UserNotAuthorizedError{}; throw UserNotAuthorizedError{};
// format = "raw" => no transcode. Other format values will be ignored Av::TranscodeParameters& transcodeParameters{ parameters.transcodeParameters.emplace() };
const bool transcode{ (!format || (*format != "raw")) && user->getSubsonicTranscodeEnable() };
if (transcode)
{
std::size_t bitRate{ user->getSubsonicTranscodeBitrate() / 1000 };
// "If set to zero, no limit is imposed"
if (maxBitRate && *maxBitRate != 0)
bitRate = Utils::clamp(*maxBitRate, std::size_t{ 48 }, bitRate);
Av::TranscodeParameters transcodeParameters;
transcodeParameters.bitrate = bitRate * 1000;
transcodeParameters.format = userTranscodeFormatToAvFormat(user->getSubsonicTranscodeFormat());
transcodeParameters.stripMetadata = false; // We want clients to use metadata (offline use, replay gain, etc.) transcodeParameters.stripMetadata = false; // We want clients to use metadata (offline use, replay gain, etc.)
transcodeParameters.offset = std::chrono::seconds{ timeOffset ? *timeOffset : 0 }; transcodeParameters.offset = std::chrono::seconds{ timeOffset };
parameters.transcodeParameters = std::move(transcodeParameters); if (std::optional<Av::Format> requestedFormat{ subsonicStreamFormatToAvFormat(format) })
} transcodeParameters.format = *requestedFormat;
else
transcodeParameters.format = userTranscodeFormatToAvFormat(user->getSubsonicDefaultTranscodeFormat());
transcodeParameters.bitrate = user->getSubsonicDefaultTranscodeBitrate();
if (maxBitRate != 0)
transcodeParameters.bitrate = Utils::clamp(transcodeParameters.bitrate, std::size_t{ 48 }, maxBitRate);
} }
return parameters; return parameters;
+1 -2
View File
@@ -120,8 +120,7 @@ namespace API::Subsonic
trackResponse.setAttribute("suffix", extension.string().substr(1)); trackResponse.setAttribute("suffix", extension.string().substr(1));
} }
if (user->getSubsonicTranscodeEnable()) trackResponse.setAttribute("transcodedSuffix", formatToSuffix(user->getSubsonicDefaultTranscodeFormat()));
trackResponse.setAttribute("transcodedSuffix", formatToSuffix(user->getSubsonicTranscodeFormat()));
trackResponse.setAttribute("coverArt", idToString(track->getId())); trackResponse.setAttribute("coverArt", idToString(track->getId()));
+14
View File
@@ -177,6 +177,20 @@ namespace StringUtils
return oss.str(); return oss.str();
} }
bool stringCaseInsensitiveEqual(std::string_view strA, std::string_view strB)
{
if (strA.size() != strB.size())
return false;
for (std::size_t i{}; i < strA.size(); ++i)
{
if (std::tolower(strA[i]) != std::tolower(strB[i]))
return false;
}
return true;
}
void capitalize(std::string& str) void capitalize(std::string& str)
{ {
for (auto it{ std::begin(str) }; it != std::end(str); ++it) for (auto it{ std::begin(str) }; it != std::end(str); ++it)
+2
View File
@@ -55,6 +55,8 @@ namespace StringUtils {
[[nodiscard]] std::string bufferToString(const std::vector<unsigned char>& data); [[nodiscard]] std::string bufferToString(const std::vector<unsigned char>& data);
[[nodiscard]] bool stringCaseInsensitiveEqual(std::string_view strA, std::string_view strB);
void capitalize(std::string& str); void capitalize(std::string& str);
template<typename T> template<typename T>
+4 -27
View File
@@ -160,15 +160,13 @@ class SettingsModel : public Wt::WFormModel
} }
{ {
user.modify()->setSubsonicTranscodeEnable(Wt::asNumber(value(SubsonicTranscodeEnableField)));
auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(SubsonicTranscodeBitrateField))}; auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(SubsonicTranscodeBitrateField))};
if (subsonicTranscodeBitrateRow) if (subsonicTranscodeBitrateRow)
user.modify()->setSubsonicTranscodeBitrate(_transcodeBitrateModel->getValue(*subsonicTranscodeBitrateRow)); user.modify()->setSubsonicDefaultTranscodeBitrate(_transcodeBitrateModel->getValue(*subsonicTranscodeBitrateRow));
auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromString(valueText(SubsonicTranscodeFormatField))}; auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromString(valueText(SubsonicTranscodeFormatField))};
if (subsonicTranscodeFormatRow) if (subsonicTranscodeFormatRow)
user.modify()->setSubsonicTranscodeFormat(_transcodeFormatModel->getValue(*subsonicTranscodeFormatRow)); user.modify()->setSubsonicDefaultTranscodeFormat(_transcodeFormatModel->getValue(*subsonicTranscodeFormatRow));
auto subsonicArtistListModeRow {_subsonicArtistListModeModel->getRowFromString(valueText(SubsonicArtistListModeField))}; auto subsonicArtistListModeRow {_subsonicArtistListModeModel->getRowFromString(valueText(SubsonicArtistListModeField))};
if (subsonicArtistListModeRow) if (subsonicArtistListModeRow)
@@ -224,19 +222,12 @@ class SettingsModel : public Wt::WFormModel
setValue(ReplayGainPreAmpGainIfNoInfoField, settings.replayGain.preAmpGainIfNoInfo); setValue(ReplayGainPreAmpGainIfNoInfoField, settings.replayGain.preAmpGainIfNoInfo);
} }
setValue(SubsonicTranscodeEnableField, LmsApp->getUser()->getSubsonicTranscodeEnable());
{ {
const bool usesTranscode {LmsApp->getUser()->getSubsonicTranscodeEnable()}; auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromValue(user->getSubsonicDefaultTranscodeBitrate())};
setReadOnly(SubsonicTranscodeFormatField, !usesTranscode);
setReadOnly(SubsonicTranscodeBitrateField, !usesTranscode);
}
{
auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromValue(user->getSubsonicTranscodeBitrate())};
if (subsonicTranscodeBitrateRow) if (subsonicTranscodeBitrateRow)
setValue(SubsonicTranscodeBitrateField, _transcodeBitrateModel->getString(*subsonicTranscodeBitrateRow)); setValue(SubsonicTranscodeBitrateField, _transcodeBitrateModel->getString(*subsonicTranscodeBitrateRow));
auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromValue(user->getSubsonicTranscodeFormat())}; auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromValue(user->getSubsonicDefaultTranscodeFormat())};
if (subsonicTranscodeFormatRow) if (subsonicTranscodeFormatRow)
setValue(SubsonicTranscodeFormatField, _transcodeFormatModel->getString(*subsonicTranscodeFormatRow)); setValue(SubsonicTranscodeFormatField, _transcodeFormatModel->getString(*subsonicTranscodeFormatRow));
@@ -482,11 +473,6 @@ SettingsView::refreshView()
{ {
t->setCondition("if-has-subsonic-api", Service<IConfig>::get()->getBool("api-subsonic", true)); t->setCondition("if-has-subsonic-api", Service<IConfig>::get()->getBool("api-subsonic", true));
// Transcode
auto transcode {std::make_unique<Wt::WCheckBox>()};
auto* transcodeRaw {transcode.get()};
t->setFormWidget(SettingsModel::SubsonicTranscodeEnableField, std::move(transcode));
// Format // Format
auto transcodeFormat {std::make_unique<Wt::WComboBox>()}; auto transcodeFormat {std::make_unique<Wt::WComboBox>()};
transcodeFormat->setModel(model->getTranscodeFormatModel()); transcodeFormat->setModel(model->getTranscodeFormatModel());
@@ -501,15 +487,6 @@ SettingsView::refreshView()
auto artistListMode {std::make_unique<Wt::WComboBox>()}; auto artistListMode {std::make_unique<Wt::WComboBox>()};
artistListMode->setModel(model->getSubsonicArtistListModeModel()); artistListMode->setModel(model->getSubsonicArtistListModeModel());
t->setFormWidget(SettingsModel::SubsonicArtistListModeField, std::move(artistListMode)); t->setFormWidget(SettingsModel::SubsonicArtistListModeField, std::move(artistListMode));
transcodeRaw->changed().connect([=]
{
const bool enable {transcodeRaw->checkState() == Wt::CheckState::Checked};
model->setReadOnly(SettingsModel::SubsonicTranscodeFormatField, !enable);
model->setReadOnly(SettingsModel::SubsonicTranscodeBitrateField, !enable);
t->updateModel(model.get());
t->updateView(model.get());
});
} }
// Scrobbling // Scrobbling