Added some logic in the playqueue (play next / play previous handling)

This commit is contained in:
emeric
2018-03-19 13:45:09 +01:00
parent 50036c8918
commit 5006a82ce6
8 changed files with 163 additions and 188 deletions
+1 -187
View File
@@ -1,191 +1,5 @@
.main-nav {
margin-bottom: 0px;
}
.playqueue {
background-color: #EEE;
border-radius: 10px;
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.4);
}
.playqueue-playing {
.Lms-playqueue-selected {
font-weight: bold;
}
.playqueue-track {
font-size: 120%;
margin-top: 12px;
line-height: normal;
}
.playqueue-artist {
line-height: normal;
font-style: italic;
}
.playqueue-cover {
width: 64px;
height: 64px;
}
.mediaplayer-track {
font-weight: bold;
font-size: 120%;
}
.mediaplayer-artist {
font-style: italic;
}
.mediaplayer {
background-color: #CCC;
border-radius: 10px;
display: flex;
justify-content: space-between;
align-items: center;
height: 72px;
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.4);
}
.mediaplayer-cover {
margin: 8px;
width: 64px;
min-width: 64px;
height: 64px;
min-height: 64px;
display: flex;
justify-content: space-between;
align-items: center;
}
.mediaplayer-cover img {
border-radius: 8px;
border-radius: 8px;
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.4);
}
.mediaplayer-btn {
margin: 8px;
}
.mediaplayer-btn i {
cursor: pointer;
text-shadow: 0px 2px 3px rgba(0, 0, 0, 0.4);
}
.mediaplayer-btn-active {
color: #428bca;
}
.mediaplayer-info-container {
width: 100%;
min-width: 100px;
margin: 8px;
}
.mediaplayer-track-info {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.mediaplayer-progress-container {
display: flex;
align-items: center;
}
.mediaplayer-badge {
min-width: initial;
}
.mediaplayer-current-duration {
margin-right: 8px;
}
.mediaplayer-total-duration {
margin-left: 8px;
}
.mediaplayer-seekbar {
}
.mediaplayer-volume-container {
width: 24px;
height: 64px;
margin: 8px;
}
.mediaplayer-volume {
height: 64px;
-webkit-appearance: slider-vertical;
writing-mode: bt-lr;
}
.mobile-btn {
margin: 8px;
}
.mobile-btn-active {
color: #428bca;
}
.mobile-btn i {
cursor: pointer;
text-shadow: 0px 2px 3px rgba(0, 0, 0, 0.4);
}
.mobile-search-title {
font-weight: bold;
height: 32px;
line-height: 32px;
background-color: grey;
color: white;
text-align: center;
}
.mobile-search-entry {
min-height: 64px;
border-bottom: 1px solid lightgray;
overflow: hidden;
text-overflow: ellipsis;
}
.mobile-search-entry:active {
background-color: lightgrey;
}
.mobile-search-more {
height: 64px;
border-bottom: 1px solid lightgray;
}
.mobile-search-more:active {
background-color: lightgrey;
}
.mobile-track {
font-weight: bold;
}
.mobile-artist {
font-style: italic;
}
.vertical-align {
display: flex;
align-items: center;
}
.mobile-audio-footer {
background-color: #f5f5f5;
height: 60px;
}
.mobile-audio-player-cover {
width: 48px;
height: 48px;
}
.release_res_shadow {
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.5)
}
+13
View File
@@ -109,6 +109,19 @@ Playlist::getEntries(int offset, int size, bool& moreResults) const
return res;
}
Wt::Dbo::ptr<PlaylistEntry>
Playlist::getEntry(std::size_t pos) const
{
Wt::Dbo::ptr<PlaylistEntry> res;
bool moreResults;
auto entries = getEntries(pos, 1, moreResults);
if (!entries.empty())
res = entries.front();
return res;
}
std::size_t
Playlist::getCount() const
{
+1
View File
@@ -54,6 +54,7 @@ class Playlist : public Wt::Dbo::Dbo<Playlist>
// Get tracks, ordered by position
std::size_t getCount() const;
Wt::Dbo::ptr<PlaylistEntry> getEntry(std::size_t pos) const;
std::vector<Wt::Dbo::ptr<PlaylistEntry>> getEntries(int offset, int size, bool& moreResults) const;
template<class Action>
+17
View File
@@ -340,6 +340,23 @@ LmsApplication::handleAuthEvent(void)
auto player = new MediaPlayer();
main->bindWidget("player", player);
// Events from MediaPlayer
player->playNext.connect(std::bind([=]
{
playqueue->playNext();
}));
player->playPrevious.connect(std::bind([=]
{
playqueue->playPrevious();
}));
player->playbackEnded.connect(std::bind([=]
{
playqueue->playNext();
}));
// Events from the PlayQueue
playqueue->playTrack.connect(player, &MediaPlayer::playTrack);
// Events from MediaScanner
if (CurrentUser()->isAdmin())
{
+15
View File
@@ -21,6 +21,8 @@
#include <Wt/WTemplate>
#include <Wt/WText>
#include "utils/Logger.hpp"
#include "MediaPlayer.hpp"
namespace UserInterface {
@@ -35,9 +37,17 @@ MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent)
auto prevBtn = new Wt::WText(Wt::WString::tr("btn-mediaplayer-prev"), Wt::XHTMLText);
player->bindWidget("previous", prevBtn);
prevBtn->clicked().connect(std::bind([=]
{
playPrevious.emit();
}));
auto nextBtn = new Wt::WText(Wt::WString::tr("btn-mediaplayer-next"), Wt::XHTMLText);
player->bindWidget("next", nextBtn);
nextBtn->clicked().connect(std::bind([=]
{
playNext.emit();
}));
auto shuffleBtn = new Wt::WText(Wt::WString::tr("btn-mediaplayer-shuffle"), Wt::XHTMLText);
player->bindWidget("shuffle", shuffleBtn);
@@ -53,7 +63,12 @@ MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent)
auto trackName = new Wt::WText("---");
player->bindWidget("track-name", trackName);
}
void
MediaPlayer::playTrack(Database::Track::id_type id)
{
LMS_LOG(UI, DEBUG) << "Playing track ID = " << id;
}
} // namespace UserInterface
+11
View File
@@ -19,14 +19,25 @@
#pragma once
#include <Wt/WSignal>
#include <Wt/WContainerWidget>
#include "database/Types.hpp"
namespace UserInterface {
class MediaPlayer : public Wt::WContainerWidget
{
public:
MediaPlayer(Wt::WContainerWidget* parent = 0);
void playTrack(Database::Track::id_type);
// Signals
Wt::Signal<void> playbackEnded;
Wt::Signal<void> playPrevious;
Wt::Signal<void> playNext;
};
} // namespace UserInterface
+88 -1
View File
@@ -75,6 +75,8 @@ PlayQueue::PlayQueue(Wt::WContainerWidget* parent)
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
if (!playlist)
playlist = Database::Playlist::create(DboSession(), currentPlayQueueName, false, CurrentUser());
_trackPos = CurrentUser()->getCurPlayingTrackPos();
}
_showMore->clicked().connect(std::bind([=]
@@ -86,6 +88,63 @@ PlayQueue::PlayQueue(Wt::WContainerWidget* parent)
addSome();
}
void
PlayQueue::stop()
{
updateCurrentTrack(false);
_trackPos.reset();
}
void
PlayQueue::play(std::size_t pos)
{
updateCurrentTrack(false);
Database::Track::id_type trackId;
{
Wt::Dbo::Transaction transaction(DboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
// If out of range, stop playing
if (pos >= playlist->getCount())
{
stop();
return;
}
_trackPos = pos;
trackId = playlist->getEntry(*_trackPos)->getTrack().id();
updateCurrentTrack(true);
}
playTrack.emit(trackId);
}
void
PlayQueue::playPrevious()
{
if (!_trackPos)
return;
if (*_trackPos == 0)
stop();
else
play(*_trackPos - 1);
}
void
PlayQueue::playNext()
{
if (!_trackPos)
{
play(0);
return;
}
play(*_trackPos + 1);
}
void
PlayQueue::updateInfo()
{
@@ -95,6 +154,22 @@ PlayQueue::updateInfo()
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(playlist->getCount()));
}
void
PlayQueue::updateCurrentTrack(bool selected)
{
if (!_trackPos || *_trackPos >= static_cast<std::size_t>(_entriesContainer->count()))
return;
auto track = _entriesContainer->widget(*_trackPos);
if (track)
{
if (selected)
track->addStyleClass("Lms-playqueue-selected");
else
track->removeStyleClass("Lms-playqueue-selected");
}
}
void
PlayQueue::addTracks(const std::vector<Database::Track::pointer>& tracks)
{
@@ -169,7 +244,12 @@ PlayQueue::addSome()
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
// TODO
playBtn->clicked().connect(std::bind([=]
{
auto pos = _entriesContainer->indexOf(entry);
if (pos >= 0)
play(pos);
}));
auto delBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.delete"), Wt::XHTMLText);
entry->bindWidget("del-btn", delBtn);
@@ -183,6 +263,13 @@ PlayQueue::addSome()
entryToRemove.remove();
}
if (_trackPos)
{
auto pos = _entriesContainer->indexOf(entry);
if (pos > 0 && *_trackPos >= static_cast<std::size_t>(pos))
(*_trackPos)--;
}
_entriesContainer->removeWidget(entry);
updateInfo();
+17
View File
@@ -20,9 +20,12 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WSignal>
#include <Wt/WTemplate>
#include <Wt/WText>
#include <boost/optional.hpp>
#include "database/Types.hpp"
namespace UserInterface {
@@ -35,13 +38,27 @@ class PlayQueue : public Wt::WContainerWidget
void addTracks(const std::vector<Database::Track::pointer>& tracks);
void playTracks(const std::vector<Database::Track::pointer>& tracks);
// play the next track in the queue
void playNext();
// play the previous track in the queue
void playPrevious();
// Signal emitted when a track is to be played
Wt::Signal<Database::Track::id_type> playTrack;
private:
void addSome();
void updateInfo();
void updateCurrentTrack(bool selected);
void play(std::size_t pos);
void stop();
Wt::WContainerWidget* _entriesContainer;
Wt::WTemplate* _showMore;
Wt::WText* _nbTracks;
boost::optional<std::size_t> _trackPos; // current track position, if set
};
} // namespace UserInterface