JS implementation of the player
This commit is contained in:
+11
-30
@@ -2,37 +2,18 @@
|
|||||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
<!--FORMS message blocks-->
|
<!--FORMS message blocks-->
|
||||||
|
|
||||||
<message id="btn-mediaplayer-prev">
|
|
||||||
<i class="fa fa-step-backward"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="btn-mediaplayer-play">
|
|
||||||
<i class="fa fa-play"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="btn-mediaplayer-pause">
|
|
||||||
<i class="fa fa-pause"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="btn-mediaplayer-next">
|
|
||||||
<i class="fa fa-step-forward"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="btn-mediaplayer-shuffle">
|
|
||||||
<i class="fa fa-random"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="btn-mediaplayer-repeat">
|
|
||||||
<i class="fa fa-repeat"></i>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="template-mediaplayer">
|
<message id="template-mediaplayer">
|
||||||
${previous} ${play-pause} ${next}
|
<audio id="lms-mp-audio"></audio>
|
||||||
${cover}
|
<div id="lms-mp-play"><i class="fa fa-play fa-2x"></i></div>
|
||||||
${current-time} ${total-time} ${shuffle} ${repeat}
|
<div id="lms-mp-pause" style="display: none"><i class="fa fa-pause fa-2x"></i></div>
|
||||||
${<if-has-artist>}${artist-name}${</if-has-artist>}
|
<div id="lms-mp-title">...</div>
|
||||||
${<if-has-release>}${release-name}${</if-has-release>}
|
<div id="lms-mp-artist"></div>
|
||||||
${track-name}
|
<div id="lms-mp-release"></div>
|
||||||
|
<div id="lms-mp-time">00:00</div>
|
||||||
|
<div id="lms-mp-duration">00:00</div>
|
||||||
|
<div id="lms-mp-cover"></div>
|
||||||
|
<div id="lms-mp-previous"><i class="fa fa-step-backward fa-2x"></i></div>
|
||||||
|
<div id="lms-mp-next"><i class="fa fa-step-forward fa-2x"></i></div>
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
</messages>
|
</messages>
|
||||||
|
|||||||
@@ -1 +1,81 @@
|
|||||||
/* TEST */
|
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
||||||
|
|
||||||
|
var LMS = LMS || {};
|
||||||
|
|
||||||
|
LMS.mediaplayer = function () {
|
||||||
|
|
||||||
|
var _root = {};
|
||||||
|
var _elems = {};
|
||||||
|
|
||||||
|
var _updateControls = function() {
|
||||||
|
if (_elems.audio.paused) {
|
||||||
|
_elems.play.style.display = "block";
|
||||||
|
_elems.pause.style.display = "none";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_elems.pause.style.display = "block";
|
||||||
|
_elems.play.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var init = function(root) {
|
||||||
|
_root = root;
|
||||||
|
|
||||||
|
_elems.audio = document.getElementById("lms-mp-audio");
|
||||||
|
_elems.play = document.getElementById("lms-mp-play");
|
||||||
|
_elems.pause = document.getElementById("lms-mp-pause");
|
||||||
|
_elems.previous = document.getElementById("lms-mp-previous");
|
||||||
|
_elems.next = document.getElementById("lms-mp-next");
|
||||||
|
_elems.title = document.getElementById("lms-mp-title");
|
||||||
|
_elems.artist = document.getElementById("lms-mp-artist");
|
||||||
|
_elems.release = document.getElementById("lms-mp-release");
|
||||||
|
_elems.duration = document.getElementById("lms-mp-duration");
|
||||||
|
|
||||||
|
_elems.play.addEventListener("click", function() {
|
||||||
|
_elems.audio.play();
|
||||||
|
});
|
||||||
|
_elems.pause.addEventListener("click", function() {
|
||||||
|
_elems.audio.pause();
|
||||||
|
});
|
||||||
|
|
||||||
|
_elems.previous.addEventListener("click", function() {
|
||||||
|
Wt.emit(_root, "playPrevious");
|
||||||
|
});
|
||||||
|
_elems.next.addEventListener("click", function() {
|
||||||
|
Wt.emit(_root, "playNext");
|
||||||
|
});
|
||||||
|
|
||||||
|
_elems.audio.addEventListener("play", _updateControls);
|
||||||
|
_elems.audio.addEventListener("playing", _updateControls);
|
||||||
|
_elems.audio.addEventListener("pause", _updateControls);
|
||||||
|
|
||||||
|
_elems.audio.addEventListener("ended", function() {
|
||||||
|
Wt.emit(_root, "playbackEnded");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadTrack = function(params, autoplay) {
|
||||||
|
console.log("Loading track '" + params.name + "', release = '" + params.release + "', artist = '" + params.artist + "', resource = '" + params.resource + "', imgResource = '" + params.imgResource);
|
||||||
|
|
||||||
|
_elems.title.innerHTML = params.name;
|
||||||
|
_elems.artist.innerHTML = params.artist;
|
||||||
|
_elems.release.innerHTML = params.release;
|
||||||
|
_elems.audio.src = params.resource;
|
||||||
|
_elems.duration.innerHTML = params.duration;
|
||||||
|
|
||||||
|
if (autoplay)
|
||||||
|
_elems.audio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stop = function() {
|
||||||
|
_elems.audio.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init,
|
||||||
|
loadTrack: loadTrack,
|
||||||
|
stop: stop,
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
// @license-end
|
||||||
|
|||||||
+26
-55
@@ -16,10 +16,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <Wt/WImage>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <Wt/WSlider>
|
|
||||||
#include <Wt/WTemplate>
|
|
||||||
#include <Wt/WText>
|
|
||||||
|
|
||||||
#include "av/AvInfo.hpp"
|
#include "av/AvInfo.hpp"
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
@@ -33,50 +30,17 @@
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent)
|
MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent)
|
||||||
: Wt::WContainerWidget(parent)
|
: Wt::WTemplate(Wt::WString::tr("template-mediaplayer"), parent),
|
||||||
|
playbackEnded(this, "playbackEnded"),
|
||||||
|
playPrevious(this, "playPrevious"),
|
||||||
|
playNext(this, "playNext")
|
||||||
{
|
{
|
||||||
_audio = new Wt::WAudio(this);
|
wApp->doJavaScript("LMS.mediaplayer.init(" + jsRef() + ")");
|
||||||
_audio->setOptions(Wt::WAudio::Autoplay);
|
}
|
||||||
_audio->setPreloadMode(Wt::WAudio::PreloadNone);
|
|
||||||
|
|
||||||
_audio->ended().connect(std::bind([=] ()
|
static std::string escape(const std::string& str)
|
||||||
{
|
{
|
||||||
playbackEnded.emit();
|
return boost::replace_all_copy(str, "\"", "\\\"");
|
||||||
}));
|
|
||||||
|
|
||||||
auto player = new Wt::WTemplate(Wt::WString::tr("template-mediaplayer"), this);
|
|
||||||
|
|
||||||
auto playPauseBtn = new Wt::WText(Wt::WString::tr("btn-mediaplayer-play"), Wt::XHTMLText);
|
|
||||||
player->bindWidget("play-pause", playPauseBtn);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
auto repeatBtn = new Wt::WText(Wt::WString::tr("btn-mediaplayer-repeat"), Wt::XHTMLText);
|
|
||||||
player->bindWidget("repeat", repeatBtn);
|
|
||||||
|
|
||||||
player->bindString("current-time", "00:00");
|
|
||||||
player->bindString("total-time", "00:00");
|
|
||||||
|
|
||||||
auto cover = new Wt::WImage();
|
|
||||||
player->bindWidget("cover", cover);
|
|
||||||
|
|
||||||
auto trackName = new Wt::WText("---");
|
|
||||||
player->bindWidget("track-name", trackName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -86,32 +50,39 @@ MediaPlayer::playTrack(Database::Track::id_type trackId)
|
|||||||
|
|
||||||
Wt::Dbo::Transaction transaction(DboSession());
|
Wt::Dbo::Transaction transaction(DboSession());
|
||||||
auto track = Database::Track::getById(DboSession(), trackId);
|
auto track = Database::Track::getById(DboSession(), trackId);
|
||||||
transaction.commit();
|
|
||||||
|
|
||||||
// Analyse track, select the best media stream
|
// Analyse track, select the best media stream
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Av::MediaFile mediaFile(track->getPath());
|
Av::MediaFile mediaFile(track->getPath());
|
||||||
|
|
||||||
auto streamId = mediaFile.getBestStream();
|
auto resource = LmsApp->getTranscodeResource()->getUrl(trackId, Av::Encoding::MP3, boost::posix_time::seconds(0));
|
||||||
|
|
||||||
_audio->pause();
|
std::ostringstream oss;
|
||||||
_audio->clearSources();
|
oss
|
||||||
_audio->addSource(LmsApp->getTranscodeResource()->getUrl(trackId, Av::Encoding::MP3, boost::posix_time::seconds(0), streamId));
|
<< "var params = {"
|
||||||
_audio->setPreloadMode(Wt::WAudio::PreloadNone);
|
<< " name: \"" << escape(track->getName()) + "\","
|
||||||
_audio->play();
|
<< " release: " << (track->getRelease() ? "\"" + escape(track->getRelease()->getName()) + "\"" : "undefined" ) << ","
|
||||||
|
<< " artist: " << (track->getArtist() ? "\"" + escape(track->getArtist()->getName()) + "\"" : "undefined" ) << ","
|
||||||
|
<< " resource: \"" << resource << "\","
|
||||||
|
<< " duration: " << track->getDuration().total_seconds() << ","
|
||||||
|
<< "};";
|
||||||
|
oss << "LMS.mediaplayer.loadTrack(params, true)"; // true to autoplay
|
||||||
|
|
||||||
|
LMS_LOG(UI, DEBUG) << "Runing js = '" << oss.str() << "'";
|
||||||
|
|
||||||
|
wApp->doJavaScript(oss.str());
|
||||||
}
|
}
|
||||||
catch (Av::MediaFileException& e)
|
catch (Av::MediaFileException& e)
|
||||||
{
|
{
|
||||||
LMS_LOG(UI, ERROR) << "MediaFileException: " << e.what();
|
LMS_LOG(UI, ERROR) << "MediaFileException: " << e.what();
|
||||||
stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaPlayer::stop()
|
MediaPlayer::stop()
|
||||||
{
|
{
|
||||||
_audio->pause();
|
wApp->doJavaScript("LMS.mediaplayer.stop()");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -19,15 +19,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/WSignal>
|
#include <Wt/WJavaScript>
|
||||||
#include <Wt/WAudio>
|
#include <Wt/WTemplate>
|
||||||
#include <Wt/WContainerWidget>
|
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class MediaPlayer : public Wt::WContainerWidget
|
class MediaPlayer : public Wt::WTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MediaPlayer(Wt::WContainerWidget* parent = 0);
|
MediaPlayer(Wt::WContainerWidget* parent = 0);
|
||||||
@@ -36,12 +35,12 @@ class MediaPlayer : public Wt::WContainerWidget
|
|||||||
void playTrack(Database::Track::id_type);
|
void playTrack(Database::Track::id_type);
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
Wt::Signal<void> playbackEnded;
|
Wt::JSignal<void> playbackEnded;
|
||||||
Wt::Signal<void> playPrevious;
|
Wt::JSignal<void> playPrevious;
|
||||||
Wt::Signal<void> playNext;
|
Wt::JSignal<void> playNext;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Wt::WAudio* _audio;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -41,11 +41,9 @@ TranscodeResource:: ~TranscodeResource()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
TranscodeResource::getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset, boost::optional<std::size_t> streamId) const
|
TranscodeResource::getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset) const
|
||||||
{
|
{
|
||||||
std::string res = url()+ "&trackid=" + std::to_string(trackId) + "&encoding=" + std::to_string(Av::encoding_to_int(encoding));
|
std::string res = url()+ "&trackid=" + std::to_string(trackId) + "&encoding=" + std::to_string(Av::encoding_to_int(encoding));
|
||||||
if (streamId)
|
|
||||||
res += "&stream=" + std::to_string(*streamId);
|
|
||||||
res += "&offset=" + std::to_string(offset.seconds());
|
res += "&offset=" + std::to_string(offset.seconds());
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WResource>
|
#include <Wt/WResource>
|
||||||
|
|
||||||
#include "av/AvTranscoder.hpp"
|
#include "av/AvTranscoder.hpp"
|
||||||
@@ -37,7 +35,7 @@ class TranscodeResource : public Wt::WResource
|
|||||||
TranscodeResource(Database::Handler& db, Wt::WObject *parent);
|
TranscodeResource(Database::Handler& db, Wt::WObject *parent);
|
||||||
~TranscodeResource();
|
~TranscodeResource();
|
||||||
|
|
||||||
std::string getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset, boost::optional<size_t> stream = boost::none) const;
|
std::string getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset) const;
|
||||||
|
|
||||||
void handleRequest(const Wt::Http::Request& request,
|
void handleRequest(const Wt::Http::Request& request,
|
||||||
Wt::Http::Response& response);
|
Wt::Http::Response& response);
|
||||||
|
|||||||
Reference in New Issue
Block a user