Merge branch 'develop' for release v3.39.0
This commit is contained in:
+3
-3
@@ -72,7 +72,7 @@ RUN \
|
|||||||
--enable-protocol=file,pipe \
|
--enable-protocol=file,pipe \
|
||||||
--enable-filter=aresample \
|
--enable-filter=aresample \
|
||||||
--extra-libs=-ldl && \
|
--extra-libs=-ldl && \
|
||||||
make && \
|
make -j$(nproc) && \
|
||||||
make install && \
|
make install && \
|
||||||
make distclean
|
make distclean
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ RUN \
|
|||||||
RUN \
|
RUN \
|
||||||
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} -DBUILD_EXAMPLES=OFF -DENABLE_LIBWTTEST=OFF -DCONNECTOR_FCGI=OFF && \
|
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} -DBUILD_EXAMPLES=OFF -DENABLE_LIBWTTEST=OFF -DCONNECTOR_FCGI=OFF && \
|
||||||
make && \
|
make -j$(nproc) && \
|
||||||
make install
|
make install
|
||||||
|
|
||||||
# STB
|
# STB
|
||||||
@@ -103,7 +103,7 @@ COPY . /tmp/lms/
|
|||||||
RUN \
|
RUN \
|
||||||
DIR=/tmp/lms/build && mkdir -p ${DIR} && cd ${DIR} && \
|
DIR=/tmp/lms/build && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
PKG_CONFIG_PATH=/tmp/install/lib/pkgconfig CXXFLAGS="-I${PREFIX}/include" LDFLAGS="-L${PREFIX}/lib -Wl,--rpath-link=${PREFIX}/lib" cmake /tmp/lms/ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_PREFIX_PATH=${PREFIX} && \
|
PKG_CONFIG_PATH=/tmp/install/lib/pkgconfig CXXFLAGS="-I${PREFIX}/include" LDFLAGS="-L${PREFIX}/lib -Wl,--rpath-link=${PREFIX}/lib" cmake /tmp/lms/ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_PREFIX_PATH=${PREFIX} && \
|
||||||
LD_LIBRARY_PATH=${PREFIX}/lib VERBOSE=1 make && \
|
LD_LIBRARY_PATH=${PREFIX}/lib VERBOSE=1 make -j$(nproc) && \
|
||||||
LD_LIBRARY_PATH=${PREFIX}/lib make test && \
|
LD_LIBRARY_PATH=${PREFIX}/lib make test && \
|
||||||
make install && \
|
make install && \
|
||||||
mkdir -p ${PREFIX}/etc/ && \
|
mkdir -p ${PREFIX}/etc/ && \
|
||||||
|
|||||||
+2
-1
@@ -10,8 +10,9 @@
|
|||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
<li>${star class="dropdown-item"}</li>
|
<li>${star class="dropdown-item"}</li>
|
||||||
${<if-has-mbid>}
|
${<if-has-mbid>}
|
||||||
<li><a href="${mbid-link}" target="_blank" class="dropdown-item">${tr:Lms.Explore.musicbrainz-artist}</a></li>
|
<li><a href="${mbid-link}" target="_blank" class="dropdown-item">${tr:Lms.Explore.musicbrainz-artist}</a></li>
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
<message id="Lms.Explore.play">Play</message>
|
<message id="Lms.Explore.play">Play</message>
|
||||||
<message id="Lms.Explore.play-last">Play last</message>
|
<message id="Lms.Explore.play-last">Play last</message>
|
||||||
|
<message id="Lms.Explore.play-next">Play next</message>
|
||||||
<message id="Lms.Explore.play-shuffled">Play shuffled</message>
|
<message id="Lms.Explore.play-shuffled">Play shuffled</message>
|
||||||
<message id="Lms.Explore.random">Random</message>
|
<message id="Lms.Explore.random">Random</message>
|
||||||
<message id="Lms.Explore.recently-added">Recently added</message>
|
<message id="Lms.Explore.recently-added">Recently added</message>
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
<message id="Lms.Explore.play">Jouer</message>
|
<message id="Lms.Explore.play">Jouer</message>
|
||||||
<message id="Lms.Explore.play-last">Jouer en dernier</message>
|
<message id="Lms.Explore.play-last">Jouer en dernier</message>
|
||||||
|
<message id="Lms.Explore.play-next">Jouer ensuite</message>
|
||||||
<message id="Lms.Explore.play-shuffled">Jouer aléatoirement</message>
|
<message id="Lms.Explore.play-shuffled">Jouer aléatoirement</message>
|
||||||
<message id="Lms.Explore.random">Aléatoire</message>
|
<message id="Lms.Explore.random">Aléatoire</message>
|
||||||
<message id="Lms.Explore.recently-added">Ajoutés récemment</message>
|
<message id="Lms.Explore.recently-added">Ajoutés récemment</message>
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
<message id="Lms.Explore.musicbrainz-release">Album MusicBrainz</message>
|
<message id="Lms.Explore.musicbrainz-release">Album MusicBrainz</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.play-last">Aggiungi alla coda di riproduzione</message>
|
<message id="Lms.Explore.play-last">Aggiungi alla coda di riproduzione</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.play-shuffled">Aggiungi alla coda di riproduzione mischiando</message>
|
<message id="Lms.Explore.play-shuffled">Aggiungi alla coda di riproduzione mischiando</message>
|
||||||
<message id="Lms.Explore.random">Casuali</message>
|
<message id="Lms.Explore.random">Casuali</message>
|
||||||
<message id="Lms.Explore.recently-added">Aggiunti di recente</message>
|
<message id="Lms.Explore.recently-added">Aggiunti di recente</message>
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz 专辑</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz 专辑</message>
|
||||||
<message id="Lms.Explore.play">播放</message>
|
<message id="Lms.Explore.play">播放</message>
|
||||||
<message id="Lms.Explore.play-last">最后播放</message>
|
<message id="Lms.Explore.play-last">最后播放</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.play-shuffled">随机播放</message>
|
<message id="Lms.Explore.play-shuffled">随机播放</message>
|
||||||
<message id="Lms.Explore.random">随机</message>
|
<message id="Lms.Explore.random">随机</message>
|
||||||
<message id="Lms.Explore.recently-added">最近添加</message>
|
<message id="Lms.Explore.recently-added">最近添加</message>
|
||||||
|
|||||||
+4
-2
@@ -21,8 +21,9 @@
|
|||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
<li>${star class="dropdown-item"}</li>
|
<li>${star class="dropdown-item"}</li>
|
||||||
${<if-has-mbid>}
|
${<if-has-mbid>}
|
||||||
<li><a href="${mbid-link}" target="_blank" class="dropdown-item">${tr:Lms.Explore.musicbrainz-release}</a></li>
|
<li><a href="${mbid-link}" target="_blank" class="dropdown-item">${tr:Lms.Explore.musicbrainz-release}</a></li>
|
||||||
@@ -82,7 +83,8 @@
|
|||||||
<div class="dropdown d-inline-block">
|
<div class="dropdown d-inline-block">
|
||||||
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
||||||
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
||||||
<li>${play class="dropdown-item"}</li>
|
<li class="d-block d-sm-none">${play class="dropdown-item"}</li>
|
||||||
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
<li>${star class="dropdown-item"}</li>
|
<li>${star class="dropdown-item"}</li>
|
||||||
<li>${download class="dropdown-item"}</li>
|
<li>${download class="dropdown-item"}</li>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
+3
-1
@@ -29,6 +29,7 @@
|
|||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -69,7 +70,8 @@
|
|||||||
<div class="dropdown d-inline-block">
|
<div class="dropdown d-inline-block">
|
||||||
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
||||||
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
||||||
<li>${play class="dropdown-item"}</li>
|
<li class="d-block d-sm-none">${play class="dropdown-item"}</li>
|
||||||
|
<li>${play-next class="dropdown-item"}</li>
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
<li>${star class="dropdown-item"}</li>
|
<li>${star class="dropdown-item"}</li>
|
||||||
<li>${download class="dropdown-item"}</li>
|
<li>${download class="dropdown-item"}</li>
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ TrackList::getEntry(std::size_t pos) const
|
|||||||
{
|
{
|
||||||
TrackListEntry::pointer res;
|
TrackListEntry::pointer res;
|
||||||
|
|
||||||
auto entries = getEntries(pos, 1);
|
auto entries = getEntries(Range {pos, 1});
|
||||||
if (!entries.empty())
|
if (!entries.empty())
|
||||||
res = entries.front();
|
res = entries.front();
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ TrackList::getEntry(std::size_t pos) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TrackListEntry::pointer>
|
std::vector<TrackListEntry::pointer>
|
||||||
TrackList::getEntries(std::optional<std::size_t> offset, std::optional<std::size_t> size) const
|
TrackList::getEntries(std::optional<Range> range) const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
|
|
||||||
@@ -165,8 +165,8 @@ TrackList::getEntries(std::optional<std::size_t> offset, std::optional<std::size
|
|||||||
session()->find<TrackListEntry>()
|
session()->find<TrackListEntry>()
|
||||||
.where("tracklist_id = ?").bind(getId())
|
.where("tracklist_id = ?").bind(getId())
|
||||||
.orderBy("id")
|
.orderBy("id")
|
||||||
.limit(size ? static_cast<int>(*size) : -1)
|
.limit(range ? static_cast<int>(range->size) + 1 : -1)
|
||||||
.offset(offset ? static_cast<int>(*offset) : -1)
|
.offset(range ? static_cast<int>(range->offset) : -1)
|
||||||
.resultList()};
|
.resultList()};
|
||||||
|
|
||||||
return std::vector<TrackListEntry::pointer>(entries.begin(), entries.end());
|
return std::vector<TrackListEntry::pointer>(entries.begin(), entries.end());
|
||||||
@@ -671,7 +671,7 @@ TrackListEntry::TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> trac
|
|||||||
TrackListEntry::pointer
|
TrackListEntry::pointer
|
||||||
TrackListEntry::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
|
TrackListEntry::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
|
||||||
{
|
{
|
||||||
return session.getDboSession().add(std::unique_ptr<TrackListEntry>( new TrackListEntry {track, tracklist, dateTime}));
|
return session.getDboSession().add(std::unique_ptr<TrackListEntry> {new TrackListEntry {track, tracklist, dateTime}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Track;
|
|||||||
class TrackArtistLink;
|
class TrackArtistLink;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class Artist : public Object<Artist, ArtistId>
|
class Artist final : public Object<Artist, ArtistId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
class User;
|
class User;
|
||||||
class AuthToken : public Object<AuthToken, AuthTokenId>
|
class AuthToken final : public Object<AuthToken, AuthTokenId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AuthToken() = default;
|
AuthToken() = default;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ClusterType;
|
|||||||
class ScanSettings;
|
class ScanSettings;
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
class Cluster : public Object<Cluster, ClusterId>
|
class Cluster final : public Object<Cluster, ClusterId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cluster() = default;
|
Cluster() = default;
|
||||||
@@ -81,7 +81,7 @@ class Cluster : public Object<Cluster, ClusterId>
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ClusterType : public Object<ClusterType, ClusterTypeId>
|
class ClusterType final : public Object<ClusterType, ClusterTypeId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClusterType() = default;
|
ClusterType() = default;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class Session;
|
|||||||
class Track;
|
class Track;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class Listen : public Object<Listen, ListenId>
|
class Listen final : public Object<Listen, ListenId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Listen() = default;
|
Listen() = default;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Session;
|
|||||||
class Track;
|
class Track;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class Release : public Object<Release, ReleaseId>
|
class Release final : public Object<Release, ReleaseId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Database {
|
|||||||
class ClusterType;
|
class ClusterType;
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
class ScanSettings : public Object<ScanSettings, ScanSettingsId>
|
class ScanSettings final : public Object<ScanSettings, ScanSettingsId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Do not modify values (just add)
|
// Do not modify values (just add)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class StarredArtist : public Object<StarredArtist, StarredArtistId>
|
class StarredArtist final : public Object<StarredArtist, StarredArtistId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredArtist() = default;
|
StarredArtist() = default;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class StarredRelease : public Object<StarredRelease, StarredReleaseId>
|
class StarredRelease final : public Object<StarredRelease, StarredReleaseId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredRelease() = default;
|
StarredRelease() = default;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class StarredTrack : public Object<StarredTrack, StarredTrackId>
|
class StarredTrack final : public Object<StarredTrack, StarredTrackId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredTrack() = default;
|
StarredTrack() = default;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class TrackArtistLink;
|
|||||||
class TrackStats;
|
class TrackStats;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class Track : public Object<Track, TrackId>
|
class Track final : public Object<Track, TrackId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
|
|||||||
@@ -35,12 +35,11 @@ LMS_DECLARE_IDTYPE(TrackArtistLinkId)
|
|||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
|
|
||||||
class Artist;
|
class Artist;
|
||||||
class Session;
|
class Session;
|
||||||
class Track;
|
class Track;
|
||||||
|
|
||||||
class TrackArtistLink : public Object<TrackArtistLink, TrackArtistLinkId>
|
class TrackArtistLink final : public Object<TrackArtistLink, TrackArtistLinkId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class Session;
|
|||||||
class Track;
|
class Track;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class TrackBookmark : public Object<TrackBookmark, TrackBookmarkId>
|
class TrackBookmark final : public Object<TrackBookmark, TrackBookmarkId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackBookmark () = default;
|
TrackBookmark () = default;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ using FeatureName = std::string;
|
|||||||
using FeatureValues = std::vector<double>;
|
using FeatureValues = std::vector<double>;
|
||||||
using FeatureValuesMap = std::unordered_map<FeatureName, FeatureValues>;
|
using FeatureValuesMap = std::unordered_map<FeatureName, FeatureValues>;
|
||||||
|
|
||||||
class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
|
class TrackFeatures final : public Object<TrackFeatures, TrackFeaturesId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackFeatures() = default;
|
TrackFeatures() = default;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Track;
|
|||||||
class TrackListEntry;
|
class TrackListEntry;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
class TrackList : public Object<TrackList, TrackListId>
|
class TrackList final : public Object<TrackList, TrackListId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackList() = default;
|
TrackList() = default;
|
||||||
@@ -90,7 +90,7 @@ class TrackList : public Object<TrackList, TrackListId>
|
|||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
std::size_t getCount() const;
|
std::size_t getCount() const;
|
||||||
ObjectPtr<TrackListEntry> getEntry(std::size_t pos) const;
|
ObjectPtr<TrackListEntry> getEntry(std::size_t pos) const;
|
||||||
std::vector<ObjectPtr<TrackListEntry>> getEntries(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
|
std::vector<ObjectPtr<TrackListEntry>> getEntries(std::optional<Range> range = {}) const;
|
||||||
ObjectPtr<TrackListEntry> getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTime& dateTime) const;
|
ObjectPtr<TrackListEntry> getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTime& dateTime) const;
|
||||||
|
|
||||||
std::vector<ObjectPtr<Artist>> getArtists(const std::vector<ClusterId>& clusters, std::optional<TrackArtistLinkType> linkType, ArtistSortMethod sortMethod, std::optional<Range> range, bool& moreResults) const;
|
std::vector<ObjectPtr<Artist>> getArtists(const std::vector<ClusterId>& clusters, std::optional<TrackArtistLinkType> linkType, ArtistSortMethod sortMethod, std::optional<Range> range, bool& moreResults) const;
|
||||||
@@ -144,7 +144,7 @@ class TrackList : public Object<TrackList, TrackListId>
|
|||||||
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _entries;
|
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TrackListEntry : public Object<TrackListEntry, TrackListEntryId>
|
class TrackListEntry final : public Object<TrackListEntry, TrackListEntryId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackListEntry() = default;
|
TrackListEntry() = default;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Database {
|
|||||||
class AuthToken;
|
class AuthToken;
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
class User : public Object<User, UserId>
|
class User final : public Object<User, UserId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct PasswordHash
|
struct PasswordHash
|
||||||
|
|||||||
@@ -479,3 +479,43 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
|
|||||||
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
|
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, SingleTrackList_getEntries)
|
||||||
|
{
|
||||||
|
ScopedUser user {session, "MyUser"};
|
||||||
|
ScopedTrackList trackList {session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet()};
|
||||||
|
ScopedTrack track1 {session, "MyTrack"};
|
||||||
|
ScopedTrack track2 {session, "MyTrack"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
session.create<TrackListEntry>(track1.get(), trackList.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
auto entries {trackList.get()->getEntries()};
|
||||||
|
ASSERT_EQ(entries.size(), 1);
|
||||||
|
EXPECT_EQ(entries.front()->getTrack()->getId(), track1.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
session.create<TrackListEntry>(track2.get(), trackList.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
auto entries {trackList.get()->getEntries()};
|
||||||
|
ASSERT_EQ(entries.size(), 2);
|
||||||
|
EXPECT_EQ(entries[0]->getTrack()->getId(), track1.getId());
|
||||||
|
EXPECT_EQ(entries[1]->getTrack()->getId(), track2.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
auto entries {trackList.get()->getEntries(Range {1, 1})};
|
||||||
|
ASSERT_EQ(entries.size(), 1);
|
||||||
|
EXPECT_EQ(entries[0]->getTrack()->getId(), track2.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
@@ -35,6 +35,8 @@ namespace Recommendation
|
|||||||
class IPlaylistGeneratorService
|
class IPlaylistGeneratorService
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
virtual ~IPlaylistGeneratorService() = default;
|
||||||
|
|
||||||
// extend an existing playlist with similar tracks (but use playlist contraints)
|
// extend an existing playlist with similar tracks (but use playlist contraints)
|
||||||
virtual TrackContainer extendPlaylist(Database::TrackListId tracklistId, std::size_t maxCount) const = 0;
|
virtual TrackContainer extendPlaylist(Database::TrackListId tracklistId, std::size_t maxCount) const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ R"(function(current) {
|
|||||||
|
|
||||||
_mediaPlayer->playbackEnded.connect([this]
|
_mediaPlayer->playbackEnded.connect([this]
|
||||||
{
|
{
|
||||||
_playQueue->playNext();
|
_playQueue->onPlaybackEnded();
|
||||||
});
|
});
|
||||||
|
|
||||||
_playQueue->trackSelected.connect([this] (Database::TrackId trackId, bool play, float replayGain)
|
_playQueue->trackSelected.connect([this] (Database::TrackId trackId, bool play, float replayGain)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace UserInterface
|
|||||||
|
|
||||||
doJavaScript(oss.str());
|
doJavaScript(oss.str());
|
||||||
|
|
||||||
addWidget(move(modalWidget));
|
addWidget(std::move(modalWidget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+58
-16
@@ -158,7 +158,7 @@ PlayQueue::PlayQueue()
|
|||||||
for (const Database::TrackListEntry::pointer& entry : entries)
|
for (const Database::TrackListEntry::pointer& entry : entries)
|
||||||
LmsApp->getDbSession().create<Database::TrackListEntry>(entry->getTrack(), queue);
|
LmsApp->getDbSession().create<Database::TrackListEntry>(entry->getTrack(), queue);
|
||||||
}
|
}
|
||||||
_entriesContainer->clear();
|
_entriesContainer->reset();
|
||||||
addSome();
|
addSome();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -233,7 +233,6 @@ PlayQueue::PlayQueue()
|
|||||||
});
|
});
|
||||||
|
|
||||||
updateInfo();
|
updateInfo();
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -269,7 +268,8 @@ PlayQueue::clearTracks()
|
|||||||
getQueue().modify()->clear();
|
getQueue().modify()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
_entriesContainer->clear();
|
_entriesContainer->reset();
|
||||||
|
_trackPos.reset();
|
||||||
updateInfo();
|
updateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +278,7 @@ PlayQueue::stop()
|
|||||||
{
|
{
|
||||||
updateCurrentTrack(false);
|
updateCurrentTrack(false);
|
||||||
_trackPos.reset();
|
_trackPos.reset();
|
||||||
|
_isTrackSelected = false;
|
||||||
trackUnselected.emit();
|
trackUnselected.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +319,7 @@ PlayQueue::loadTrack(std::size_t pos, bool play)
|
|||||||
|
|
||||||
enqueueRadioTracksIfNeeded();
|
enqueueRadioTracksIfNeeded();
|
||||||
updateCurrentTrack(true);
|
updateCurrentTrack(true);
|
||||||
|
_isTrackSelected = true;
|
||||||
trackSelected.emit(trackId, play, replayGain ? *replayGain : 0);
|
trackSelected.emit(trackId, play, replayGain ? *replayGain : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,6 +347,12 @@ PlayQueue::playNext()
|
|||||||
loadTrack(*_trackPos + 1, true);
|
loadTrack(*_trackPos + 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PlayQueue::onPlaybackEnded()
|
||||||
|
{
|
||||||
|
playNext();
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
PlayQueue::getCount()
|
PlayQueue::getCount()
|
||||||
{
|
{
|
||||||
@@ -401,11 +409,9 @@ PlayQueue::updateCurrentTrack(bool selected)
|
|||||||
entry->toggleStyleClass("Lms-entry-playing", selected);
|
entry->toggleStyleClass("Lms-entry-playing", selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
void
|
||||||
PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
||||||
{
|
{
|
||||||
std::size_t nbTracksQueued {};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
||||||
|
|
||||||
@@ -415,22 +421,51 @@ PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
|||||||
std::size_t nbTracksToEnqueue {queueSize + trackIds.size() > getCapacity() ? getCapacity() - queueSize : trackIds.size()};
|
std::size_t nbTracksToEnqueue {queueSize + trackIds.size() > getCapacity() ? getCapacity() - queueSize : trackIds.size()};
|
||||||
for (const Database::TrackId trackId : trackIds)
|
for (const Database::TrackId trackId : trackIds)
|
||||||
{
|
{
|
||||||
|
if (nbTracksToEnqueue == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)};
|
Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)};
|
||||||
if (!track)
|
if (!track)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (nbTracksQueued == nbTracksToEnqueue)
|
|
||||||
break;
|
|
||||||
|
|
||||||
LmsApp->getDbSession().create<Database::TrackListEntry>(track, queue);
|
LmsApp->getDbSession().create<Database::TrackListEntry>(track, queue);
|
||||||
nbTracksQueued++;
|
nbTracksToEnqueue--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInfo();
|
updateInfo();
|
||||||
addSome();
|
addSome();
|
||||||
|
_entriesContainer->setHasMore();
|
||||||
|
}
|
||||||
|
|
||||||
return nbTracksQueued;
|
std::vector<Database::TrackId>
|
||||||
|
PlayQueue::getAndClearNextTracks()
|
||||||
|
{
|
||||||
|
std::vector<Database::TrackId> tracks;
|
||||||
|
|
||||||
|
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
||||||
|
|
||||||
|
Database::TrackList::pointer queue {getQueue()};
|
||||||
|
std::vector<Database::TrackListEntry::pointer> entries {queue->getEntries(Database::Range {_trackPos ? *_trackPos + 1 : 0, getCapacity()})};
|
||||||
|
tracks.reserve(entries.size());
|
||||||
|
for (Database::TrackListEntry::pointer entry : entries)
|
||||||
|
{
|
||||||
|
tracks.push_back(entry->getTrack()->getId());
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_trackPos)
|
||||||
|
{
|
||||||
|
// entries may have been cleared
|
||||||
|
if (*_trackPos + 1 < _entriesContainer->getCount())
|
||||||
|
_entriesContainer->remove(*_trackPos + 1, _entriesContainer->getCount() - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entriesContainer->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -439,6 +474,14 @@ PlayQueue::play(const std::vector<Database::TrackId>& trackIds)
|
|||||||
playAtIndex(trackIds, 0);
|
playAtIndex(trackIds, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PlayQueue::playNext(const std::vector<Database::TrackId>& trackIds)
|
||||||
|
{
|
||||||
|
std::vector<Database::TrackId> nextTracks {getAndClearNextTracks()};
|
||||||
|
nextTracks.insert(std::cbegin(nextTracks), std::cbegin(trackIds), std::cend(trackIds));
|
||||||
|
playOrAddLast(nextTracks);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PlayQueue::playShuffled(const std::vector<Database::TrackId>& trackIds)
|
PlayQueue::playShuffled(const std::vector<Database::TrackId>& trackIds)
|
||||||
{
|
{
|
||||||
@@ -453,7 +496,7 @@ void
|
|||||||
PlayQueue::playOrAddLast(const std::vector<Database::TrackId>& trackIds)
|
PlayQueue::playOrAddLast(const std::vector<Database::TrackId>& trackIds)
|
||||||
{
|
{
|
||||||
enqueueTracks(trackIds);
|
enqueueTracks(trackIds);
|
||||||
if (!_trackPos)
|
if (!_isTrackSelected)
|
||||||
loadTrack(0, true);
|
loadTrack(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,11 +514,9 @@ PlayQueue::addSome()
|
|||||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
const Database::TrackList::pointer queue {getQueue()};
|
const Database::TrackList::pointer queue {getQueue()};
|
||||||
const auto tracklistEntries {queue->getEntries(_entriesContainer->getCount(), _batchSize)};
|
const auto tracklistEntries {queue->getEntries(Database::Range {_entriesContainer->getCount(), _batchSize})};
|
||||||
for (const Database::TrackListEntry::pointer& tracklistEntry : tracklistEntries)
|
for (const Database::TrackListEntry::pointer& tracklistEntry : tracklistEntries)
|
||||||
addEntry(tracklistEntry);
|
addEntry(tracklistEntry);
|
||||||
|
|
||||||
_entriesContainer->setHasMore(_entriesContainer->getCount() < queue->getCount());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -491,7 +532,6 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry)
|
|||||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||||
|
|
||||||
const auto artists {track->getArtistIds({Database::TrackArtistLinkType::Artist})};
|
const auto artists {track->getArtistIds({Database::TrackArtistLinkType::Artist})};
|
||||||
LMS_LOG(UI, DEBUG) << "Found " << artists.size() << " artists!";
|
|
||||||
if (!artists.empty())
|
if (!artists.empty())
|
||||||
{
|
{
|
||||||
entry->setCondition("if-has-artists", true);
|
entry->setCondition("if-has-artists", true);
|
||||||
@@ -544,6 +584,8 @@ PlayQueue::addEntry(const Database::TrackListEntry::pointer& tracklistEntry)
|
|||||||
const std::optional<std::size_t> pos {_entriesContainer->getIndexOf(*entry)};
|
const std::optional<std::size_t> pos {_entriesContainer->getIndexOf(*entry)};
|
||||||
if (pos && *_trackPos >= *pos)
|
if (pos && *_trackPos >= *pos)
|
||||||
(*_trackPos)--;
|
(*_trackPos)--;
|
||||||
|
else if (*_trackPos >= _entriesContainer->getCount())
|
||||||
|
_trackPos.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
_entriesContainer->remove(*entry);
|
_entriesContainer->remove(*entry);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class PlayQueue : public Template
|
|||||||
PlayQueue();
|
PlayQueue();
|
||||||
|
|
||||||
void play(const std::vector<Database::TrackId>& trackIds);
|
void play(const std::vector<Database::TrackId>& trackIds);
|
||||||
|
void playNext(const std::vector<Database::TrackId>& trackIds);
|
||||||
void playShuffled(const std::vector<Database::TrackId>& trackIds);
|
void playShuffled(const std::vector<Database::TrackId>& trackIds);
|
||||||
void playOrAddLast(const std::vector<Database::TrackId>& trackIds); // play if queue empty, otherwise just add last
|
void playOrAddLast(const std::vector<Database::TrackId>& trackIds); // play if queue empty, otherwise just add last
|
||||||
void playAtIndex(const std::vector<Database::TrackId>& trackIds, std::size_t index);
|
void playAtIndex(const std::vector<Database::TrackId>& trackIds, std::size_t index);
|
||||||
@@ -69,6 +70,8 @@ class PlayQueue : public Template
|
|||||||
// Signal emitted when track count changed
|
// Signal emitted when track count changed
|
||||||
Wt::Signal<std::size_t> trackCountChanged;
|
Wt::Signal<std::size_t> trackCountChanged;
|
||||||
|
|
||||||
|
void onPlaybackEnded();
|
||||||
|
|
||||||
std::size_t getCapacity() const { return _capacity; }
|
std::size_t getCapacity() const { return _capacity; }
|
||||||
std::size_t getCount();
|
std::size_t getCount();
|
||||||
|
|
||||||
@@ -80,7 +83,8 @@ class PlayQueue : public Template
|
|||||||
bool isFull() const;
|
bool isFull() const;
|
||||||
|
|
||||||
void clearTracks();
|
void clearTracks();
|
||||||
std::size_t enqueueTracks(const std::vector<Database::TrackId>& trackIds);
|
void enqueueTracks(const std::vector<Database::TrackId>& trackIds);
|
||||||
|
std::vector<Database::TrackId> getAndClearNextTracks();
|
||||||
void addSome();
|
void addSome();
|
||||||
void addEntry(const Database::ObjectPtr<Database::TrackListEntry>& entry);
|
void addEntry(const Database::ObjectPtr<Database::TrackListEntry>& entry);
|
||||||
void enqueueRadioTracksIfNeeded();
|
void enqueueRadioTracksIfNeeded();
|
||||||
@@ -110,6 +114,7 @@ class PlayQueue : public Template
|
|||||||
Wt::WCheckBox* _repeatBtn {};
|
Wt::WCheckBox* _repeatBtn {};
|
||||||
Wt::WCheckBox* _radioBtn {};
|
Wt::WCheckBox* _radioBtn {};
|
||||||
std::optional<std::size_t> _trackPos; // current track position, if set
|
std::optional<std::size_t> _trackPos; // current track position, if set
|
||||||
|
bool _isTrackSelected {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ namespace UserInterface {
|
|||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
|
|
||||||
class DatabaseSettingsModel : public Wt::WFormModel
|
class DatabaseSettingsModel : public Wt::WFormModel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -69,7 +68,7 @@ class DatabaseSettingsModel : public Wt::WFormModel
|
|||||||
|
|
||||||
auto dirValidator {createDirectoryValidator()};
|
auto dirValidator {createDirectoryValidator()};
|
||||||
dirValidator->setMandatory(true);
|
dirValidator->setMandatory(true);
|
||||||
setValidator(MediaDirectoryField, move(dirValidator));
|
setValidator(MediaDirectoryField, std::move(dirValidator));
|
||||||
|
|
||||||
setValidator(UpdatePeriodField, createMandatoryValidator());
|
setValidator(UpdatePeriodField, createMandatoryValidator());
|
||||||
setValidator(UpdateStartTimeField, createMandatoryValidator());
|
setValidator(UpdateStartTimeField, createMandatoryValidator());
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "InfiniteScrollingContainer.hpp"
|
#include "InfiniteScrollingContainer.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include "LoadingIndicator.hpp"
|
#include "LoadingIndicator.hpp"
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
@@ -28,14 +29,20 @@ namespace UserInterface
|
|||||||
, _elements {bindNew<Wt::WContainerWidget>("elements")}
|
, _elements {bindNew<Wt::WContainerWidget>("elements")}
|
||||||
, _loadingIndicator {bindWidget<Wt::WTemplate>("loading-indicator", createLoadingIndicator())}
|
, _loadingIndicator {bindWidget<Wt::WTemplate>("loading-indicator", createLoadingIndicator())}
|
||||||
{
|
{
|
||||||
hideLoadingIndicator();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InfiniteScrollingContainer::clear()
|
InfiniteScrollingContainer::clear()
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InfiniteScrollingContainer::reset()
|
||||||
{
|
{
|
||||||
_elements->clear();
|
_elements->clear();
|
||||||
hideLoadingIndicator();
|
setHasMore(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -50,6 +57,12 @@ namespace UserInterface
|
|||||||
return _elements->addWidget(std::move(result));
|
return _elements->addWidget(std::move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InfiniteScrollingContainer::setHasMore()
|
||||||
|
{
|
||||||
|
setHasMore(true);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InfiniteScrollingContainer::setHasMore(bool hasMore)
|
InfiniteScrollingContainer::setHasMore(bool hasMore)
|
||||||
{
|
{
|
||||||
@@ -65,6 +78,22 @@ namespace UserInterface
|
|||||||
_elements->removeWidget(&widget);
|
_elements->removeWidget(&widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InfiniteScrollingContainer::remove(std::size_t first, std::size_t last)
|
||||||
|
{
|
||||||
|
assert(last >= first);
|
||||||
|
assert(last < static_cast<std::size_t>(_elements->count()));
|
||||||
|
|
||||||
|
// remove from end as API is quite uneffective (minimize moves)
|
||||||
|
std::size_t i {last};
|
||||||
|
while (Wt::WWidget* widget {_elements->widget(i)})
|
||||||
|
{
|
||||||
|
_elements->removeWidget(widget);
|
||||||
|
if (i-- == first)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Wt::WWidget*
|
Wt::WWidget*
|
||||||
InfiniteScrollingContainer::getWidget(std::size_t pos) const
|
InfiniteScrollingContainer::getWidget(std::size_t pos) const
|
||||||
{
|
{
|
||||||
@@ -87,7 +116,9 @@ namespace UserInterface
|
|||||||
if (!visible)
|
if (!visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const auto previousCount {_elements->count()};
|
||||||
onRequestElements.emit();
|
onRequestElements.emit();
|
||||||
|
setHasMore(previousCount != _elements->count());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -29,13 +30,15 @@
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class InfiniteScrollingContainer : public Wt::WTemplate
|
// Atomatically raises onRequestElements signal when the sentinel is displayed
|
||||||
|
// can add elements afterwards by calling setHasMoreElements()
|
||||||
|
class InfiniteScrollingContainer final : public Wt::WTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// "text" must contain loading-indicator and "elements"
|
// "text" must contain loading-indicator and "elements"
|
||||||
InfiniteScrollingContainer(const Wt::WString& text = Wt::WString::tr("Lms.infinite-scrolling-container.template"));
|
InfiniteScrollingContainer(const Wt::WString& text = Wt::WString::tr("Lms.infinite-scrolling-container.template"));
|
||||||
|
|
||||||
void clear();
|
void reset();
|
||||||
std::size_t getCount();
|
std::size_t getCount();
|
||||||
void add(std::unique_ptr<Wt::WWidget> result);
|
void add(std::unique_ptr<Wt::WWidget> result);
|
||||||
|
|
||||||
@@ -46,17 +49,19 @@ namespace UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
void remove(Wt::WWidget& widget);
|
void remove(Wt::WWidget& widget);
|
||||||
|
void remove(std::size_t first, std::size_t last);
|
||||||
|
|
||||||
Wt::WWidget* getWidget(std::size_t pos) const;
|
Wt::WWidget* getWidget(std::size_t pos) const;
|
||||||
std::optional<std::size_t> getIndexOf(Wt::WWidget& widget) const;
|
std::optional<std::size_t> getIndexOf(Wt::WWidget& widget) const;
|
||||||
|
void setHasMore(); // can be used to add elements afterwards
|
||||||
void setHasMore(bool hasMore);
|
|
||||||
|
|
||||||
Wt::Signal<> onRequestElements;
|
Wt::Signal<> onRequestElements;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void clear() override;
|
||||||
void displayLoadingIndicator();
|
void displayLoadingIndicator();
|
||||||
void hideLoadingIndicator();
|
void hideLoadingIndicator();
|
||||||
|
void setHasMore(bool hasMore); // can be used to add elements afterwards
|
||||||
|
|
||||||
Wt::WContainerWidget* _elements;
|
Wt::WContainerWidget* _elements;
|
||||||
Wt::WTemplate* _loadingIndicator;
|
Wt::WTemplate* _loadingIndicator;
|
||||||
|
|||||||
@@ -108,11 +108,16 @@ Artist::refreshView()
|
|||||||
if (!wApp->internalPathMatches("/artist/"))
|
if (!wApp->internalPathMatches("/artist/"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const auto artistId {extractArtistIdFromInternalPath()};
|
||||||
|
|
||||||
|
// consider everything is up to date is the same artist is being rendered
|
||||||
|
if (artistId && *artistId == _artistId)
|
||||||
|
return;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
_artistId = {};
|
_artistId = {};
|
||||||
_trackContainer = nullptr;
|
_trackContainer = nullptr;
|
||||||
|
|
||||||
const auto artistId {extractArtistIdFromInternalPath()};
|
|
||||||
if (!artistId)
|
if (!artistId)
|
||||||
throw ArtistNotFoundException {};
|
throw ArtistNotFoundException {};
|
||||||
|
|
||||||
@@ -166,28 +171,33 @@ Artist::refreshView()
|
|||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {_artistId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {_artistId});
|
||||||
});
|
});
|
||||||
|
bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, {_artistId});
|
||||||
|
});
|
||||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_artistId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_artistId});
|
||||||
});
|
});
|
||||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
->setLink(Wt::WLink {std::make_unique<DownloadArtistResource>(*artistId)});
|
->setLink(Wt::WLink {std::make_unique<DownloadArtistResource>(_artistId)});
|
||||||
|
|
||||||
{
|
{
|
||||||
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), *artistId); }};
|
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), _artistId); }};
|
||||||
|
|
||||||
Wt::WPushButton* starBtn {bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star"))};
|
Wt::WPushButton* starBtn {bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star"))};
|
||||||
starBtn->clicked().connect([=]
|
starBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
if (isStarred())
|
if (isStarred())
|
||||||
{
|
{
|
||||||
Service<Scrobbling::IScrobblingService>::get()->unstar(LmsApp->getUserId(), *artistId);
|
Service<Scrobbling::IScrobblingService>::get()->unstar(LmsApp->getUserId(), _artistId);
|
||||||
starBtn->setText(Wt::WString::tr("Lms.Explore.star"));
|
starBtn->setText(Wt::WString::tr("Lms.Explore.star"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Service<Scrobbling::IScrobblingService>::get()->star(LmsApp->getUserId(), *artistId);
|
Service<Scrobbling::IScrobblingService>::get()->star(LmsApp->getUserId(), _artistId);
|
||||||
starBtn->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
starBtn->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -232,7 +242,6 @@ Artist::refreshReleases()
|
|||||||
{
|
{
|
||||||
addSomeReleases(releases);
|
addSomeReleases(releases);
|
||||||
});
|
});
|
||||||
releases.container->setHasMore(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -272,11 +281,10 @@ Artist::refreshAppearsOnReleases()
|
|||||||
releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.Artist.appears-on"));
|
releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.Artist.appears-on"));
|
||||||
_appearsOnReleaseContainer.releases = releases.results;
|
_appearsOnReleaseContainer.releases = releases.results;
|
||||||
_appearsOnReleaseContainer.container = releaseContainer->bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
_appearsOnReleaseContainer.container = releaseContainer->bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
||||||
_appearsOnReleaseContainer.container->onRequestElements.connect(this, [this, &releases]
|
_appearsOnReleaseContainer.container->onRequestElements.connect(this, [this]
|
||||||
{
|
{
|
||||||
addSomeReleases(_appearsOnReleaseContainer);
|
addSomeReleases(_appearsOnReleaseContainer);
|
||||||
});
|
});
|
||||||
_appearsOnReleaseContainer.container->setHasMore(true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -340,11 +348,6 @@ Artist::addSomeReleases(ReleaseContainer& releaseContainer)
|
|||||||
const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseContainer.releases[i])};
|
const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseContainer.releases[i])};
|
||||||
releaseContainer.container->add(ReleaseListHelpers::createEntryForArtist(release, artist));
|
releaseContainer.container->add(ReleaseListHelpers::createEntryForArtist(release, artist));
|
||||||
}
|
}
|
||||||
releaseContainer.container->setHasMore(releaseContainer.container->getCount() < releaseContainer.releases.size());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
releaseContainer.container->setHasMore(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,15 +367,10 @@ Artist::addSomeNonReleaseTracks()
|
|||||||
params.setNonRelease(true);
|
params.setNonRelease(true);
|
||||||
|
|
||||||
const auto tracks {Track::find(LmsApp->getDbSession(), params)};
|
const auto tracks {Track::find(LmsApp->getDbSession(), params)};
|
||||||
bool moreResults {tracks.moreResults};
|
|
||||||
|
|
||||||
for (const TrackId trackId : tracks.results)
|
for (const TrackId trackId : tracks.results)
|
||||||
{
|
{
|
||||||
if (_trackContainer->getCount() == _tracksMaxCount)
|
if (_trackContainer->getCount() == _tracksMaxCount)
|
||||||
{
|
|
||||||
moreResults = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
||||||
_trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
_trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
||||||
@@ -380,8 +378,6 @@ Artist::addSomeNonReleaseTracks()
|
|||||||
areTracksAdded = true;
|
areTracksAdded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_trackContainer->setHasMore(moreResults);
|
|
||||||
|
|
||||||
return areTracksAdded;
|
return areTracksAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,9 +102,8 @@ Artists::Artists(Filters& filters)
|
|||||||
void
|
void
|
||||||
Artists::refreshView()
|
Artists::refreshView()
|
||||||
{
|
{
|
||||||
_container->clear();
|
_container->reset();
|
||||||
_artistCollector.reset();
|
_artistCollector.reset();
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -171,8 +170,6 @@ Artists::addSome()
|
|||||||
_container->add(ArtistListHelpers::createEntry(artist));
|
_container->add(ArtistListHelpers::createEntry(artist));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_container->setHasMore(artistIds.moreResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ namespace UserInterface
|
|||||||
case Command::Play:
|
case Command::Play:
|
||||||
_playQueue.play(trackIds);
|
_playQueue.play(trackIds);
|
||||||
break;
|
break;
|
||||||
|
case Command::PlayNext:
|
||||||
|
_playQueue.playNext(trackIds);
|
||||||
|
break;
|
||||||
case Command::PlayShuffled:
|
case Command::PlayShuffled:
|
||||||
_playQueue.playShuffled(trackIds);
|
_playQueue.playShuffled(trackIds);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ namespace UserInterface
|
|||||||
enum class Command
|
enum class Command
|
||||||
{
|
{
|
||||||
Play,
|
Play,
|
||||||
|
PlayNext,
|
||||||
PlayOrAddLast,
|
PlayOrAddLast,
|
||||||
PlayShuffled,
|
PlayShuffled,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -217,9 +217,15 @@ Release::refreshView()
|
|||||||
if (!wApp->internalPathMatches("/release/"))
|
if (!wApp->internalPathMatches("/release/"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
const auto releaseId {extractReleaseIdFromInternalPath()};
|
const auto releaseId {extractReleaseIdFromInternalPath()};
|
||||||
|
|
||||||
|
// consider everything is up to date is the same release is being rendered
|
||||||
|
if (releaseId && *releaseId == _releaseId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
_releaseId = {};
|
||||||
|
|
||||||
if (!releaseId)
|
if (!releaseId)
|
||||||
throw ReleaseNotFoundException {};
|
throw ReleaseNotFoundException {};
|
||||||
|
|
||||||
@@ -232,6 +238,7 @@ Release::refreshView()
|
|||||||
throw ReleaseNotFoundException {};
|
throw ReleaseNotFoundException {};
|
||||||
|
|
||||||
LmsApp->setTitle(release->getName());
|
LmsApp->setTitle(release->getName());
|
||||||
|
_releaseId = *releaseId;
|
||||||
|
|
||||||
refreshCopyright(release);
|
refreshCopyright(release);
|
||||||
refreshLinks(release);
|
refreshLinks(release);
|
||||||
@@ -281,44 +288,50 @@ Release::refreshView()
|
|||||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::Play, {*releaseId});
|
_playQueueController.processCommand(PlayQueueController::Command::Play, {_releaseId});
|
||||||
});
|
});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {*releaseId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {_releaseId});
|
||||||
|
});
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, {_releaseId});
|
||||||
});
|
});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {*releaseId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_releaseId});
|
||||||
});
|
});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
->setLink(Wt::WLink {std::make_unique<DownloadReleaseResource>(*releaseId)});
|
->setLink(Wt::WLink {std::make_unique<DownloadReleaseResource>(_releaseId)});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("release-info", Wt::WString::tr("Lms.Explore.release-info"))
|
bindNew<Wt::WPushButton>("release-info", Wt::WString::tr("Lms.Explore.release-info"))
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
showReleaseInfoModal(*releaseId);
|
showReleaseInfoModal(_releaseId);
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), *releaseId); }};
|
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), _releaseId); }};
|
||||||
|
|
||||||
Wt::WPushButton* starBtn {bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star"))};
|
Wt::WPushButton* starBtn {bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star"))};
|
||||||
starBtn->clicked().connect([=]
|
starBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
if (isStarred())
|
if (isStarred())
|
||||||
{
|
{
|
||||||
Service<Scrobbling::IScrobblingService>::get()->unstar(LmsApp->getUserId(), *releaseId);
|
Service<Scrobbling::IScrobblingService>::get()->unstar(LmsApp->getUserId(), _releaseId);
|
||||||
starBtn->setText(Wt::WString::tr("Lms.Explore.star"));
|
starBtn->setText(Wt::WString::tr("Lms.Explore.star"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Service<Scrobbling::IScrobblingService>::get()->star(LmsApp->getUserId(), *releaseId);
|
Service<Scrobbling::IScrobblingService>::get()->star(LmsApp->getUserId(), _releaseId);
|
||||||
starBtn->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
starBtn->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -367,7 +380,7 @@ Release::refreshView()
|
|||||||
};
|
};
|
||||||
|
|
||||||
Database::Track::FindParameters params;
|
Database::Track::FindParameters params;
|
||||||
params.setRelease(*releaseId);
|
params.setRelease(_releaseId);
|
||||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||||
params.setClusters(_filters.getClusterIds());
|
params.setClusters(_filters.getClusterIds());
|
||||||
|
|
||||||
@@ -416,6 +429,11 @@ Release::refreshView()
|
|||||||
{
|
{
|
||||||
_playQueueController.playTrackInRelease(trackId);
|
_playQueueController.playTrackInRelease(trackId);
|
||||||
});
|
});
|
||||||
|
entry->bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"))
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, {trackId});
|
||||||
|
});
|
||||||
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace UserInterface
|
|||||||
|
|
||||||
Filters& _filters;
|
Filters& _filters;
|
||||||
PlayQueueController& _playQueueController;
|
PlayQueueController& _playQueueController;
|
||||||
|
Database::ReleaseId _releaseId;
|
||||||
};
|
};
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ Releases::Releases(Filters& filters, PlayQueueController& playQueueController)
|
|||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllReleases());
|
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllReleases());
|
||||||
});
|
});
|
||||||
|
bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, getAllReleases());
|
||||||
|
});
|
||||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
@@ -102,9 +107,8 @@ Releases::Releases(Filters& filters, PlayQueueController& playQueueController)
|
|||||||
void
|
void
|
||||||
Releases::refreshView()
|
Releases::refreshView()
|
||||||
{
|
{
|
||||||
_container->clear();
|
_container->reset();
|
||||||
_releaseCollector.reset();
|
_releaseCollector.reset();
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -128,8 +132,6 @@ Releases::addSome()
|
|||||||
_container->add(ReleaseListHelpers::createEntry(release));
|
_container->add(ReleaseListHelpers::createEntry(release));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_container->setHasMore(releaseIds.moreResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ReleaseId>
|
std::vector<ReleaseId>
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ namespace UserInterface
|
|||||||
void
|
void
|
||||||
SearchView::refreshView()
|
SearchView::refreshView()
|
||||||
{
|
{
|
||||||
_artists->clear();
|
_artists->reset();
|
||||||
_releases->clear();
|
_releases->reset();
|
||||||
_tracks->clear();
|
_tracks->reset();
|
||||||
|
|
||||||
addSomeArtists();
|
addSomeArtists();
|
||||||
addSomeReleases();
|
addSomeReleases();
|
||||||
@@ -104,7 +104,6 @@ namespace UserInterface
|
|||||||
|
|
||||||
const Range range {_artists->getCount(), getBatchSize(Mode::Artist)};
|
const Range range {_artists->getCount(), getBatchSize(Mode::Artist)};
|
||||||
const RangeResults<ArtistId> artistIds {_artistCollector.get(range)};
|
const RangeResults<ArtistId> artistIds {_artistCollector.get(range)};
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
@@ -114,8 +113,6 @@ namespace UserInterface
|
|||||||
_artists->add(ArtistListHelpers::createEntry(artist));
|
_artists->add(ArtistListHelpers::createEntry(artist));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_artists->setHasMore(artistIds.moreResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -125,7 +122,6 @@ namespace UserInterface
|
|||||||
|
|
||||||
const Range range {_releases->getCount(), getBatchSize(Mode::Release)};
|
const Range range {_releases->getCount(), getBatchSize(Mode::Release)};
|
||||||
const RangeResults<ReleaseId> releaseIds {_releaseCollector.get(range)};
|
const RangeResults<ReleaseId> releaseIds {_releaseCollector.get(range)};
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
@@ -135,8 +131,6 @@ namespace UserInterface
|
|||||||
_releases->add(ReleaseListHelpers::createEntry(release));
|
_releases->add(ReleaseListHelpers::createEntry(release));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_releases->setHasMore(releaseIds.moreResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -156,8 +150,6 @@ namespace UserInterface
|
|||||||
_tracks->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
_tracks->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_tracks->setHasMore(trackIds.moreResults);
|
|
||||||
}
|
}
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ namespace UserInterface::TrackListHelpers
|
|||||||
{
|
{
|
||||||
playQueueController.processCommand(PlayQueueController::Command::Play, {trackId});
|
playQueueController.processCommand(PlayQueueController::Command::Play, {trackId});
|
||||||
});
|
});
|
||||||
|
entry->bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"))
|
||||||
|
->clicked().connect([=, &playQueueController]
|
||||||
|
{
|
||||||
|
playQueueController.processCommand(PlayQueueController::Command::PlayNext, {trackId});
|
||||||
|
});
|
||||||
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
->clicked().connect([=, &playQueueController]
|
->clicked().connect([=, &playQueueController]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -179,8 +179,6 @@ namespace UserInterface
|
|||||||
{
|
{
|
||||||
addSome();
|
addSome();
|
||||||
});
|
});
|
||||||
|
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -201,8 +199,6 @@ namespace UserInterface
|
|||||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
||||||
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
||||||
}
|
}
|
||||||
|
|
||||||
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
|
||||||
}
|
}
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ using namespace Database;
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
TrackLists::TrackLists(Filters& filters)
|
TrackLists::TrackLists(Filters& filters)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.TrackLists.template")}
|
: Template {Wt::WString::tr("Lms.Explore.TrackLists.template")}
|
||||||
, _filters {filters}
|
, _filters {filters}
|
||||||
@@ -92,9 +91,8 @@ namespace UserInterface
|
|||||||
void
|
void
|
||||||
TrackLists::refreshView()
|
TrackLists::refreshView()
|
||||||
{
|
{
|
||||||
_container->clear();
|
_container->reset();
|
||||||
_trackListWidgets.clear();
|
_trackListWidgets.clear();
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -126,8 +124,6 @@ namespace UserInterface
|
|||||||
if (const TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)})
|
if (const TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)})
|
||||||
addTracklist(trackList);
|
addTracklist(trackList);
|
||||||
}
|
}
|
||||||
|
|
||||||
_container->setHasMore(trackListIds.moreResults && _container->getCount() <= _maxCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ Tracks::Tracks(Filters& filters, PlayQueueController& playQueueController)
|
|||||||
{
|
{
|
||||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllTracks());
|
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllTracks());
|
||||||
});
|
});
|
||||||
|
bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, getAllTracks());
|
||||||
|
});
|
||||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
@@ -103,9 +108,8 @@ Tracks::Tracks(Filters& filters, PlayQueueController& playQueueController)
|
|||||||
void
|
void
|
||||||
Tracks::refreshView()
|
Tracks::refreshView()
|
||||||
{
|
{
|
||||||
_container->clear();
|
_container->reset();
|
||||||
_trackCollector.reset();
|
_trackCollector.reset();
|
||||||
addSome();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -127,8 +131,6 @@ Tracks::addSome()
|
|||||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
||||||
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
||||||
}
|
}
|
||||||
|
|
||||||
_container->setHasMore(trackIds.moreResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::TrackId>
|
std::vector<Database::TrackId>
|
||||||
|
|||||||
Reference in New Issue
Block a user