[UI] Updated audio player layout

This commit is contained in:
emeric
2016-01-11 20:24:41 +01:00
parent c24dd3c9d8
commit e2dee6d4b8
9 changed files with 232 additions and 564 deletions
-1
View File
@@ -26,7 +26,6 @@ lms_SOURCES = \
$(srcdir)/ui/auth/LmsAuth.cpp \
$(srcdir)/ui/audio/AudioPlayer.cpp \
$(srcdir)/ui/audio/desktop/DesktopAudio.cpp \
$(srcdir)/ui/audio/desktop/AudioMediaPlayer.cpp \
$(srcdir)/ui/audio/desktop/FilterChain.cpp \
$(srcdir)/ui/audio/desktop/KeywordSearchFilter.cpp \
$(srcdir)/ui/audio/desktop/PlayQueue.cpp \
+127 -77
View File
@@ -33,7 +33,7 @@
namespace UserInterface {
void
bool
AudioPlayer::loadTrack(Database::Track::id_type trackId)
{
Wt::Dbo::Transaction transaction(DboSession());
@@ -42,7 +42,7 @@ AudioPlayer::loadTrack(Database::Track::id_type trackId)
if (!track)
{
LMS_LOG(UI, INFO) << "No track found for id " << trackId;
return;
return false;
}
_trackName->setText(Wt::WString::fromUTF8(track->getName()));
@@ -57,7 +57,7 @@ AudioPlayer::loadTrack(Database::Track::id_type trackId)
if (!mediaFile.open() || !mediaFile.scan())
{
LMS_LOG(UI, ERROR) << "Cannot open file '" << track->getPath();
return;
return false;
}
@@ -79,120 +79,170 @@ AudioPlayer::loadTrack(Database::Track::id_type trackId)
//TODO, try to load everything in JS in order to prevent the WriteError bug?
_audio->pause();
_audio->clearSources();
//TODOencoding
//TODO, encoding
_audio->addSource(SessionTranscodeResource()->getUrl(trackId, Av::Encoding::MP3, 0, streams));
_audio->setPreloadMode(Wt::WAudio::PreloadAuto);
_audio->play();
return true;
}
AudioPlayer::AudioPlayer(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent)
: Wt::WTemplate(parent)
{
Wt::WTemplate *t = new Wt::WTemplate(this);
t->setTemplateText(Wt::WString::tr("wa-audio-player"));
setTemplateText(Wt::WString::tr("wa-audio-player"));
addStyleClass("mediaplayer");
_audio = new Wt::WAudio(this);
// TODO potential leak here
_audio = new Wt::WAudio();
_audio->setOptions(Wt::WAudio::Autoplay);
_audio->setPreloadMode(Wt::WAudio::PreloadAuto);
bindWidget("audio", _audio);
_audio->ended().connect(std::bind([=] ()
{
_playbackEnded.emit();
}));
_cover = new Wt::WImage();
t->bindWidget("cover", _cover);
bindWidget("cover", _cover);
_cover->setImageLink(SessionCoverResource()->getUnknownTrackUrl(64));
InputRange *seekbar = new InputRange();
t->bindWidget("seekbar", seekbar);
bindWidget("seekbar", seekbar);
_trackName = new Wt::WText();
t->bindWidget("track", _trackName);
bindWidget("track", _trackName);
_artistName = new Wt::WText();
t->bindWidget("artist", _artistName);
bindWidget("artist", _artistName);
_releaseName = new Wt::WText();
t->bindWidget("release", _releaseName);
bindWidget("release", _releaseName);
InputRange *volumeSlider = new InputRange();
t->bindWidget("volume", volumeSlider);
bindWidget("volume", volumeSlider);
Wt::WPushButton *playlistBtn = new Wt::WPushButton("<i class=\"fa fa-list fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("playlist", playlistBtn);
Wt::WText *playlistBtn = new Wt::WText("<i class=\"fa fa-list fa-2x\"></i>", Wt::XHTMLText);
playlistBtn->addStyleClass("mediaplayer-btn");
bindWidget("playlist", playlistBtn);
Wt::WPushButton *repeatBtn = new Wt::WPushButton("<i class=\"fa fa-repeat fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("repeat", repeatBtn);
Wt::WText *repeatBtn = new Wt::WText("<i class=\"fa fa-repeat fa-2x\"></i>", Wt::XHTMLText);
repeatBtn->addStyleClass("mediaplayer-btn");
bindWidget("repeat", repeatBtn);
repeatBtn->clicked().connect(std::bind([=] ()
{
if (repeatBtn->hasStyleClass("mediaplayer-btn-active"))
{
repeatBtn->removeStyleClass("mediaplayer-btn-active");
_loop.emit(false);
}
else
{
repeatBtn->addStyleClass("mediaplayer-btn-active");
_loop.emit(true);
}
}));
Wt::WPushButton *shuffleBtn = new Wt::WPushButton("<i class=\"fa fa-random fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("shuffle", shuffleBtn);
Wt::WText *shuffleBtn = new Wt::WText("<i class=\"fa fa-random fa-2x\"></i>", Wt::XHTMLText);
shuffleBtn->addStyleClass("mediaplayer-btn");
bindWidget("shuffle", shuffleBtn);
shuffleBtn->clicked().connect(std::bind([=] ()
{
if (shuffleBtn->hasStyleClass("mediaplayer-btn-active"))
{
shuffleBtn->removeStyleClass("mediaplayer-btn-active");
_shuffle.emit(false);
}
else
{
shuffleBtn->addStyleClass("mediaplayer-btn-active");
_shuffle.emit(true);
}
}));
Wt::WPushButton *prevBtn = new Wt::WPushButton("<i class=\"fa fa-step-backward fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("prev", prevBtn);
Wt::WText *prevBtn = new Wt::WText("<i class=\"fa fa-step-backward fa-2x\"></i>", Wt::XHTMLText);
prevBtn->addStyleClass("mediaplayer-btn");
bindWidget("prev", prevBtn);
prevBtn->clicked().connect(std::bind([=] ()
{
_playPrevious.emit();
}));
Wt::WPushButton *nextBtn = new Wt::WPushButton("<i class=\"fa fa-step-forward fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("next", nextBtn);
Wt::WText *nextBtn = new Wt::WText("<i class=\"fa fa-step-forward fa-2x\"></i>", Wt::XHTMLText);
nextBtn->addStyleClass("mediaplayer-btn");
bindWidget("next", nextBtn);
nextBtn->clicked().connect(std::bind([=] ()
{
_playNext.emit();
}));
Wt::WPushButton *playPauseBtn = new Wt::WPushButton("<i class=\"fa fa-play fa-lg\"></i>", Wt::XHTMLText);
t->bindWidget("play-pause", playPauseBtn);
Wt::WText *playPauseBtn = new Wt::WText("<i class=\"fa fa-play fa-3x\"></i>", Wt::XHTMLText);
playPauseBtn->addStyleClass("mediaplayer-btn");
bindWidget("play-pause", playPauseBtn);
Wt::WText *trackCurrentTime = new Wt::WText("00:00");
t->bindWidget("curtime", trackCurrentTime);
bindWidget("curtime", trackCurrentTime);
_trackDuration = new Wt::WText("00:00");
t->bindWidget("duration", _trackDuration);
bindWidget("duration", _trackDuration);
this->doJavaScript(
"\
document.lms = {};\
document.lms.audio = {};\
document.lms.audio.audio = " + _audio->jsRef() + ";\
document.lms.audio.seekbar = " + seekbar->jsRef() +";\
document.lms.audio.volumeSlider = " + volumeSlider->jsRef() + ";\
document.lms.audio.curTimeText = " + trackCurrentTime->jsRef() + ";\
document.lms.audio.playPause = " + playPauseBtn->jsRef() + ";\
\
document.lms.audio.offset = 0;\
document.lms.audio.curTime = 0;\
document.lms.audio.state = \"init\";\
document.lms.audio.volume = 1;\
\
document.lms.audio.seekbar.value = 0;\
document.lms.audio.seekbar.disabled = true;\
\
document.lms.audio.volumeSlider.min = 0;\
document.lms.audio.volumeSlider.max = 100;\
document.lms.audio.volumeSlider.value = 100;\
\
function updateUI() {\
document.lms.audio.curTimeText.innerHTML = document.lms.audio.curTime;\
document.lms.audio.seekbar.value = document.lms.audio.curTime;\
}\
"\
document.lms = {};\
document.lms.audio = {};\
document.lms.audio.audio = " + _audio->jsRef() + ";\
document.lms.audio.seekbar = " + seekbar->jsRef() +";\
document.lms.audio.volumeSlider = " + volumeSlider->jsRef() + ";\
document.lms.audio.curTimeText = " + trackCurrentTime->jsRef() + ";\
document.lms.audio.playPause = " + playPauseBtn->jsRef() + ";\
\
document.lms.audio.offset = 0;\
document.lms.audio.curTime = 0;\
document.lms.audio.state = \"init\";\
document.lms.audio.volume = 1;\
\
document.lms.audio.seekbar.value = 0;\
document.lms.audio.seekbar.disabled = true;\
\
document.lms.audio.volumeSlider.min = 0;\
document.lms.audio.volumeSlider.max = 100;\
document.lms.audio.volumeSlider.value = 100;\
\
function updateUI() {\
document.lms.audio.curTimeText.innerHTML = document.lms.audio.curTime;\
document.lms.audio.seekbar.value = document.lms.audio.curTime;\
}\
\
var mouseDown = 0;\
function seekMouseDown(e) {\
++mouseDown;\
}\
function seekMouseUp(e) {\
--mouseDown;\
}\
function seekMouseUp(e) {\
--mouseDown;\
}\
\
function seeking(e) {\
if (document.lms.audio.state == \"init\")\
return;\
\
document.lms.audio.curTimeText.innerHTML = document.lms.audio.seekbar.value;\
\
document.lms.audio.curTimeText.innerHTML = document.lms.audio.seekbar.value;\
}\
\
function seek(e) {\
if (document.lms.audio.state == \"init\")\
return;\
\
document.lms.audio.audio.pause(); \
document.lms.audio.offset = parseInt(document.lms.audio.seekbar.value);\
document.lms.audio.curTime = document.lms.audio.seekbar.value;\
var audioSource = document.lms.audio.audio.getElementsByTagName(\"source\")[0];\
var src = audioSource.src;\
src = src.slice(0, src.lastIndexOf(\"=\") + 1);\
audioSource.src = src + document.lms.audio.seekbar.value;\
document.lms.audio.audio.load(); \
document.lms.audio.audio.play(); \
document.lms.audio.curTimeText.innerHTML = ~~document.lms.audio.curTime + \" \";\
\
document.lms.audio.audio.pause(); \
document.lms.audio.offset = parseInt(document.lms.audio.seekbar.value);\
document.lms.audio.curTime = document.lms.audio.seekbar.value;\
var audioSource = document.lms.audio.audio.getElementsByTagName(\"source\")[0];\
var src = audioSource.src;\
src = src.slice(0, src.lastIndexOf(\"=\") + 1);\
audioSource.src = src + document.lms.audio.seekbar.value;\
document.lms.audio.audio.load(); \
document.lms.audio.audio.play(); \
document.lms.audio.curTimeText.innerHTML = ~~document.lms.audio.curTime + \" \";\
}\
\
function volumeChanged() {\
@@ -208,12 +258,12 @@ AudioPlayer::AudioPlayer(Wt::WContainerWidget *parent)
function playPause() {\
if (document.lms.audio.state == \"init\") \
return;\
\
if (document.lms.audio.audio.paused)\
document.lms.audio.audio.play();\
else\
document.lms.audio.audio.pause();\
\
\
if (document.lms.audio.audio.paused)\
document.lms.audio.audio.play();\
else\
document.lms.audio.audio.pause();\
\
}\
\
document.lms.audio.audio.addEventListener('timeupdate', updateCurTime); \
@@ -223,8 +273,8 @@ AudioPlayer::AudioPlayer(Wt::WContainerWidget *parent)
document.lms.audio.seekbar.addEventListener('mouseup', seekMouseUp);\
document.lms.audio.volumeSlider.addEventListener('input', volumeChanged);\
document.lms.audio.playPause.addEventListener('click', playPause);\
"
);
"
);
}
+18 -3
View File
@@ -19,23 +19,38 @@
#pragma once
#include <Wt/WTemplate>
#include <Wt/WSignal>
#include "common/InputRange.hpp"
#include "database/Types.hpp"
namespace UserInterface {
class AudioPlayer : public Wt::WContainerWidget
class AudioPlayer : public Wt::WTemplate
{
public:
AudioPlayer(Wt::WContainerWidget *parent = 0);
void loadTrack(Database::Track::id_type trackId);
bool loadTrack(Database::Track::id_type trackId);
// Slots
// Signals
Wt::Signal<void>& playbackEnded() {return _playbackEnded;}
Wt::Signal<void>& playNext() {return _playNext;}
Wt::Signal<void>& playPrevious() {return _playPrevious;}
Wt::Signal<bool>& shuffle() {return _shuffle;}
Wt::Signal<bool>& loop() {return _loop;}
private:
// Signals
Wt::Signal<void> _playbackEnded;
Wt::Signal<void> _playNext;
Wt::Signal<void> _playPrevious;
Wt::Signal<bool> _shuffle;
Wt::Signal<bool> _loop;
Wt::WAudio* _audio;
Wt::WText* _trackDuration;
Wt::WText* _trackName;
-289
View File
@@ -1,289 +0,0 @@
/*
* Copyright (C) 2013 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 <Wt/WMediaPlayer>
#include <Wt/WProgressBar>
#include <Wt/WCheckBox>
#include <Wt/WHBoxLayout>
#include <Wt/WVBoxLayout>
#include "logger/Logger.hpp"
#include "LmsApplication.hpp"
#include "AudioMediaPlayer.hpp"
namespace UserInterface {
namespace Desktop {
Wt::WMediaPlayer::Encoding
AudioMediaPlayer::getBestEncoding()
{
// MP3 seems to be better supported everywhere
return Wt::WMediaPlayer::MP3;
}
AudioMediaPlayer::AudioMediaPlayer(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent),
_mediaResource(nullptr)
{
this->setStyleClass("mediaplayer");
Wt::WVBoxLayout* mainLayout = new Wt::WVBoxLayout();
this->setLayout(mainLayout);
// Determine the encoding to be used
{
Wt::Dbo::Transaction transaction(DboSession());
switch (CurrentUser()->getAudioEncoding())
{
case Database::AudioEncoding::MP3: _encoding = Wt::WMediaPlayer::MP3; break;
case Database::AudioEncoding::WEBMA: _encoding = Wt::WMediaPlayer::WEBMA; break;
case Database::AudioEncoding::OGA: _encoding = Wt::WMediaPlayer::OGA; break;
case Database::AudioEncoding::AUTO:
default:
_encoding = getBestEncoding();
}
}
LMS_LOG(UI, INFO) << "Audio player using encoding " << _encoding;
// Current Media info
Wt::WHBoxLayout *currentMediaLayout = new Wt::WHBoxLayout();
mainLayout->addLayout(currentMediaLayout, 1);
currentMediaLayout->addWidget( _mediaCover = new Wt::WImage());
_mediaCover->setImageLink( LmsApplication::instance()->getCoverResource()->getUnknownTrackUrl(72));
_mediaCover->setStyleClass("mediaplayer-current-cover");
Wt::WVBoxLayout* mediaInfoLayout = new Wt::WVBoxLayout();
currentMediaLayout->addLayout(mediaInfoLayout, 1);
mediaInfoLayout->addWidget( _mediaTitle = new Wt::WText("---"));
mediaInfoLayout->addWidget( _mediaArtistRelease = new Wt::WText("---"));
_mediaTitle->setStyleClass("mediaplayer-current-track vertical-align");
_mediaArtistRelease->setStyleClass("mediaplayer-current-artist vertical-align");
// Time control
Wt::WHBoxLayout *sliderLayout = new Wt::WHBoxLayout();
mainLayout->addLayout(sliderLayout);
sliderLayout->addWidget(_curTime = new Wt::WText("00:00:00"));
sliderLayout->addWidget(_timeSlider = new Wt::WSlider( ), 1);
sliderLayout->addWidget(_duration = new Wt::WText("00:00:00"));
_timeSlider->setHeight(26); // Default is too big (50)
_curTime->setStyleClass("vertical-align");
_duration->setStyleClass("vertical-align");
// Controls
Wt::WHBoxLayout *controlsLayout = new Wt::WHBoxLayout();
mainLayout->addLayout(controlsLayout);
Wt::WPushButton *prevBtn = new Wt::WPushButton("<<");
controlsLayout->addWidget(prevBtn);
prevBtn->setStyleClass("mediaplayer-btn-controls");
Wt::WContainerWidget *btnContainer = new Wt::WContainerWidget();;
_playBtn = new Wt::WPushButton("Play");
btnContainer->addWidget(_playBtn);
_playBtn->setWidth(70);
_playBtn->setStyleClass("mediaplayer-btn-controls");
_pauseBtn = new Wt::WPushButton("Pause");
btnContainer->addWidget(_pauseBtn);
_pauseBtn->setWidth(70);
_pauseBtn->setStyleClass("mediaplayer-btn-controls");
controlsLayout->addWidget(btnContainer);
Wt::WPushButton *nextBtn = new Wt::WPushButton(">>");
controlsLayout->addWidget(nextBtn);
nextBtn->setStyleClass("mediaplayer-btn-controls");
_volumeSlider = new Wt::WSlider();
_volumeSlider->setRange(0,100);
_volumeSlider->setWidth(60); // Default is too big (150)
_volumeSlider->setHeight(26); // Default is too big (50)
_volumeSlider->setMinimumSize(50, Wt::WLength::Auto);
controlsLayout->addWidget(_volumeSlider, 1);
Wt::WPushButton *loop = new Wt::WPushButton("Loop");
loop->setCheckable(true);
loop->setStyleClass("btn-xs");
loop->checked().connect(std::bind([=] () { _loop.emit( true ); }));
loop->unChecked().connect(std::bind([=] () { _loop.emit( false ); }));
controlsLayout->addWidget(loop);
Wt::WPushButton *shuffle = new Wt::WPushButton("Shuffle");
shuffle->setCheckable(true);
shuffle->setStyleClass("btn-xs");
shuffle->checked().connect(std::bind([=] () { _shuffle.emit( true ); }));
shuffle->unChecked().connect(std::bind([=] () { _shuffle.emit( false );}));
controlsLayout->addWidget(shuffle);
_mediaPlayer = new Wt::WMediaPlayer( Wt::WMediaPlayer::Audio, btnContainer );
_mediaPlayer->addSource( _encoding, "" );
_mediaPlayer->ended().connect(this, &AudioMediaPlayer::handleTrackEnded);
_mediaPlayer->setControlsWidget( 0 );
_mediaPlayer->setButton(Wt::WMediaPlayer::Play, _playBtn);
_mediaPlayer->setButton(Wt::WMediaPlayer::Pause, _pauseBtn);
_mediaPlayer->timeUpdated().connect(this, &AudioMediaPlayer::handleTimeUpdated);
_volumeSlider->setValue(_mediaPlayer->volume() * 100);
nextBtn->clicked().connect(std::bind([=] ()
{
_mediaPlayer->stop();
_playNext.emit();
}));
prevBtn->clicked().connect(std::bind([=] ()
{
_mediaPlayer->stop();
_playPrevious.emit();
}));
_timeSlider->valueChanged().connect(this, &AudioMediaPlayer::handlePlayOffset);
_timeSlider->setDisabled(true);
_volumeSlider->sliderMoved().connect(this, &AudioMediaPlayer::handleVolumeSliderMoved);
}
void
AudioMediaPlayer::loadPlayer(boost::filesystem::path filePath, Av::TranscodeParameters& parameters)
{
_currentFile = filePath;
_currentParameters = parameters;
_mediaPlayer->clearSources();
if (_mediaResource)
delete _mediaResource;
_mediaResource = new AvConvTranscodeStreamResource( filePath, parameters, this );
_mediaPlayer->addSource( getEncoding(), Wt::WLink(_mediaResource));
// Auto play
_mediaPlayer->play();
}
bool
AudioMediaPlayer::load(Database::Track::id_type trackId)
{
Av::TranscodeParameters parameters;
boost::filesystem::path path;
boost::posix_time::time_duration duration;
{
Wt::Dbo::Transaction transaction(DboSession());
Database::Track::pointer track = Database::Track::getById(DboSession(), trackId);
if (!track)
{
LMS_LOG(UI, INFO) << "Cannot find track id " << trackId;
return false;
}
path = track->getPath();
parameters.setBitrate(Av::Stream::Type::Audio, CurrentUser()->getAudioBitrate() );
duration = track->getDuration();
_mediaTitle->setText ( Wt::WString::fromUTF8(track->getName()) );
_mediaArtistRelease->setText ( Wt::WString::fromUTF8(track->getArtist()->getName()) + " - " + Wt::WString::fromUTF8(track->getRelease()->getName()) );
_mediaCover->setImageLink( Wt::WLink (LmsApplication::instance()->getCoverResource()->getTrackUrl(trackId, 72)));
}
Av::MediaFile mediaFile(path);
if (!mediaFile.open() || !mediaFile.scan())
{
LMS_LOG(UI, INFO) << "Cannot open file '" << path << "'";
return false;
}
// It seems to be far better to manually map the streams
// otherwise, some files may have to be fully transcoded to be played by browser...
int audioBestStreamId = mediaFile.getBestStreamId(Av::Stream::Type::Audio);
if (audioBestStreamId != -1)
parameters.addStream(audioBestStreamId);
Av::Encoding encoding;
switch (_encoding)
{
case Wt::WMediaPlayer::MP3: encoding = Av::Encoding::MP3; break;
case Wt::WMediaPlayer::OGA: encoding = Av::Encoding::OGA; break;
case Wt::WMediaPlayer::WEBMA: encoding = Av::Encoding::WEBMA; break;
default:
encoding = Av::Encoding::MP3;
}
parameters.setEncoding(encoding);
_timeSlider->setDisabled(false);
_timeSlider->setRange(0, duration.total_seconds() );
_timeSlider->setValue(0);
_duration->setText( boost::posix_time::to_simple_string( duration ));
loadPlayer(path, parameters);
return true;
}
void
AudioMediaPlayer::handlePlayOffset(int offsetSecs)
{
Av::TranscodeParameters parameters = _currentParameters;
parameters.setOffset( boost::posix_time::seconds(offsetSecs) );
loadPlayer(_currentFile, parameters);
}
void
AudioMediaPlayer::handleTrackEnded(void)
{
LMS_LOG(UI, INFO) << "Track ended!";
_playbackEnded.emit();
}
void
AudioMediaPlayer::handleTimeUpdated(void)
{
boost::posix_time::time_duration currentTime ( boost::posix_time::seconds( _mediaPlayer->currentTime() + _currentParameters.getOffset().total_seconds()));
_timeSlider->setValue( currentTime.total_seconds() );
_curTime->setText( boost::posix_time::to_simple_string( currentTime) );
}
void
AudioMediaPlayer::handleVolumeSliderMoved(int value)
{
_mediaPlayer->setVolume( value / 100. );
}
} // namespace Desktop
} // namespace UserInterface
-110
View File
@@ -1,110 +0,0 @@
/*
* Copyright (C) 2013 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/>.
*/
#ifndef __AUDIO_MEDIA_PLAYER_HPP
#define __AUDIO_MEDIA_PLAYER_HPP
#include <memory>
#include <Wt/WSlider>
#include <Wt/WPushButton>
#include <Wt/WContainerWidget>
#include <Wt/WMediaPlayer>
#include <Wt/WText>
#include <Wt/WImage>
#include "database/Types.hpp"
#include "av/AvTranscoder.hpp"
#include "resource/AvConvTranscodeStreamResource.hpp"
#include "resource/CoverResource.hpp"
namespace UserInterface {
namespace Desktop {
class AudioMediaPlayer : public Wt::WContainerWidget
{
public:
static Wt::WMediaPlayer::Encoding getBestEncoding();
AudioMediaPlayer(Wt::WContainerWidget *parent = 0);
// Load Media to be played
bool load(Database::Track::id_type trackId);
// Accessors
Wt::WMediaPlayer::Encoding getEncoding() const { return _encoding; }
// Signal slots
Wt::Signal<void>& playbackEnded() {return _playbackEnded;}
Wt::Signal<void>& playNext() {return _playNext;}
Wt::Signal<void>& playPrevious() {return _playPrevious;}
Wt::Signal<bool>& shuffle() {return _shuffle;}
Wt::Signal<bool>& loop() {return _loop;}
private:
void handlePlayOffset(int offsetSecs);
void handleTrackEnded(void);
void handleValueChanged(double);
void handleTimeUpdated(void);
void handleSliderMoved(int value);
void handleVolumeSliderMoved(int value);
void loadPlayer(boost::filesystem::path filePath, Av::TranscodeParameters& parameters);
// Signals
Wt::Signal<void> _playbackEnded;
Wt::Signal<void> _playNext;
Wt::Signal<void> _playPrevious;
Wt::Signal<bool> _shuffle;
Wt::Signal<bool> _loop;
// Core
Wt::WMediaPlayer* _mediaPlayer;
AvConvTranscodeStreamResource* _mediaResource;
Wt::WMediaPlayer::Encoding _encoding;
// Media info
Wt::WImage* _mediaCover;
Wt::WText* _mediaTitle;
Wt::WText* _mediaArtistRelease;
// Controls
Wt::WPushButton* _playBtn;
Wt::WPushButton* _pauseBtn;
Wt::WSlider* _timeSlider;
Wt::WSlider* _volumeSlider;
Wt::WText* _curTime;
Wt::WText* _duration;
// Transcode
boost::filesystem::path _currentFile;
Av::TranscodeParameters _currentParameters;
};
} // namespace Desktop
} // namespace UserInterface
#endif
+60 -61
View File
@@ -120,73 +120,72 @@ _playQueue(nullptr)
_playQueue = new PlayQueue();
// Playlist/PlayQueue
Wt::WContainerWidget* playQueueContainer = new Wt::WContainerWidget();
playQueueContainer->setStyleClass("playqueue");
Wt::WVBoxLayout* playQueueLayout = new Wt::WVBoxLayout();
playQueueContainer->setLayout(playQueueLayout);
playQueueLayout->addWidget( _playQueue, 1);
Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout();
Wt::WPushButton *playlistBtn = new Wt::WPushButton("Playlist");
playlistBtn->setStyleClass("btn-sm btn-primary");
playlistControls->addWidget(playlistBtn);
// Playlist menu
{
Wt::WPopupMenu *popupMain = new Wt::WPopupMenu();
_popupMenuSave = new Wt::WPopupMenu();
popupMain->addMenu("Save", _popupMenuSave);
_popupMenuLoad = new Wt::WPopupMenu();
popupMain->addMenu("Load", _popupMenuLoad);
_popupMenuDelete = new Wt::WPopupMenu();
popupMain->addMenu("Delete", _popupMenuDelete);
playlistBtn->setMenu(popupMain);
}
Wt::WPushButton *upBtn = new Wt::WPushButton("UP");
upBtn->setStyleClass("btn-sm");
playlistControls->addWidget(upBtn);
Wt::WPushButton *downBtn = new Wt::WPushButton("DO");
downBtn->setStyleClass("btn-sm");
playlistControls->addWidget(downBtn);
Wt::WPushButton *delBtn = new Wt::WPushButton("DEL");
delBtn->setStyleClass("btn-sm btn-warning");
playlistControls->addWidget(delBtn);
Wt::WPushButton *clearBtn = new Wt::WPushButton("CLR");
clearBtn->setStyleClass("btn-sm btn-danger");
playlistControls->addWidget(clearBtn);
delBtn->clicked().connect(_playQueue, &PlayQueue::delSelected);
upBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedUp);
downBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedDown);
clearBtn->clicked().connect(_playQueue, &PlayQueue::delAll);
playQueueLayout->addLayout(playlistControls);
mainLayout->addWidget(playQueueContainer, 0, 0, 2, 1);
// Load the last known queue
playlistLoadToPlayqueue(CurrentQueuePlaylistName);
// Select the last known playing track
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::WContainerWidget* playQueueContainer = new Wt::WContainerWidget();
playQueueContainer->setStyleClass("playqueue");
Wt::WVBoxLayout* playQueueLayout = new Wt::WVBoxLayout();
playQueueContainer->setLayout(playQueueLayout);
_mediaPlayer = new AudioMediaPlayer();
playQueueLayout->addWidget(_mediaPlayer);
playQueueLayout->addWidget( _playQueue, 1);
Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout();
Wt::WPushButton *playlistBtn = new Wt::WPushButton("Playlist");
playlistBtn->setStyleClass("btn-sm btn-primary");
playlistControls->addWidget(playlistBtn);
// Playlist menu
{
Wt::WPopupMenu *popupMain = new Wt::WPopupMenu();
_popupMenuSave = new Wt::WPopupMenu();
popupMain->addMenu("Save", _popupMenuSave);
_popupMenuLoad = new Wt::WPopupMenu();
popupMain->addMenu("Load", _popupMenuLoad);
_popupMenuDelete = new Wt::WPopupMenu();
popupMain->addMenu("Delete", _popupMenuDelete);
playlistBtn->setMenu(popupMain);
}
Wt::WPushButton *upBtn = new Wt::WPushButton("UP");
upBtn->setStyleClass("btn-sm");
playlistControls->addWidget(upBtn);
Wt::WPushButton *downBtn = new Wt::WPushButton("DO");
downBtn->setStyleClass("btn-sm");
playlistControls->addWidget(downBtn);
Wt::WPushButton *delBtn = new Wt::WPushButton("DEL");
delBtn->setStyleClass("btn-sm btn-warning");
playlistControls->addWidget(delBtn);
Wt::WPushButton *clearBtn = new Wt::WPushButton("CLR");
clearBtn->setStyleClass("btn-sm btn-danger");
playlistControls->addWidget(clearBtn);
delBtn->clicked().connect(_playQueue, &PlayQueue::delSelected);
upBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedUp);
downBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedDown);
clearBtn->clicked().connect(_playQueue, &PlayQueue::delAll);
playQueueLayout->addLayout(playlistControls);
mainLayout->addWidget(playQueueContainer, 0, 0, 2, 1);
// Load the last known queue
playlistLoadToPlayqueue(CurrentQueuePlaylistName);
// Select the last known playing track
_playQueue->select(CurrentUser()->getCurPlayingTrackPos());
}
_mediaPlayer = new AudioPlayer();
mainLayout->addWidget(_mediaPlayer, 2, 0, 1, 4);
mainLayout->setRowStretch(1, 1);
mainLayout->setRowResizable(0, true, Wt::WLength(250, Wt::WLength::Pixel));
mainLayout->setColumnResizable(0, true);
mainLayout->setColumnResizable(0, true, Wt::WLength(400, Wt::WLength::Pixel));
// Double click on track
// Set the selected tracks to the play queue
@@ -510,7 +509,7 @@ Audio::playTrack(Track::id_type trackId, int pos)
CurrentUser().modify()->setCurPlayingTrackPos(pos);
}
if (!_mediaPlayer->load(trackId))
if (!_mediaPlayer->loadTrack(trackId))
_playQueue->playNext();
}
+3 -6
View File
@@ -17,14 +17,13 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UI_AUDIO_DESKTOP_HPP
#define UI_AUDIO_DESKTOP_HPP
#pragma once
#include <string>
#include <Wt/WPopupMenu>
#include "AudioMediaPlayer.hpp"
#include "audio/AudioPlayer.hpp"
#include "TrackView.hpp"
#include "PlayQueue.hpp"
@@ -66,7 +65,7 @@ class Audio : public UserInterface::Audio
void handlePlaylistSelected(Wt::WString name);
AudioMediaPlayer* _mediaPlayer;
AudioPlayer* _mediaPlayer;
TrackView* _trackView;
PlayQueue* _playQueue;
@@ -80,5 +79,3 @@ class Audio : public UserInterface::Audio
} // namespace Desktop
} // namespace UserInterface
#endif