Subsonic API: Added an option to only list album artists. fixes #27
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
<!--FORMS message blocks-->
|
||||||
|
|
||||||
|
<message id="Lms.Admin.Subsonic.template">
|
||||||
|
<legend>${tr:Lms.Admin.Subsonic.subsonic}</legend>
|
||||||
|
<div class="form-horizontal">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="${id:enable}">
|
||||||
|
${tr:Lms.Admin.Subsonic.enable-api}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<div class="checkbox"><label>${enable}</label></div>
|
||||||
|
</div>
|
||||||
|
<div class="help-block col-sm-5">
|
||||||
|
${enable-info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="${id:artist-list-mode}">
|
||||||
|
${tr:Lms.Admin.Subsonic.artist-list-mode}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-5">
|
||||||
|
${artist-list-mode}
|
||||||
|
</div>
|
||||||
|
<div class="help-block col-sm-5">
|
||||||
|
${artist-list-mode-info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
${apply-btn class="btn-primary"} ${discard-btn}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</message>
|
||||||
|
|
||||||
|
</messages>
|
||||||
@@ -68,6 +68,14 @@
|
|||||||
<message id="Lms.Admin.Database.Status.status-scheduled">Scheduled on {1}</message>
|
<message id="Lms.Admin.Database.Status.status-scheduled">Scheduled on {1}</message>
|
||||||
<message id="Lms.Admin.Database.Status.status-in-progress">Scanning {1} of {2} files ({3} %)</message>
|
<message id="Lms.Admin.Database.Status.status-in-progress">Scanning {1} of {2} files ({3} %)</message>
|
||||||
|
|
||||||
|
<!--Subsonic-->
|
||||||
|
<message id="Lms.Admin.Subsonic.all-artists">All artists</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.release-artists">Album artists</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.enable-api">Enable</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.artist-list-mode">Artist list mode</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.need-restart">You must restart the service for this change to take effect</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.subsonic">Subsonic API</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">New user</message>
|
<message id="Lms.Admin.Users.add">New user</message>
|
||||||
<message id="Lms.Admin.Users.admin">Admin</message>
|
<message id="Lms.Admin.Users.admin">Admin</message>
|
||||||
|
|||||||
+10
-2
@@ -68,6 +68,14 @@
|
|||||||
<message id="Lms.Admin.Database.Status.status-scheduled">Planifié le {1}</message>
|
<message id="Lms.Admin.Database.Status.status-scheduled">Planifié le {1}</message>
|
||||||
<message id="Lms.Admin.Database.Status.status-in-progress">Scan de {1} fichiers sur {2} ({3} %)</message>
|
<message id="Lms.Admin.Database.Status.status-in-progress">Scan de {1} fichiers sur {2} ({3} %)</message>
|
||||||
|
|
||||||
|
<!--Subsonic-->
|
||||||
|
<message id="Lms.Admin.Subsonic.all-artists">Tous les artistes</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.release-artists">Artistes d'album</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.enable-api">Activer</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.artist-list-mode">Mode de listage des artistes</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.need-restart">Vous devez redémarrer le service pour que cette option soit prise en compte</message>
|
||||||
|
<message id="Lms.Admin.Subsonic.subsonic">API Subsonic</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">Ajouter</message>
|
<message id="Lms.Admin.Users.add">Ajouter</message>
|
||||||
<message id="Lms.Admin.Users.admin">Admin</message>
|
<message id="Lms.Admin.Users.admin">Admin</message>
|
||||||
@@ -116,8 +124,8 @@
|
|||||||
|
|
||||||
<!--Explore:Artists-->
|
<!--Explore:Artists-->
|
||||||
<message id="Lms.Explore.Artists.linktype-all">Tous les artistes</message>
|
<message id="Lms.Explore.Artists.linktype-all">Tous les artistes</message>
|
||||||
<message id="Lms.Explore.Artists.linktype-artist">Artiste de piste</message>
|
<message id="Lms.Explore.Artists.linktype-artist">Artistes de piste</message>
|
||||||
<message id="Lms.Explore.Artists.linktype-releaseartist">Artiste d'album</message>
|
<message id="Lms.Explore.Artists.linktype-releaseartist">Artistes d'album</message>
|
||||||
<message id="Lms.Explore.ArtistsInfo.recently-added">Ajouts récents</message>
|
<message id="Lms.Explore.ArtistsInfo.recently-added">Ajouts récents</message>
|
||||||
<message id="Lms.Explore.ArtistsInfo.most-played">Artistes populaires</message>
|
<message id="Lms.Explore.ArtistsInfo.most-played">Artistes populaires</message>
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ deploy-path = "/";
|
|||||||
# Acoustic brainz's root API
|
# Acoustic brainz's root API
|
||||||
acousticbrainz-api-url = "https://acousticbrainz.org/api/v1/";
|
acousticbrainz-api-url = "https://acousticbrainz.org/api/v1/";
|
||||||
|
|
||||||
# API
|
|
||||||
api-subsonic = true;
|
|
||||||
|
|
||||||
# Turn on this option to allow the demo account creation/use
|
# Turn on this option to allow the demo account creation/use
|
||||||
demo = false;
|
demo = false;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ add_library(lmsdatabase SHARED
|
|||||||
impl/Session.cpp
|
impl/Session.cpp
|
||||||
impl/SessionPool.cpp
|
impl/SessionPool.cpp
|
||||||
impl/SqlQuery.cpp
|
impl/SqlQuery.cpp
|
||||||
|
impl/SubsonicSettings.cpp
|
||||||
impl/Track.cpp
|
impl/Track.cpp
|
||||||
impl/TrackBookmark.cpp
|
impl/TrackBookmark.cpp
|
||||||
impl/User.cpp
|
impl/User.cpp
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "database/Db.hpp"
|
#include "database/Db.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/ScanSettings.hpp"
|
#include "database/ScanSettings.hpp"
|
||||||
|
#include "database/SubsonicSettings.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
#include "database/TrackBookmark.hpp"
|
#include "database/TrackBookmark.hpp"
|
||||||
#include "database/TrackArtistLink.hpp"
|
#include "database/TrackArtistLink.hpp"
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
|
|
||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
#define LMS_DATABASE_VERSION 18
|
#define LMS_DATABASE_VERSION 19
|
||||||
|
|
||||||
using Version = std::size_t;
|
using Version = std::size_t;
|
||||||
|
|
||||||
@@ -206,6 +207,16 @@ CREATE TABLE "release_backup" (
|
|||||||
// 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(*this).modify()->incScanVersion();
|
ScanSettings::get(*this).modify()->incScanVersion();
|
||||||
}
|
}
|
||||||
|
else if (version == 18)
|
||||||
|
{
|
||||||
|
_session.execute(R"(
|
||||||
|
CREATE TABLE IF NOT EXISTS "subsonic_settings" (
|
||||||
|
"id" integer primary key autoincrement,
|
||||||
|
"version" integer not null,
|
||||||
|
"api_enabled" boolean not null,
|
||||||
|
"artist_list_mode" integer not null
|
||||||
|
))");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
||||||
@@ -228,6 +239,7 @@ Session::Session(Db& db)
|
|||||||
_session.mapClass<ClusterType>("cluster_type");
|
_session.mapClass<ClusterType>("cluster_type");
|
||||||
_session.mapClass<Release>("release");
|
_session.mapClass<Release>("release");
|
||||||
_session.mapClass<ScanSettings>("scan_settings");
|
_session.mapClass<ScanSettings>("scan_settings");
|
||||||
|
_session.mapClass<SubsonicSettings>("subsonic_settings");
|
||||||
_session.mapClass<Track>("track");
|
_session.mapClass<Track>("track");
|
||||||
_session.mapClass<TrackBookmark>("track_bookmark");
|
_session.mapClass<TrackBookmark>("track_bookmark");
|
||||||
_session.mapClass<TrackArtistLink>("track_artist_link");
|
_session.mapClass<TrackArtistLink>("track_artist_link");
|
||||||
@@ -353,6 +365,7 @@ Session::prepareTables()
|
|||||||
auto uniqueTransaction {createUniqueTransaction()};
|
auto uniqueTransaction {createUniqueTransaction()};
|
||||||
|
|
||||||
ScanSettings::init(*this);
|
ScanSettings::init(*this);
|
||||||
|
SubsonicSettings::init(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "database/SubsonicSettings.hpp"
|
||||||
|
|
||||||
|
#include "database/Session.hpp"
|
||||||
|
|
||||||
|
namespace Database {
|
||||||
|
|
||||||
|
void
|
||||||
|
SubsonicSettings::init(Session& session)
|
||||||
|
{
|
||||||
|
session.checkUniqueLocked();
|
||||||
|
|
||||||
|
pointer settings {get(session)};
|
||||||
|
if (settings)
|
||||||
|
return;
|
||||||
|
|
||||||
|
settings = session.getDboSession().add(std::make_unique<SubsonicSettings>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SubsonicSettings::pointer
|
||||||
|
SubsonicSettings::get(Session& session)
|
||||||
|
{
|
||||||
|
session.checkSharedLocked();
|
||||||
|
|
||||||
|
return session.getDboSession().find<SubsonicSettings>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // ns Database
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Dbo.h>
|
||||||
|
|
||||||
|
namespace Database {
|
||||||
|
|
||||||
|
class Session;
|
||||||
|
|
||||||
|
class SubsonicSettings : public Wt::Dbo::Dbo<SubsonicSettings>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using pointer = Wt::Dbo::ptr<SubsonicSettings>;
|
||||||
|
|
||||||
|
enum class ArtistListMode
|
||||||
|
{
|
||||||
|
AllArtists,
|
||||||
|
ReleaseArtists,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline constexpr bool defaultSubsonicAPIEnabled {true};
|
||||||
|
static inline constexpr ArtistListMode defaultArtistListMode {ArtistListMode::AllArtists};
|
||||||
|
|
||||||
|
static void init(Session& session);
|
||||||
|
|
||||||
|
static pointer get(Session& session);
|
||||||
|
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
bool isAPIEnabled() const { return _isAPIEnabled; }
|
||||||
|
ArtistListMode getArtistListMode() const { return _artistListMode; }
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void setAPIEnabled(bool enabled) { _isAPIEnabled = enabled; }
|
||||||
|
void setArtistListMode(ArtistListMode mode) {_artistListMode = mode; }
|
||||||
|
|
||||||
|
template<class Action>
|
||||||
|
void persist(Action& a)
|
||||||
|
{
|
||||||
|
Wt::Dbo::field(a, _isAPIEnabled, "api_enabled");
|
||||||
|
Wt::Dbo::field(a, _artistListMode, "artist_list_mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool _isAPIEnabled {defaultSubsonicAPIEnabled};
|
||||||
|
ArtistListMode _artistListMode {defaultArtistListMode};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Database
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "database/Db.hpp"
|
#include "database/Db.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
#include "database/SubsonicSettings.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
#include "database/TrackBookmark.hpp"
|
#include "database/TrackBookmark.hpp"
|
||||||
#include "database/TrackList.hpp"
|
#include "database/TrackList.hpp"
|
||||||
@@ -901,7 +902,23 @@ handleGetArtistsRequest(RequestContext& context)
|
|||||||
if (!user)
|
if (!user)
|
||||||
throw UserNotAuthorizedError {};
|
throw UserNotAuthorizedError {};
|
||||||
|
|
||||||
auto artists {Artist::getAll(context.dbSession, Artist::SortMethod::BySortName)};
|
std::optional<TrackArtistLink::Type> linkType;
|
||||||
|
switch (SubsonicSettings::get(context.dbSession)->getArtistListMode())
|
||||||
|
{
|
||||||
|
case SubsonicSettings::ArtistListMode::AllArtists:
|
||||||
|
break;
|
||||||
|
case SubsonicSettings::ArtistListMode::ReleaseArtists:
|
||||||
|
linkType = TrackArtistLink::Type::ReleaseArtist;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool more {};
|
||||||
|
const std::vector<Artist::pointer> artists {Artist::getByFilter(context.dbSession,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
linkType,
|
||||||
|
Artist::SortMethod::BySortName,
|
||||||
|
{}, {}, more)};
|
||||||
for (const Artist::pointer& artist : artists)
|
for (const Artist::pointer& artist : artists)
|
||||||
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, true /* id3 */));
|
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, true /* id3 */));
|
||||||
|
|
||||||
@@ -1028,7 +1045,23 @@ handleGetIndexesRequest(RequestContext& context)
|
|||||||
if (!user)
|
if (!user)
|
||||||
throw UserNotAuthorizedError {};
|
throw UserNotAuthorizedError {};
|
||||||
|
|
||||||
auto artists {Artist::getAll(context.dbSession, Artist::SortMethod::BySortName)};
|
std::optional<TrackArtistLink::Type> linkType;
|
||||||
|
switch (SubsonicSettings::get(context.dbSession)->getArtistListMode())
|
||||||
|
{
|
||||||
|
case SubsonicSettings::ArtistListMode::AllArtists:
|
||||||
|
break;
|
||||||
|
case SubsonicSettings::ArtistListMode::ReleaseArtists:
|
||||||
|
linkType = TrackArtistLink::Type::ReleaseArtist;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool more {};
|
||||||
|
const std::vector<Artist::pointer> artists {Artist::getByFilter(context.dbSession,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
linkType,
|
||||||
|
Artist::SortMethod::BySortName,
|
||||||
|
{}, {}, more)};
|
||||||
for (const Artist::pointer& artist : artists)
|
for (const Artist::pointer& artist : artists)
|
||||||
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, false /* no id3 */));
|
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, false /* no id3 */));
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ add_executable(lms
|
|||||||
ui/admin/DatabaseSettingsView.cpp
|
ui/admin/DatabaseSettingsView.cpp
|
||||||
ui/admin/DatabaseStatus.cpp
|
ui/admin/DatabaseStatus.cpp
|
||||||
ui/admin/InitWizardView.cpp
|
ui/admin/InitWizardView.cpp
|
||||||
|
ui/admin/SubsonicView.cpp
|
||||||
ui/admin/UserView.cpp
|
ui/admin/UserView.cpp
|
||||||
ui/admin/UsersView.cpp
|
ui/admin/UsersView.cpp
|
||||||
ui/common/Validators.cpp
|
ui/common/Validators.cpp
|
||||||
|
|||||||
+8
-2
@@ -28,6 +28,7 @@
|
|||||||
#include "av/AvTranscoder.hpp"
|
#include "av/AvTranscoder.hpp"
|
||||||
#include "cover/ICoverArtGrabber.hpp"
|
#include "cover/ICoverArtGrabber.hpp"
|
||||||
#include "database/Db.hpp"
|
#include "database/Db.hpp"
|
||||||
|
#include "database/SubsonicSettings.hpp"
|
||||||
#include "scanner/IMediaScanner.hpp"
|
#include "scanner/IMediaScanner.hpp"
|
||||||
#include "recommendation/IEngine.hpp"
|
#include "recommendation/IEngine.hpp"
|
||||||
#include "subsonic/SubsonicResource.hpp"
|
#include "subsonic/SubsonicResource.hpp"
|
||||||
@@ -169,8 +170,13 @@ int main(int argc, char* argv[])
|
|||||||
API::Subsonic::SubsonicResource subsonicResource {database};
|
API::Subsonic::SubsonicResource subsonicResource {database};
|
||||||
|
|
||||||
// bind API resources
|
// bind API resources
|
||||||
if (ServiceProvider<IConfig>::get()->getBool("api-subsonic", true))
|
{
|
||||||
server.addResource(&subsonicResource, subsonicResource.getPath());
|
Database::Session session {database};
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
if (Database::SubsonicSettings::get(session)->isAPIEnabled())
|
||||||
|
server.addResource(&subsonicResource, subsonicResource.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
// bind UI entry point
|
// bind UI entry point
|
||||||
server.addEntryPoint(Wt::EntryPointType::Application,
|
server.addEntryPoint(Wt::EntryPointType::Application,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
#include "admin/DatabaseSettingsView.hpp"
|
#include "admin/DatabaseSettingsView.hpp"
|
||||||
#include "admin/UserView.hpp"
|
#include "admin/UserView.hpp"
|
||||||
#include "admin/UsersView.hpp"
|
#include "admin/UsersView.hpp"
|
||||||
|
#include "admin/SubsonicView.hpp"
|
||||||
#include "resource/AudioFileResource.hpp"
|
#include "resource/AudioFileResource.hpp"
|
||||||
#include "resource/AudioTranscodeResource.hpp"
|
#include "resource/AudioTranscodeResource.hpp"
|
||||||
#include "resource/ImageResource.hpp"
|
#include "resource/ImageResource.hpp"
|
||||||
@@ -121,9 +122,10 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
|
|||||||
|
|
||||||
// Add a resource bundle
|
// Add a resource bundle
|
||||||
messageResourceBundle().use(appRoot() + "admin-database");
|
messageResourceBundle().use(appRoot() + "admin-database");
|
||||||
|
messageResourceBundle().use(appRoot() + "admin-initwizard");
|
||||||
|
messageResourceBundle().use(appRoot() + "admin-subsonic");
|
||||||
messageResourceBundle().use(appRoot() + "admin-user");
|
messageResourceBundle().use(appRoot() + "admin-user");
|
||||||
messageResourceBundle().use(appRoot() + "admin-users");
|
messageResourceBundle().use(appRoot() + "admin-users");
|
||||||
messageResourceBundle().use(appRoot() + "admin-initwizard");
|
|
||||||
messageResourceBundle().use(appRoot() + "artist");
|
messageResourceBundle().use(appRoot() + "artist");
|
||||||
messageResourceBundle().use(appRoot() + "artistinfo");
|
messageResourceBundle().use(appRoot() + "artistinfo");
|
||||||
messageResourceBundle().use(appRoot() + "artistlink");
|
messageResourceBundle().use(appRoot() + "artistlink");
|
||||||
@@ -342,6 +344,7 @@ enum IdxRoot
|
|||||||
IdxAdminDatabase,
|
IdxAdminDatabase,
|
||||||
IdxAdminUsers,
|
IdxAdminUsers,
|
||||||
IdxAdminUser,
|
IdxAdminUser,
|
||||||
|
IdxAdminSubsonic,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -365,6 +368,7 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
|
|||||||
{ "/admin/database", IdxAdminDatabase, true },
|
{ "/admin/database", IdxAdminDatabase, true },
|
||||||
{ "/admin/users", IdxAdminUsers, true },
|
{ "/admin/users", IdxAdminUsers, true },
|
||||||
{ "/admin/user", IdxAdminUser, true },
|
{ "/admin/user", IdxAdminUser, true },
|
||||||
|
{ "/admin/subsonic", IdxAdminSubsonic, true },
|
||||||
};
|
};
|
||||||
|
|
||||||
LMS_LOG(UI, DEBUG) << "Internal path changed to '" << wApp->internalPath() << "'";
|
LMS_LOG(UI, DEBUG) << "Internal path changed to '" << wApp->internalPath() << "'";
|
||||||
@@ -484,6 +488,10 @@ LmsApplication::createHome()
|
|||||||
usersSettings->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/admin/users"));
|
usersSettings->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/admin/users"));
|
||||||
usersSettings->setSelectable(false);
|
usersSettings->setSelectable(false);
|
||||||
|
|
||||||
|
auto subsonicSettings = admin->insertItem(2, Wt::WString::tr("Lms.Admin.Subsonic.subsonic"));
|
||||||
|
subsonicSettings->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/admin/subsonic"));
|
||||||
|
subsonicSettings->setSelectable(false);
|
||||||
|
|
||||||
menuItem->setMenu(std::move(admin));
|
menuItem->setMenu(std::move(admin));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,6 +522,7 @@ LmsApplication::createHome()
|
|||||||
mainStack->addNew<DatabaseSettingsView>();
|
mainStack->addNew<DatabaseSettingsView>();
|
||||||
mainStack->addNew<UsersView>();
|
mainStack->addNew<UsersView>();
|
||||||
mainStack->addNew<UserView>();
|
mainStack->addNew<UserView>();
|
||||||
|
mainStack->addNew<SubsonicView>();
|
||||||
}
|
}
|
||||||
|
|
||||||
explore->tracksAdd.connect([=] (const std::vector<Database::IdType>& trackIds)
|
explore->tracksAdd.connect([=] (const std::vector<Database::IdType>& trackIds)
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 "SubsonicView.hpp"
|
||||||
|
|
||||||
|
#include <Wt/WCheckBox.h>
|
||||||
|
#include <Wt/WComboBox.h>
|
||||||
|
#include <Wt/WPushButton.h>
|
||||||
|
#include <Wt/WTemplateFormView.h>
|
||||||
|
|
||||||
|
#include <Wt/WFormModel.h>
|
||||||
|
|
||||||
|
#include "database/SubsonicSettings.hpp"
|
||||||
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
|
#include "common/ValueStringModel.hpp"
|
||||||
|
#include "LmsApplication.hpp"
|
||||||
|
|
||||||
|
namespace UserInterface {
|
||||||
|
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
class SubsonicModel : public Wt::WFormModel
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
static inline const Field EnableField {"enable"};
|
||||||
|
static inline const Field ArtistListModeField {"artist-list-mode"};
|
||||||
|
|
||||||
|
SubsonicModel()
|
||||||
|
{
|
||||||
|
addField(EnableField);
|
||||||
|
addField(ArtistListModeField);
|
||||||
|
|
||||||
|
initializeModels();
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Wt::WAbstractItemModel> getArtistListModeModel() { return _artistListModeModel; }
|
||||||
|
|
||||||
|
void saveData()
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
||||||
|
|
||||||
|
auto settings {SubsonicSettings::get(LmsApp->getDbSession())};
|
||||||
|
|
||||||
|
settings.modify()->setAPIEnabled(Wt::asNumber(value(EnableField)));
|
||||||
|
auto artistListModeRow {_artistListModeModel->getRowFromString(valueText(ArtistListModeField))};
|
||||||
|
if (artistListModeRow)
|
||||||
|
settings.modify()->setArtistListMode(_artistListModeModel->getValue(*artistListModeRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadData()
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
const auto settings {SubsonicSettings::get(LmsApp->getDbSession())};
|
||||||
|
|
||||||
|
setValue(EnableField, settings->isAPIEnabled());
|
||||||
|
auto artistListModeRow {_artistListModeModel->getRowFromValue(settings->getArtistListMode())};
|
||||||
|
|
||||||
|
if (artistListModeRow)
|
||||||
|
setValue(ArtistListModeField, _artistListModeModel->getString(*artistListModeRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeModels()
|
||||||
|
{
|
||||||
|
_artistListModeModel = std::make_shared<ValueStringModel<SubsonicSettings::ArtistListMode>>();
|
||||||
|
|
||||||
|
_artistListModeModel->add(Wt::WString::tr("Lms.Admin.Subsonic.all-artists"), SubsonicSettings::ArtistListMode::AllArtists);
|
||||||
|
_artistListModeModel->add(Wt::WString::tr("Lms.Admin.Subsonic.release-artists"), SubsonicSettings::ArtistListMode::ReleaseArtists);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool validateField(Field field) override
|
||||||
|
{
|
||||||
|
if (field == EnableField)
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
if (SubsonicSettings::get(LmsApp->getDbSession())->isAPIEnabled() != Wt::asNumber(value(EnableField)))
|
||||||
|
setValidation(field, Wt::WValidator::Result( Wt::ValidationState::Valid, Wt::WString::tr("Lms.Admin.Subsonic.need-restart")));
|
||||||
|
else
|
||||||
|
setValidation(field, Wt::WValidator::Result( Wt::ValidationState::Valid));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wt::WFormModel::validateField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ValueStringModel<SubsonicSettings::ArtistListMode>> _artistListModeModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
SubsonicView::SubsonicView()
|
||||||
|
{
|
||||||
|
wApp->internalPathChanged().connect([this]()
|
||||||
|
{
|
||||||
|
refreshView();
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubsonicView::refreshView()
|
||||||
|
{
|
||||||
|
if (!wApp->internalPathMatches("/admin/subsonic"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
Wt::WTemplateFormView* t {addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Admin.Subsonic.template"))};
|
||||||
|
|
||||||
|
auto model {std::make_shared<SubsonicModel>()};
|
||||||
|
|
||||||
|
// Enable
|
||||||
|
auto enable {std::make_unique<Wt::WCheckBox>()};
|
||||||
|
auto* enableRaw {enable.get()};
|
||||||
|
t->setFormWidget(SubsonicModel::EnableField, std::move(enable));
|
||||||
|
|
||||||
|
// Artis tlist mode
|
||||||
|
auto artistListMode = std::make_unique<Wt::WComboBox>();
|
||||||
|
artistListMode->setModel(model->getArtistListModeModel());
|
||||||
|
t->setFormWidget(SubsonicModel::ArtistListModeField, std::move(artistListMode));
|
||||||
|
|
||||||
|
enableRaw->changed().connect([=]()
|
||||||
|
{
|
||||||
|
bool enable {enableRaw->checkState() == Wt::CheckState::Checked};
|
||||||
|
model->setReadOnly(SubsonicModel::ArtistListModeField, !enable);
|
||||||
|
t->updateModel(model.get());
|
||||||
|
t->updateView(model.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
Wt::WPushButton* saveBtn = t->bindNew<Wt::WPushButton>("apply-btn", Wt::WString::tr("Lms.apply"));
|
||||||
|
Wt::WPushButton *discardBtn {t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")))};
|
||||||
|
|
||||||
|
saveBtn->clicked().connect([=]()
|
||||||
|
{
|
||||||
|
t->updateModel(model.get());
|
||||||
|
|
||||||
|
if (model->validate())
|
||||||
|
{
|
||||||
|
model->saveData();
|
||||||
|
LmsApp->notifyMsg(MsgType::Success, Wt::WString::tr("Lms.Settings.settings-saved"));
|
||||||
|
}
|
||||||
|
t->updateView(model.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
discardBtn->clicked().connect([=]()
|
||||||
|
{
|
||||||
|
model->loadData();
|
||||||
|
model->validate();
|
||||||
|
t->updateView(model.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
t->updateView(model.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Wt/WContainerWidget.h>
|
||||||
|
|
||||||
|
namespace UserInterface {
|
||||||
|
|
||||||
|
class SubsonicView : public Wt::WContainerWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SubsonicView();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refreshView();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "UserView.hpp"
|
#include "UserView.hpp"
|
||||||
|
|
||||||
#include <Wt/WApplication.h>
|
|
||||||
#include <Wt/WCheckBox.h>
|
#include <Wt/WCheckBox.h>
|
||||||
#include <Wt/WComboBox.h>
|
#include <Wt/WComboBox.h>
|
||||||
#include <Wt/WLineEdit.h>
|
#include <Wt/WLineEdit.h>
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ UsersView::UsersView()
|
|||||||
_container = bindNew<Wt::WContainerWidget>("users");
|
_container = bindNew<Wt::WContainerWidget>("users");
|
||||||
|
|
||||||
Wt::WPushButton* addBtn = bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.Admin.Users.add"));
|
Wt::WPushButton* addBtn = bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.Admin.Users.add"));
|
||||||
addBtn->clicked().connect(std::bind([=]
|
addBtn->clicked().connect([]()
|
||||||
{
|
{
|
||||||
LmsApp->setInternalPath("/admin/user", true);
|
LmsApp->setInternalPath("/admin/user", true);
|
||||||
}));
|
});
|
||||||
|
|
||||||
wApp->internalPathChanged().connect(std::bind([=]
|
wApp->internalPathChanged().connect([this]()
|
||||||
{
|
{
|
||||||
refreshView();
|
refreshView();
|
||||||
}));
|
});
|
||||||
|
|
||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
@@ -83,10 +83,10 @@ UsersView::refreshView()
|
|||||||
|
|
||||||
entry->setCondition("if-edit", true);
|
entry->setCondition("if-edit", true);
|
||||||
Wt::WPushButton* editBtn = entry->bindNew<Wt::WPushButton>("edit-btn", Wt::WString::tr("Lms.Admin.Users.edit"));
|
Wt::WPushButton* editBtn = entry->bindNew<Wt::WPushButton>("edit-btn", Wt::WString::tr("Lms.Admin.Users.edit"));
|
||||||
editBtn->clicked().connect(std::bind([=]
|
editBtn->clicked().connect([=]()
|
||||||
{
|
{
|
||||||
LmsApp->setInternalPath("/admin/user/" + std::to_string(user.id()), true);
|
LmsApp->setInternalPath("/admin/user/" + std::to_string(userId), true);
|
||||||
}));
|
});
|
||||||
|
|
||||||
Wt::WPushButton* delBtn = entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.Admin.Users.del"));
|
Wt::WPushButton* delBtn = entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.Admin.Users.del"));
|
||||||
delBtn->clicked().connect([=]
|
delBtn->clicked().connect([=]
|
||||||
|
|||||||
Reference in New Issue
Block a user