From e0ae294abf44149c359fbcef2c0cecaf5fceac5b Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 30 May 2018 13:35:49 +0200 Subject: [PATCH] Adding basic radio mode --- approot/messages.xml | 3 ++- approot/playqueue.xml | 1 + src/database/Cluster.cpp | 23 +++++++++++++---- src/database/Cluster.hpp | 2 ++ src/database/Playlist.cpp | 30 +++++++++++++++++++++- src/database/Playlist.hpp | 11 +++++++-- src/ui/PlayQueueView.cpp | 52 +++++++++++++++++++++++++++++++++++++++ src/ui/PlayQueueView.hpp | 4 +++ 8 files changed, 117 insertions(+), 9 deletions(-) diff --git a/approot/messages.xml b/approot/messages.xml index f1e6febe..75297f4f 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -68,10 +68,11 @@ Various artists -Play Queue {1} tracks Added {1} tracks Playing {1} tracks +Play Queue +Radio mode Account diff --git a/approot/playqueue.xml b/approot/playqueue.xml index 81bb710c..4b26bd9d 100644 --- a/approot/playqueue.xml +++ b/approot/playqueue.xml @@ -11,6 +11,7 @@

${tr:Lms.PlayQueue.playqueue}

${nb-tracks} ${clear-btn} + ${radio-mode} ${entries} ${show-more} diff --git a/src/database/Cluster.cpp b/src/database/Cluster.cpp index 5468cd46..136c48e9 100644 --- a/src/database/Cluster.cpp +++ b/src/database/Cluster.cpp @@ -57,6 +57,24 @@ Cluster::getById(Wt::Dbo::Session& session, IdType id) return session.find().where("id = ?").bind(id); } +void +Cluster::addTrack(Wt::Dbo::ptr track) +{ + _tracks.insert(track); +} + +std::vector> +Cluster::getTracks(int offset, int limit) const +{ + Wt::Dbo::collection res = session()->query("select t from track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") + .where("c.id = ?").bind(self()->id()) + .offset(offset) + .limit(limit); + + return std::vector>(res.begin(), res.end()); +} + + ClusterType::ClusterType(std::string name) : _name(name) { @@ -117,11 +135,6 @@ ClusterType::getClusters() const return std::vector(res.begin(), res.end()); } -void -Cluster::addTrack(Wt::Dbo::ptr track) -{ - _tracks.insert(track); -} } // namespace Database diff --git a/src/database/Cluster.hpp b/src/database/Cluster.hpp index 65c1d68f..ba7965a3 100644 --- a/src/database/Cluster.hpp +++ b/src/database/Cluster.hpp @@ -53,6 +53,8 @@ class Cluster : public Wt::Dbo::Dbo const std::string& getName(void) const { return _name; } Wt::Dbo::ptr getType() const { return _clusterType; } const Wt::Dbo::collection>& getTracks() const { return _tracks; } + std::size_t getTrackCount() const { return _tracks.size(); } + std::vector> getTracks(int offset, int limit) const; void addTrack(Wt::Dbo::ptr track); diff --git a/src/database/Playlist.cpp b/src/database/Playlist.cpp index ca5cccd1..17d55eab 100644 --- a/src/database/Playlist.cpp +++ b/src/database/Playlist.cpp @@ -20,6 +20,7 @@ #include +#include "Cluster.hpp" #include "User.hpp" #include "Track.hpp" @@ -50,7 +51,7 @@ PlaylistEntry::PlaylistEntry() } PlaylistEntry::pointer -PlaylistEntry::getById(Wt::Dbo::Session& session, PlaylistEntry::id_type id) +PlaylistEntry::getById(Wt::Dbo::Session& session, IdType id) { return session.find().where("id = ?").bind(id); } @@ -133,4 +134,31 @@ Playlist::getCount() const return _entries.size(); } +std::vector> +Playlist::getClusters() const +{ + assert(session()); + assert(IdIsValid(self()->id())); + + Wt::Dbo::collection res = session()->query("SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN playlist_entry p_e ON p_e.track_id = t.id INNER JOIN playlist p ON p.id = p_e.playlist_id") + .where("p.id = ?").bind(self()->id()) + .groupBy("c.id") + .orderBy("COUNT(c.id) DESC"); + + return std::vector>(res.begin(), res.end()); +} + +bool +Playlist::hasTrack(IdType trackId) const +{ + assert(session()); + assert(IdIsValid(self()->id())); + + Wt::Dbo::collection res = session()->query("SELECT p_e from playlist_entry p_e INNER JOIN playlist p ON p_e.playlist_id = p.id") + .where("p_e.track_id = ?").bind(trackId); + + return res.size() > 0; +} + + } // namespace Database diff --git a/src/database/Playlist.hpp b/src/database/Playlist.hpp index b9840fc9..27135c9d 100644 --- a/src/database/Playlist.hpp +++ b/src/database/Playlist.hpp @@ -23,11 +23,14 @@ #include +#include "Types.hpp" + namespace Database { class PlaylistEntry; class User; class Track; +class Cluster; class Playlist : public Wt::Dbo::Dbo { @@ -56,6 +59,11 @@ class Playlist : public Wt::Dbo::Dbo Wt::Dbo::ptr getEntry(std::size_t pos) const; std::vector> getEntries(int offset, int size, bool& moreResults) const; + // Get clusters, order by occurence + std::vector> getClusters() const; + + bool hasTrack(IdType trackId) const; + template void persist(Action& a) { @@ -79,12 +87,11 @@ class PlaylistEntry public: using pointer = Wt::Dbo::ptr; - using id_type = Wt::Dbo::dbo_traits::IdType; PlaylistEntry(); PlaylistEntry(Wt::Dbo::ptr track, Wt::Dbo::ptr playlist); - static pointer getById(Wt::Dbo::Session& session, id_type id); + static pointer getById(Wt::Dbo::Session& session, IdType id); // Create utility static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr track, Wt::Dbo::ptr playlist); diff --git a/src/ui/PlayQueueView.cpp b/src/ui/PlayQueueView.cpp index ed27df15..824a2e9e 100644 --- a/src/ui/PlayQueueView.cpp +++ b/src/ui/PlayQueueView.cpp @@ -19,6 +19,8 @@ #include "PlayQueueView.hpp" +#include + #include #include #include @@ -46,6 +48,8 @@ PlayQueue::PlayQueue() _showMore->addFunction("tr", &Wt::WTemplate::Functions::tr); _showMore->setHidden(true); + _radioMode = bindNew("radio-mode", Wt::WString::tr("Lms.PlayQueue.radio-mode")); + _nbTracks = bindNew("nb-tracks"); { @@ -107,6 +111,10 @@ PlayQueue::play(std::size_t pos) return; } + // If last and radio mode, fill the next song + if (_radioMode->checkState() == Wt::CheckState::Checked && pos == playlist->getCount() - 1) + addRadioTrack(); + _trackPos = pos; trackId = playlist->getEntry(*_trackPos)->getTrack().id(); updateCurrentTrack(true); @@ -181,6 +189,12 @@ PlayQueue::enqueueTracks(const std::vector& tracks) addSome(); } +void +PlayQueue::enqueueTrack(Database::Track::pointer track) +{ + enqueueTracks(std::vector(1, track)); +} + void PlayQueue::addTracks(const std::vector& tracks) { @@ -274,5 +288,43 @@ PlayQueue::addSome() _showMore->setHidden(!moreResults); } +void +PlayQueue::addRadioTrack() +{ + auto now = std::chrono::system_clock::now(); + std::mt19937 randGenerator(std::chrono::duration_cast(now.time_since_epoch()).count()); + + LMS_LOG(UI, INFO) << "Radio mode: adding track"; + + auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser()); + + // Get all the tracks of the playlist, get the cluster that is mostly used + // and reuse it to get the next track + auto clusters = playlist->getClusters(); + if (clusters.empty()) + return; + + for (auto cluster : clusters) + { + LMS_LOG(UI, DEBUG) << "Processing cluster '" << cluster->getName() << "'"; + + auto nbTracks = cluster->getTrackCount(); + if (nbTracks == 0) + continue; + + std::uniform_int_distribution dist(0, nbTracks - 1); + + auto trackToAdd = cluster->getTracks(dist(randGenerator), 1).front(); + + if (!playlist->hasTrack(trackToAdd.id())) + { + enqueueTrack(trackToAdd); + return; + } + } + + LMS_LOG(UI, INFO) << "No more track to be added!"; +} + } // namespace UserInterface diff --git a/src/ui/PlayQueueView.hpp b/src/ui/PlayQueueView.hpp index b2652bb8..dfee6f6d 100644 --- a/src/ui/PlayQueueView.hpp +++ b/src/ui/PlayQueueView.hpp @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -52,13 +53,16 @@ class PlayQueue : public Wt::WTemplate private: void enqueueTracks(const std::vector& tracks); + void enqueueTrack(Database::Track::pointer track); void addSome(); + void addRadioTrack(); void updateInfo(); void updateCurrentTrack(bool selected); void play(std::size_t pos); void stop(); + Wt::WCheckBox* _radioMode; Wt::WContainerWidget* _entriesContainer; Wt::WTemplate* _showMore; Wt::WText* _nbTracks;