WIP. PlayQueue

This commit is contained in:
emeric
2014-11-04 20:37:19 +01:00
parent 67e9e79c0e
commit d0e472028d
9 changed files with 322 additions and 52 deletions
+6 -5
View File
@@ -12,14 +12,14 @@ lms_SOURCES = \
$(srcdir)/cover/CoverArt.cpp \
$(srcdir)/cover/CoverArtGrabber.cpp \
$(srcdir)/database/Artist.cpp \
$(srcdir)/database/Genre.cpp \
$(srcdir)/database/Release.cpp \
$(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Track.cpp \
$(srcdir)/database/DatabaseHandler.cpp \
$(srcdir)/database/Genre.cpp \
$(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Release.cpp \
$(srcdir)/database/SqlQuery.cpp \
$(srcdir)/database/Video.cpp \
$(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \
$(srcdir)/database/Video.cpp \
$(srcdir)/database-updater/DatabaseUpdater.cpp \
$(srcdir)/database-updater/Checksum.cpp \
$(srcdir)/logger/Logger.cpp \
@@ -46,6 +46,7 @@ lms_SOURCES = \
$(srcdir)/ui/audio/AudioMediaPlayer.cpp \
$(srcdir)/ui/audio/FilterChain.cpp \
$(srcdir)/ui/audio/KeywordSearchFilter.cpp \
$(srcdir)/ui/audio/PlayQueue.cpp \
$(srcdir)/ui/audio/TableFilter.cpp \
$(srcdir)/ui/audio/TrackView.cpp \
$(srcdir)/ui/common/DirectoryValidator.cpp \
+73 -22
View File
@@ -30,6 +30,7 @@
#include "TableFilter.hpp"
#include "KeywordSearchFilter.hpp"
#include "TrackView.hpp"
#include "PlayQueue.hpp"
#include "Audio.hpp"
@@ -66,33 +67,49 @@ _mediaPlayer(nullptr)
mainLayout->addLayout(filterLayout, 0, 0);
// ENDOF(Filters)
// TODO ADD controls (Play, add)
// TODO ADD some stats (nb files, total duration, etc.)
Wt::WVBoxLayout* trackLayout = new Wt::WVBoxLayout();
TrackView* trackView = new TrackView(_db);
mainLayout->addWidget( trackView, 1, 0);
trackLayout->addWidget(trackView, 1);
Wt::WHBoxLayout* trackControls = new Wt::WHBoxLayout();
Wt::WPushButton* playBtn = new Wt::WPushButton("Play");
trackControls->addWidget(playBtn);
Wt::WPushButton* addBtn = new Wt::WPushButton("Add");
trackControls->addWidget(addBtn);
trackControls->addWidget(new Wt::WText("Total duration: "), 1);
trackLayout->addLayout(trackControls);
mainLayout->addLayout(trackLayout, 1, 0);
_filterChain.addFilter(trackView);
// TODO Playlist here
PlayQueue *playQueue = new PlayQueue(_db);
// Playlist/PlayQueue
{
Wt::WVBoxLayout* playlist = new Wt::WVBoxLayout();
Wt::Dbo::Transaction transaction(_db.getSession());
Database::User::pointer user = _db.getCurrentUser();
Wt::WVBoxLayout* playQueueLayout = new Wt::WVBoxLayout();
Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout();
Wt::WComboBox *cb = new Wt::WComboBox();
cb->addItem("metal");
cb->addItem("rock");
cb->addItem("top50");
cb->setWidth(75);
playlistControls->addWidget(new Wt::WPushButton("Playlist"));
playlistControls->addWidget(new Wt::WText("Duration:"));
playlistControls->addWidget(cb, 1);
playlistControls->addWidget(new Wt::WPushButton("Rename"));
playlistControls->addWidget(new Wt::WPushButton("+"));
playlistControls->addWidget(new Wt::WPushButton("-"));
playQueueLayout->addLayout(playlistControls);
playlist->addLayout(playlistControls);
playQueueLayout->addWidget( playQueue, 1);
// TODO
playlist->addWidget( new Wt::WTableView(), 1);
mainLayout->addLayout(playlist, 0, 1, 2, 1);
mainLayout->addLayout(playQueueLayout, 0, 1, 2, 1);
}
_mediaPlayer = new AudioMediaPlayer();
@@ -102,13 +119,46 @@ _mediaPlayer(nullptr)
mainLayout->setRowResizable(0, true, Wt::WLength(200, Wt::WLength::Pixel));
mainLayout->setColumnResizable(0, true);
trackView->trackSelected().connect(boost::bind(&Audio::playTrack, this, _1));
_mediaPlayer->playbackEnded().connect(std::bind([=] ()
// Double click on track
// Set the selected tracks to the play queue
trackView->trackDoubleClicked().connect(std::bind([=] ()
{
// TODO link to playlist
trackView->selectNextTrack();
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
playQueue->clear();
playQueue->addTracks(trackIds);
playQueue->play();
}));
// Play button
// Set the selected tracks to the play queue
playBtn->clicked().connect(std::bind([=] ()
{
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
playQueue->clear();
playQueue->addTracks(trackIds);
playQueue->play();
}));
// Add Button
// Add the selected tracks at the end of the play queue
addBtn->clicked().connect(std::bind([=] ()
{
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
playQueue->addTracks(trackIds);
}));
playQueue->playTrack().connect(this, &Audio::playTrack);
_mediaPlayer->playbackEnded().connect(playQueue, &PlayQueue::handlePlaybackComplete);
}
void
@@ -152,5 +202,6 @@ Audio::playTrack(boost::filesystem::path p)
}
}
} // namespace UserInterface
+2
View File
@@ -45,6 +45,8 @@ class Audio : public Wt::WContainerWidget
void playTrack(boost::filesystem::path p);
void handlePlaylistSelected(Wt::WString name);
Database::Handler& _db;
AudioMediaPlayer* _mediaPlayer;
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright (C) 2014 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 "PlayQueue.hpp"
namespace UserInterface {
PlayQueue::PlayQueue(Database::Handler& db, Wt::WContainerWidget* parent)
: Wt::WTableView(parent),
_db(db),
_playingTrackId(0)
{
_model = new Wt::WStandardItemModel(0, 4, this);
// 0 Column is hidden (track id)
_model->setHeaderData(0, Wt::WString("#"));
_model->setHeaderData(1, Wt::WString("#"));
_model->setHeaderData(2, Wt::WString("Track"));
_model->setHeaderData(3, Wt::WString("Duration"));
this->setModel(_model);
this->setSelectionMode(Wt::ExtendedSelection);
this->setSortingEnabled(false);
this->setAlternatingRowColors(true);
this->setColumnWidth(0, 50);
this->setColumnWidth(1, 50);
this->setColumnHidden(0, true);
this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt)
{
if (!idx.isValid())
return;
// Update selection
Wt::WModelIndexSet indexSet;
indexSet.insert(idx);
this->setSelectedIndexes( indexSet );
// Read the requested track
readTrack(idx.row());
}, std::placeholders::_1, std::placeholders::_2));
}
void
PlayQueue::play(void)
{
readTrack(0);
}
void
PlayQueue::addTracks(const std::vector<Database::Track::id_type>& trackIds)
{
// Add tracks to model
Wt::Dbo::Transaction transaction(_db.getSession());
BOOST_FOREACH(Database::Track::id_type trackId, trackIds)
{
Database::Track::pointer track (Database::Track::getById(_db.getSession(), trackId));
if (track)
{
int dataRow = _model->rowCount();
_model->insertRows(dataRow, 1);
_model->setData(dataRow, 0, track.id());
_model->setData(dataRow, 1, dataRow + 1);
_model->setData(dataRow, 2, track->getName());
_model->setData(dataRow, 3, track->getDuration());
}
}
}
void
PlayQueue::clear(void)
{
_model->removeRows(0, _model->rowCount());
}
void
PlayQueue::handlePlaybackComplete(void)
{
// Play the next track
readTrack(_playingTrackId + 1);
}
void
PlayQueue::readTrack(int rowId)
{
Wt::Dbo::Transaction transaction(_db.getSession());
while (rowId < _model->rowCount())
{
Database::Track::id_type trackId = boost::any_cast<Database::Track::id_type>(_model->data(rowId, 0));
Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
if (track)
{
_playingTrackId = rowId;
_sigTrackPlay.emit(track->getPath());
break;
}
++rowId;
}
}
} // namespace UserInterface
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2014 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 PLAY_QUEUE_HPP
#define PLAY_QUEUE_HPP
#include <Wt/WTableView>
#include <Wt/WStandardItemModel>
#include "database/DatabaseHandler.hpp"
#include "database/AudioTypes.hpp"
namespace UserInterface {
class PlayQueue : public Wt::WTableView
{
public:
PlayQueue(Database::Handler& db, Wt::WContainerWidget* parent = 0);
void addTracks(const std::vector<Database::Track::id_type>& trackIds);
void clear(void);
void play(void);
// Signals
Wt::Signal< boost::filesystem::path >& playTrack() { return _sigTrackPlay; }
// Slots
void saveToPlaylist(std::string& playListName);
void loadFromPlaylist(std::string& playListName);
void handlePlaybackComplete(void);
private:
void readTrack(int rowId);
Wt::Signal< boost::filesystem::path > _sigTrackPlay;
Database::Handler& _db;
Wt::WStandardItemModel* _model;
int _playingTrackId;
};
} // namespace UserInterface
#endif
+1 -1
View File
@@ -105,7 +105,7 @@ TableFilter::getConstraint(Constraint& constraint)
if (!index.isValid())
continue;
ResultType result = _queryModel.resultRow( index.row() );
const ResultType& result = _queryModel.resultRow( index.row() );
// Get the track part
std::string name( result.get<0>() );
+33 -17
View File
@@ -44,10 +44,10 @@ _db(db)
_queryModel.addColumn( "track.original_date", "Original Date" );
_queryModel.addColumn( "track.genre_list", "Genres" );
_queryModel.setBatchSize(250);
_queryModel.setBatchSize(500);
this->setSortingEnabled(true);
this->setSelectionMode(Wt::SingleSelection);
this->setSelectionMode(Wt::ExtendedSelection);
this->setAlternatingRowColors(true);
this->setModel(&_queryModel);
@@ -83,8 +83,21 @@ _db(db)
this->setItemDelegateForColumn(7, delegate);
}
// TODO other event!
this->selectionChanged().connect(this, &TrackView::handleTrackSelected);
// If an item is double clicked, select the track and emit signal
this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt)
{
if (!idx.isValid())
return;
Wt::WModelIndexSet indexSet;
indexSet.insert(idx);
this->setSelectedIndexes( indexSet );
_sigTrackDoubleClicked.emit( );
}, std::placeholders::_1, std::placeholders::_2));
}
// Set constraints created by parent filters
@@ -113,6 +126,7 @@ TrackView::refresh(const Constraint& constraint)
}
/*
void
TrackView::handleTrackSelected(void)
{
@@ -133,26 +147,28 @@ TrackView::handleTrackSelected(void)
}
}
*/
void
TrackView::selectNextTrack(void)
TrackView::getSelectedTracks(std::vector<Database::Track::id_type>& track_ids)
{
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting selected tracks...";
Wt::WModelIndexSet indexSet = this->selectedIndexes();
if (!indexSet.empty()) {
Wt::WModelIndex currentIndex( *indexSet.begin() );
// Check there are remainin tracks!
if (currentIndex.isValid() && this->model()->rowCount() > currentIndex.row() + 1)
{
this->select( this->model()->index( currentIndex.row() + 1, currentIndex.column()));
ResultType result = _queryModel.resultRow( currentIndex.row() + 1 );
BOOST_FOREACH(Wt::WModelIndex index, indexSet)
{
if (!index.isValid())
continue;
// Get the track part
Wt::Dbo::ptr<Database::Track> track ( result.get<0>() );
const ResultType& result = _queryModel.resultRow( index.row() );
_trackSelected.emit( track->getPath() );
}
// Get the track part
Wt::Dbo::ptr<Database::Track> track ( result.get<0>() );
track_ids.push_back(track.id());
}
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting all selected tracks DONE...";
}
} // namespace UserInterface
+8 -6
View File
@@ -36,22 +36,24 @@ class TrackView : public Wt::WTableView, public Filter
TrackView( Database::Handler& db, Wt::WContainerWidget* parent = 0);
// Filter interface
// Set constraints created by parent filters
void refresh(const Constraint& constraint);
// Create constraints for child filters (N/A)
void getConstraint(Constraint& constraint) {}
void selectNextTrack(void); // Emit a trackSelected() if success
// Signals
Wt::Signal< boost::filesystem::path >& trackSelected() { return _trackSelected; }
// Get all the tracks that are currently displayed
void getSelectedTracks(std::vector<Database::Track::id_type>& track_ids);
typedef Wt::Signal<void> SigTrackDoubleClicked;
SigTrackDoubleClicked& trackDoubleClicked() { return _sigTrackDoubleClicked; }
private:
void handleTrackSelected();
Wt::Signal< boost::filesystem::path > _trackSelected;
SigTrackDoubleClicked _sigTrackDoubleClicked;
Database::Handler& _db;
@@ -78,7 +78,6 @@ AvConvTranscodeStreamResource::handleRequest(const Wt::Http::Request& request,
if (!transcoder->isComplete() && response.out()) {
continuation = response.createContinuation();
continuation->setData(transcoder);
LMS_LOG(MOD_UI, SEV_DEBUG) << "Continuation set!";
}
else
LMS_LOG(MOD_UI, SEV_DEBUG) << "No more data!";