Skip to content

Commit

Permalink
Merge branch 'develop' for release v3.57.0
Browse files Browse the repository at this point in the history
  • Loading branch information
epoupon committed Aug 30, 2024
2 parents 0f5c665 + 24e867e commit da39c8d
Show file tree
Hide file tree
Showing 42 changed files with 1,213 additions and 43 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules/)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
if (UNIX)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
endif ()

option(ENABLE_TESTS "Enable tests" ON)
Expand Down
36 changes: 19 additions & 17 deletions SUBSONIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,42 @@ Given the API limitations of folder navigation commands, it is recommended to pl

The Subsonic API is enabled by default.

__Note__: since _LMS_ may store hashed and salted passwords or may forward authentication requests to external services, it cannot handle the __token authentication__ method. You may need to check your client to make sure to use the __password__ authentication method. Since logins/passwords are passed in plain text through URLs, it is highly recommended to use a unique password when using the Subsonic API. Note that this may affect the use of authentication via PAM. In any case, ensure that read access to the web server logs (and to the proxy, if applicable) is well protected.
__Note__: since _LMS_ may store hashed and salted passwords or may forward authentication requests to external services, it cannot handle the __token authentication__ method. You may need to check your client to make sure to use the __password__ authentication method. Since logins/passwords are passed in plain text through URLs, it is highly recommended to use a unique password when using the Subsonic API. Note that this may affect the use of authentication via PAM. In any case, ensure the web server logs (and proxy logs, if applicable) are properly secured.

# OpenSubsonic API
OpenSubsonic is an initiative to patch and extend the legacy Subsonic API. You'll find more details in the [official documentation](https://opensubsonic.netlify.app/)

## Extra fields
The following extra fields are implemented:
* `Album` response:
* `mediaType`
* `played`
* `musicBrainzId`
* `genres`
* `artists`
* `discTitles`: discs with no subtitle are omitted
* `displayArtist`
* `releaseTypes`
* `genres`
* `isCompilation`
* `played`
* `mediaType`
* `moods`
* `musicBrainzId`
* `originalReleaseDate`
* `isCompilation`
* `discTitles`: discs with no subtitle are omitted
* `releaseTypes`
* `userRating`
* `Child` response:
* `albumArtists`
* `artists`
* `bitDepth`
* `samplingRate`
* `channelCount`
* `mediaType`
* `played`
* `musicBrainzId`: note this is actually the recording MBID when this response refers to a song
* `genres`
* `artists`
* `displayArtist`
* `albumArtists`
* `displayAlbumArtist`
* `comment`
* `contributors`
* `displayAlbumArtist`
* `displayArtist`
* `genres`
* `mediaType`
* `moods`
* `musicBrainzId`: note this is actually the recording MBID when this response refers to a song
* `played`
* `replayGain`
* `samplingRate`
* `Artist` response:
* `mediaType`
* `musicBrainzId`
Expand Down
4 changes: 4 additions & 0 deletions approot/tracks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@
${playcount}
</div>
</div>
${<if-has-comment>}
<hr/>
<pre>${comment}</pre>
${</if-has-comment>}
</div>
</div>
<div class="modal-footer">
Expand Down
3 changes: 3 additions & 0 deletions src/libs/database/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ add_library(lmsdatabase SHARED
impl/TrackArtistLink.cpp
impl/TrackFeatures.cpp
impl/TrackList.cpp
impl/RatedArtist.cpp
impl/RatedRelease.cpp
impl/RatedTrack.cpp
impl/Release.cpp
impl/ScanSettings.cpp
impl/Session.cpp
Expand Down
54 changes: 53 additions & 1 deletion src/libs/database/impl/Migration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace lms::db
{
namespace
{
static constexpr Version LMS_DATABASE_VERSION{ 62 };
static constexpr Version LMS_DATABASE_VERSION{ 64 };
}

VersionInfo::VersionInfo()
Expand Down Expand Up @@ -664,6 +664,56 @@ SELECT
session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1");
}

void migrateFromV62(Session& session)
{
// Add a new column comment
session.getDboSession()->execute("ALTER TABLE track ADD comment TEXT NOT NULL DEFAULT ''");

// Just increment the scan version of the settings to make the next scheduled scan rescan everything
session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1");
}

void migrateFromV63(Session& session)
{
// Add a rated entities

session.getDboSession()->execute(R"(CREATE TABLE IF NOT EXISTS "rated_artist" (
"id" integer primary key autoincrement,
"version" integer not null,
"rating" integer not null,
"last_updated" text,
"artist_id" bigint,
"user_id" bigint,
constraint "fk_rated_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
constraint "fk_rated_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))");

session.getDboSession()->execute(R"(CREATE TABLE IF NOT EXISTS "rated_release" (
"id" integer primary key autoincrement,
"version" integer not null,
"rating" integer not null,
"last_updated" text,
"release_id" bigint,
"user_id" bigint,
constraint "fk_rated_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
constraint "fk_rated_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))");

session.getDboSession()->execute(R"(CREATE TABLE IF NOT EXISTS "rated_track" (
"id" integer primary key autoincrement,
"version" integer not null,
"rating" bigint not null,
"last_updated" text,
"track_id" bigint,
"user_id" bigint,
constraint "fk_rated_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
constraint "fk_rated_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
))");

// Drop badly named index, will be recreated
session.getDboSession()->execute("DROP INDEX IF EXISTS listen_user_backend_date_time");
}

