[UI/MOBILE] Can now play tracks from views

This commit is contained in:
emeric
2016-03-06 20:08:28 +01:00
parent 4e63d99e3e
commit 17f84c22bb
15 changed files with 95 additions and 76 deletions
+1 -1
View File
@@ -490,7 +490,7 @@
</message> </message>
<message id="wa-trackview-track"> <message id="wa-trackview-track">
<div class="menu-item list-group-item ">${<if-has-disc-num>}${disc-num}-${</if-has-disc-num>}${<if-has-track-num>}${track-num}. ${</if-has-track-num>}${track-name} <span class="badge">${btn}</span><span class="badge">${time}</span></div> <div class="menu-item list-group-item ">${<if-has-disc-num>}${disc-num}-${</if-has-disc-num>}${<if-has-track-num>}${track-num}. ${</if-has-track-num>}${track-name} <span class="badge">${play-btn}</span></div>
</message> </message>
<message id="wa-audio-player"> <message id="wa-audio-player">
+7 -8
View File
@@ -194,11 +194,9 @@ LmsApplication::handleAuthEvent(void)
this->root()->setOverflow(Wt::WContainerWidget::OverflowHidden); this->root()->setOverflow(Wt::WContainerWidget::OverflowHidden);
setConfirmCloseMessage("Closing LMS. Are you sure?"); setConfirmCloseMessage("Closing LMS. Are you sure?");
// Handle internal paths
// Create a Vertical layout: top is the nav bar, bottom is the contents // Create a Vertical layout: top is the nav bar, bottom is the contents
Wt::WVBoxLayout *layout = new Wt::WVBoxLayout(this->root()); Wt::WVBoxLayout *layout = new Wt::WVBoxLayout(this->root());
// Create a navigation bar with a link to a web page.
Wt::WNavigationBar *navigation = new Wt::WNavigationBar(); Wt::WNavigationBar *navigation = new Wt::WNavigationBar();
navigation->setTitle("LMS", "https://github.com/epoupon/lms"); navigation->setTitle("LMS", "https://github.com/epoupon/lms");
navigation->setResponsive(true); navigation->setResponsive(true);
@@ -219,19 +217,20 @@ LmsApplication::handleAuthEvent(void)
searchEdit->setWidth(150); searchEdit->setWidth(150);
// TODO add a span with a search icon // TODO add a span with a search icon
Audio *audio; menu->setInternalPathEnabled();
menu->setInternalBasePath("/");
Audio *audio;
if (agentIsMobile()) if (agentIsMobile())
audio = new Mobile::Audio(); audio = new Mobile::Audio();
else else
audio = new Desktop::Audio(); audio = new Desktop::Audio();
menu->addItem("Audio", audio); menu->addItem("Audio", audio)->setPathComponent("audio");;
#if defined HAVE_VIDEO #if defined HAVE_VIDEO
menu->addItem("Video", new VideoWidget()); menu->addItem("Video", new VideoWidget())->setPathComponent("video");
#endif #endif
menu->addItem("Settings", new Settings::Settings()); menu->addItem("Settings", new Settings::Settings())->setPathComponent("settings");
Wt::WPopupMenu *popup = new Wt::WPopupMenu(); Wt::WPopupMenu *popup = new Wt::WPopupMenu();
popup->addItem("Logout"); popup->addItem("Logout");
+8 -15
View File
@@ -55,18 +55,6 @@ enum WidgetIdx
using namespace Database; using namespace Database;
static void playTrack(AudioPlayer *audioPlayer, Database::Track::id_type trackId)
{
LMS_LOG(UI, DEBUG) << "Playing track id " << trackId;
Wt::Dbo::Transaction transaction(DboSession());
Track::pointer track = Track::getById(DboSession(), trackId);
if (track)
audioPlayer->loadTrack(track.id());
}
void void
Audio::search(std::string text) Audio::search(std::string text)
{ {
@@ -83,12 +71,12 @@ Audio::Audio(Wt::WContainerWidget *parent)
Wt::WStackedWidget *stack = new Wt::WStackedWidget(this); Wt::WStackedWidget *stack = new Wt::WStackedWidget(this);
// Same order as WidgetIdxXXX // Same order as WidgetIdxXXX
stack->addWidget(new PreviewSearchView()); stack->addWidget(new PreviewSearchView(_playQueueEvents));
stack->addWidget(new ArtistSearchView()); stack->addWidget(new ArtistSearchView());
stack->addWidget(new ReleaseSearchView()); stack->addWidget(new ReleaseSearchView());
stack->addWidget(new TrackSearchView()); stack->addWidget(new TrackSearchView(_playQueueEvents));
stack->addWidget(new ArtistView()); stack->addWidget(new ArtistView());
stack->addWidget(new ReleaseView()); stack->addWidget(new ReleaseView(_playQueueEvents));
wApp->internalPathChanged().connect(std::bind([=] (std::string path) wApp->internalPathChanged().connect(std::bind([=] (std::string path)
{ {
@@ -117,6 +105,11 @@ Audio::Audio(Wt::WContainerWidget *parent)
AudioPlayer* audioPlayer = new AudioPlayer(); AudioPlayer* audioPlayer = new AudioPlayer();
footer->bindWidget("player", audioPlayer); footer->bindWidget("player", audioPlayer);
// Connect the events to the player
_playQueueEvents.trackPlay.connect(std::bind([=] (Database::Track::id_type id)
{
audioPlayer->loadTrack(id);
}, std::placeholders::_1));
} }
} // namespace Mobile } // namespace Mobile
+7 -3
View File
@@ -17,13 +17,15 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef UI_AUDIO_MOBILE_HPP #pragma once
#define UI_AUDIO_MOBILE_HPP
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget>
#include "audio/Audio.hpp" #include "audio/Audio.hpp"
#include "MobilePlayQueueEvents.hpp"
namespace UserInterface { namespace UserInterface {
namespace Mobile { namespace Mobile {
@@ -35,9 +37,11 @@ class Audio : public UserInterface::Audio
void search(std::string text); void search(std::string text);
private: private:
PlayQueueEvents _playQueueEvents;
}; };
} // namespace Mobile } // namespace Mobile
} // namespace UserInterface } // namespace UserInterface
#endif
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 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/>.
*/
#pragma once
#include <Wt/WSignal>
#include "database/Types.hpp"
namespace UserInterface {
namespace Mobile {
struct PlayQueueEvents
{
Wt::Signal<Database::Track::id_type> trackPlay;
};
} // namespace Mobile
} // namespace UserInterface
+2 -2
View File
@@ -36,13 +36,13 @@ namespace Mobile {
using namespace Database; using namespace Database;
PreviewSearchView::PreviewSearchView(Wt::WContainerWidget* parent) PreviewSearchView::PreviewSearchView(PlayQueueEvents& events, Wt::WContainerWidget* parent)
{ {
const std::string pathPrefix = "/audio/search/preview"; const std::string pathPrefix = "/audio/search/preview";
ArtistSearch* artistSearch = new ArtistSearch("Artists", this); ArtistSearch* artistSearch = new ArtistSearch("Artists", this);
ReleaseSearch* releaseSearch = new ReleaseSearch(this); ReleaseSearch* releaseSearch = new ReleaseSearch(this);
TrackSearch* trackSearch = new TrackSearch(this); TrackSearch* trackSearch = new TrackSearch(events, this);
artistSearch->showMore().connect(std::bind([=] artistSearch->showMore().connect(std::bind([=]
{ {
+3 -1
View File
@@ -21,6 +21,8 @@
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget>
#include "MobilePlayQueueEvents.hpp"
namespace UserInterface { namespace UserInterface {
namespace Mobile { namespace Mobile {
@@ -28,7 +30,7 @@ class PreviewSearchView : public Wt::WContainerWidget
{ {
public: public:
PreviewSearchView(Wt::WContainerWidget* parent = 0); PreviewSearchView(PlayQueueEvents& events, Wt::WContainerWidget* parent = 0);
}; };
-1
View File
@@ -20,7 +20,6 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget>
#include <Wt/WSignal>
#include <Wt/WText> #include <Wt/WText>
#include "database/SearchFilter.hpp" #include "database/SearchFilter.hpp"
+7 -26
View File
@@ -33,8 +33,9 @@ namespace Mobile {
using namespace Database; using namespace Database;
ReleaseView::ReleaseView(Wt::WContainerWidget *parent) ReleaseView::ReleaseView(PlayQueueEvents& events, Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent) : Wt::WContainerWidget(parent),
_events(events)
{ {
Wt::WTemplate* wrapper = new Wt::WTemplate(this); Wt::WTemplate* wrapper = new Wt::WTemplate(this);
wrapper->setTemplateText(Wt::WString::tr("wa-trackview-wrapper")); wrapper->setTemplateText(Wt::WString::tr("wa-trackview-wrapper"));
@@ -97,23 +98,6 @@ ReleaseView::search(SearchFilter filter, size_t nb)
addResults(nb); addResults(nb);
} }
static
std::vector<Track::pointer >
getTracks(SearchFilter filter, size_t offset, size_t nb, bool &moreResults)
{
std::vector<Track::pointer > tracks = Track::getByFilter(DboSession(), filter, offset, nb + 1);
if (tracks.size() == nb + 1)
{
moreResults = true;
tracks.pop_back();
}
else
moreResults = false;
return tracks;
}
static Wt::WString static Wt::WString
getArtistNameFromRelease(Release::pointer release) getArtistNameFromRelease(Release::pointer release)
{ {
@@ -132,7 +116,7 @@ ReleaseView::addResults(size_t nb)
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(DboSession());
bool moreResults; bool moreResults;
std::vector<Track::pointer > tracks = getTracks(_filter, _nbTracks, nb, moreResults); std::vector<Track::pointer> tracks = Track::getByFilter(DboSession(), _filter, _nbTracks, nb, moreResults);
for (Track::pointer track : tracks) for (Track::pointer track : tracks)
{ {
@@ -150,7 +134,7 @@ ReleaseView::addResults(size_t nb)
Wt::WImage *cover = new Wt::WImage(); Wt::WImage *cover = new Wt::WImage();
cover->setStyleClass ("center-block img-responsive"); // TODO move to CSS? cover->setStyleClass ("center-block img-responsive"); // TODO move to CSS?
cover->setImageLink(Wt::WLink (LmsApp->getImageResource()->getReleaseUrl(release.id(), 512))); cover->setImageLink(Wt::WLink (SessionImageResource()->getReleaseUrl(release.id(), 512)));
releaseContainer->bindWidget("cover", cover); releaseContainer->bindWidget("cover", cover);
releaseContainer->bindString("artist-name", getArtistNameFromRelease(release), Wt::PlainText); releaseContainer->bindString("artist-name", getArtistNameFromRelease(release), Wt::PlainText);
@@ -191,16 +175,13 @@ ReleaseView::addResults(size_t nb)
trackRes->bindString("track-name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText); trackRes->bindString("track-name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText);
// TODO, display artist name for compilation releases? // TODO, display artist name for compilation releases?
std::string format = track->getDuration().total_seconds() < 3600 ? "%M:%S" : "%H:%M:%S";
trackRes->bindString("time", durationToString(track->getDuration(), format), Wt::PlainText);
Wt::WText *playBtn = new Wt::WText("Play", Wt::PlainText); Wt::WText *playBtn = new Wt::WText("Play", Wt::PlainText);
playBtn->setStyleClass("center-block"); // TODO move to CSS? playBtn->setStyleClass("center-block"); // TODO move to CSS?
playBtn->clicked().connect(std::bind([=] { playBtn->clicked().connect(std::bind([=] {
_sigTrackPlay.emit(track.id()); _events.trackPlay.emit(track.id());
})); }));
trackRes->bindWidget("btn", playBtn); trackRes->bindWidget("play-btn", playBtn);
_nbTracks++; _nbTracks++;
} }
+4 -3
View File
@@ -22,6 +22,8 @@
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget>
#include <Wt/WSignal> #include <Wt/WSignal>
#include "MobilePlayQueueEvents.hpp"
namespace UserInterface { namespace UserInterface {
namespace Mobile { namespace Mobile {
@@ -31,22 +33,21 @@ class ReleaseView : public Wt::WContainerWidget
static Wt::WLink getLink(Database::Release::id_type id); static Wt::WLink getLink(Database::Release::id_type id);
ReleaseView(Wt::WContainerWidget *parent = 0); ReleaseView(PlayQueueEvents& events, Wt::WContainerWidget *parent = 0);
void search(Database::SearchFilter filter, size_t nb); void search(Database::SearchFilter filter, size_t nb);
// Slots // Slots
Wt::Signal<Database::Track::id_type>& trackPlay() { return _sigTrackPlay;}
Wt::Signal<void>& moreTracksSelected() { return _sigMoreTracksSelected;} Wt::Signal<void>& moreTracksSelected() { return _sigMoreTracksSelected;}
private: private:
Wt::Signal<Database::Track::id_type> _sigTrackPlay;
Wt::Signal<void> _sigMoreTracksSelected; Wt::Signal<void> _sigMoreTracksSelected;
void clear(void); void clear(void);
void addResults(size_t nb); void addResults(size_t nb);
PlayQueueEvents& _events;
Wt::WTemplate* _showMore; Wt::WTemplate* _showMore;
Database::SearchFilter _filter; Database::SearchFilter _filter;
+4 -3
View File
@@ -33,8 +33,9 @@ namespace Mobile {
using namespace Database; using namespace Database;
TrackSearch::TrackSearch(Wt::WContainerWidget *parent) TrackSearch::TrackSearch(PlayQueueEvents& events, Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent) : Wt::WContainerWidget(parent),
_events(events)
{ {
Wt::WTemplate* t = new Wt::WTemplate(this); Wt::WTemplate* t = new Wt::WTemplate(this);
t->setTemplateText(Wt::WString::tr("wa-track-search")); t->setTemplateText(Wt::WString::tr("wa-track-search"));
@@ -97,7 +98,7 @@ TrackSearch::addResults(size_t nb)
Wt::WText *playBtn = new Wt::WText("Play", Wt::PlainText); Wt::WText *playBtn = new Wt::WText("Play", Wt::PlainText);
playBtn->setStyleClass("center-block"); // TODO move to CSS? playBtn->setStyleClass("center-block"); // TODO move to CSS?
playBtn->clicked().connect(std::bind([=] { playBtn->clicked().connect(std::bind([=] {
_sigTrackPlay.emit(track.id()); _events.trackPlay.emit(track.id());
})); }));
res->bindWidget("btn", playBtn); res->bindWidget("btn", playBtn);
+4 -3
View File
@@ -25,6 +25,8 @@
#include "database/SearchFilter.hpp" #include "database/SearchFilter.hpp"
#include "database/Types.hpp" #include "database/Types.hpp"
#include "MobilePlayQueueEvents.hpp"
namespace UserInterface { namespace UserInterface {
namespace Mobile { namespace Mobile {
@@ -32,22 +34,21 @@ class TrackSearch : public Wt::WContainerWidget
{ {
public: public:
TrackSearch(Wt::WContainerWidget *parent = 0); TrackSearch(PlayQueueEvents& events, Wt::WContainerWidget *parent = 0);
void addResults(size_t nb); void addResults(size_t nb);
void search(Database::SearchFilter filter, size_t nb); void search(Database::SearchFilter filter, size_t nb);
// Slots // Slots
Wt::Signal<void>& showMore() { return _sigShowMore;} Wt::Signal<void>& showMore() { return _sigShowMore;}
Wt::Signal<Database::Track::id_type>& trackPlay() { return _sigTrackPlay;}
private: private:
Wt::Signal<void> _sigShowMore; Wt::Signal<void> _sigShowMore;
Wt::Signal<Database::Track::id_type> _sigTrackPlay;
void clear(void); void clear(void);
PlayQueueEvents& _events;
Wt::WTemplate* _showMore; Wt::WTemplate* _showMore;
Database::SearchFilter _filter; Database::SearchFilter _filter;
Wt::WContainerWidget* _contents; Wt::WContainerWidget* _contents;
+2 -2
View File
@@ -34,9 +34,9 @@ namespace Mobile {
using namespace Database; using namespace Database;
TrackSearchView::TrackSearchView(Wt::WContainerWidget* parent) TrackSearchView::TrackSearchView(PlayQueueEvents& events, Wt::WContainerWidget* parent)
{ {
TrackSearch* trackSearch = new TrackSearch(this); TrackSearch* trackSearch = new TrackSearch(events, this);
trackSearch->showMore().connect(std::bind([=] { trackSearch->showMore().connect(std::bind([=] {
trackSearch->addResults(SEARCH_NB_ITEMS); trackSearch->addResults(SEARCH_NB_ITEMS);
})); }));
+2 -1
View File
@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget>
#include <Wt/WSignal>
namespace UserInterface { namespace UserInterface {
namespace Mobile { namespace Mobile {
@@ -28,7 +29,7 @@ class TrackSearchView : public Wt::WContainerWidget
{ {
public: public:
TrackSearchView(Wt::WContainerWidget* parent = 0); TrackSearchView(PlayQueueEvents& events, Wt::WContainerWidget* parent = 0);
}; };
+9 -7
View File
@@ -55,6 +55,8 @@ Settings::Settings(Wt::WContainerWidget* parent)
Wt::WMenu *menu = new Wt::WMenu(contents, Wt::Vertical); Wt::WMenu *menu = new Wt::WMenu(contents, Wt::Vertical);
menu->setStyleClass("nav nav-pills nav-stacked submenu"); menu->setStyleClass("nav nav-pills nav-stacked submenu");
menu->setWidth(150); menu->setWidth(150);
menu->setInternalPathEnabled();
menu->setInternalBasePath("/settings/");
hLayout->addWidget(menu); hLayout->addWidget(menu);
hLayout->addWidget(contents, 1); hLayout->addWidget(contents, 1);
@@ -68,22 +70,22 @@ Settings::Settings(Wt::WContainerWidget* parent)
userIsAdmin = CurrentUser()->isAdmin(); userIsAdmin = CurrentUser()->isAdmin();
} }
menu->addItem("Audio", new AudioFormView()); menu->addItem("Audio", new AudioFormView())->setPathComponent("audio");
if (userIsAdmin) if (userIsAdmin)
{ {
MediaDirectories* mediaDirectory = new MediaDirectories(); MediaDirectories* mediaDirectories = new MediaDirectories();
mediaDirectory->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged); mediaDirectories->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged);
menu->addItem("Media Folders", mediaDirectory); menu->addItem("Media Folders", mediaDirectories)->setPathComponent("mediadirectories");
DatabaseFormView* databaseFormView = new DatabaseFormView(); DatabaseFormView* databaseFormView = new DatabaseFormView();
databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService); databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService);
menu->addItem("Database", databaseFormView); menu->addItem("Database", databaseFormView)->setPathComponent("database");
menu->addItem("Users", new Users()); menu->addItem("Users", new Users())->setPathComponent("users");
} }
else else
{ {
menu->addItem("Account", new AccountFormView(userId)); menu->addItem("Account", new AccountFormView(userId))->setPathComponent("account");
} }
} }