bool doDbMigration(Session& session)
{
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
Expand Down Expand Up @@ -702,6 +752,8 @@ SELECT
{ 59, migrateFromV59 },
{ 60, migrateFromV60 },
{ 61, migrateFromV61 },
{ 62, migrateFromV62 },
{ 63, migrateFromV63 },
};

bool migrationPerformed{};
Expand Down
78 changes: 78 additions & 0 deletions src/libs/database/impl/RatedArtist.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2021 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/RatedArtist.hpp"

#include <Wt/Dbo/WtSqlTraits.h>

#include "database/Artist.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"

#include "IdTypeTraits.hpp"
#include "Utils.hpp"

namespace lms::db
{
RatedArtist::RatedArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user)
: _artist{ getDboPtr(artist) }
, _user{ getDboPtr(user) }
{
}

RatedArtist::pointer RatedArtist::create(Session& session, ObjectPtr<Artist> artist, ObjectPtr<User> user)
{
return session.getDboSession()->add(std::unique_ptr<RatedArtist>{ new RatedArtist{ artist, user } });
}

std::size_t RatedArtist::getCount(Session& session)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM rated_artist"));
}

RatedArtist::pointer RatedArtist::find(Session& session, RatedArtistId id)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<RatedArtist>().where("id = ?").bind(id));
}

RatedArtist::pointer RatedArtist::find(Session& session, ArtistId artistId, UserId userId)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<RatedArtist>().where("artist_id = ?").bind(artistId).where("user_id = ?").bind(userId));
}

void RatedArtist::find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func)
{
session.checkReadTransaction();

auto query{ session.getDboSession()->query<Wt::Dbo::ptr<RatedArtist>>("SELECT r_a FROM rated_artist r_a") };

if (params.user.isValid())
query.where("r_a.user_id = ?").bind(params.user);

utils::forEachQueryRangeResult(query, params.range, func);
}

void RatedArtist::setLastUpdated(const Wt::WDateTime& lastUpdated)
{
_lastUpdated = utils::normalizeDateTime(lastUpdated);
}
} // namespace lms::db
78 changes: 78 additions & 0 deletions src/libs/database/impl/RatedRelease.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2021 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/RatedRelease.hpp"

#include <Wt/Dbo/WtSqlTraits.h>

#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"

#include "IdTypeTraits.hpp"
#include "Utils.hpp"

namespace lms::db
{
RatedRelease::RatedRelease(ObjectPtr<Release> release, ObjectPtr<User> user)
: _release{ getDboPtr(release) }
, _user{ getDboPtr(user) }
{
}

RatedRelease::pointer RatedRelease::create(Session& session, ObjectPtr<Release> release, ObjectPtr<User> user)
{
return session.getDboSession()->add(std::unique_ptr<RatedRelease>{ new RatedRelease{ release, user } });
}

std::size_t RatedRelease::getCount(Session& session)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM rated_release"));
}

RatedRelease::pointer RatedRelease::find(Session& session, RatedReleaseId id)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<RatedRelease>().where("id = ?").bind(id));
}

RatedRelease::pointer RatedRelease::find(Session& session, ReleaseId releaseId, UserId userId)
{
session.checkReadTransaction();
return utils::fetchQuerySingleResult(session.getDboSession()->find<RatedRelease>().where("release_id = ?").bind(releaseId).where("user_id = ?").bind(userId));
}

void RatedRelease::find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func)
{
session.checkReadTransaction();

auto query{ session.getDboSession()->query<Wt::Dbo::ptr<RatedRelease>>("SELECT r_r FROM rated_release r_r") };

if (params.user.isValid())
query.where("r_r.user_id = ?").bind(params.user);

utils::forEachQueryRangeResult(query, params.range, func);
}

void RatedRelease::setLastUpdated(const Wt::WDateTime& lastUpdated)
{
_lastUpdated = utils::normalizeDateTime(lastUpdated);
}
} // namespace lms::db
Loading

0 comments on commit da39c8d

Please sign in to comment.