--- a/upmpd/ohplaylist.cxx
+++ b/upmpd/ohplaylist.cxx
@@ -39,7 +39,8 @@
#include "ohplaylist.hxx"
#include "mpdcli.hxx"
#include "upmpdutils.hxx"
-#include "rendering.hxx"
+//#include "rendering.hxx"
+#include "base64.hxx"
static const string sTpProduct("urn:av-openhome-org:service:Playlist:1");
static const string sIdProduct("urn:av-openhome-org:serviceId:Playlist");
@@ -47,11 +48,113 @@
OHPlaylist::OHPlaylist(UpMpd *dev, UpMpdRenderCtl *ctl)
: UpnpService(sTpProduct, sIdProduct, dev), m_dev(dev), m_ctl(ctl)
{
-}
+ dev->addActionMapping("Play",
+ bind(&OHPlaylist::play, this, _1, _2));
+ dev->addActionMapping("Pause",
+ bind(&OHPlaylist::pause, this, _1, _2));
+ dev->addActionMapping("Stop",
+ bind(&OHPlaylist::stop, this, _1, _2));
+ dev->addActionMapping("Next",
+ bind(&OHPlaylist::next, this, _1, _2));
+ dev->addActionMapping("Previous",
+ bind(&OHPlaylist::previous, this, _1, _2));
+ dev->addActionMapping("SetRepeat",
+ bind(&OHPlaylist::setRepeat, this, _1, _2));
+ dev->addActionMapping("Repeat",
+ bind(&OHPlaylist::repeat, this, _1, _2));
+ dev->addActionMapping("SetShuffle",
+ bind(&OHPlaylist::setShuffle, this, _1, _2));
+ dev->addActionMapping("Shuffle",
+ bind(&OHPlaylist::shuffle, this, _1, _2));
+ dev->addActionMapping("SeekSecondAbsolute",
+ bind(&OHPlaylist::seekSecondAbsolute, this, _1, _2));
+ dev->addActionMapping("SeekSecondRelative",
+ bind(&OHPlaylist::seekSecondRelative, this, _1, _2));
+ dev->addActionMapping("SeekId",
+ bind(&OHPlaylist::seekId, this, _1, _2));
+ dev->addActionMapping("SeekIndex",
+ bind(&OHPlaylist::seekIndex, this, _1, _2));
+ dev->addActionMapping("TransportState",
+ bind(&OHPlaylist::transportState, this, _1, _2));
+ dev->addActionMapping("Id",
+ bind(&OHPlaylist::id, this, _1, _2));
+ dev->addActionMapping("Read",
+ bind(&OHPlaylist::ohread, this, _1, _2));
+ dev->addActionMapping("Readlist",
+ bind(&OHPlaylist::readlist, this, _1, _2));
+ dev->addActionMapping("Insert",
+ bind(&OHPlaylist::insert, this, _1, _2));
+ dev->addActionMapping("DeleteId",
+ bind(&OHPlaylist::deleteId, this, _1, _2));
+ dev->addActionMapping("DeleteAll",
+ bind(&OHPlaylist::deleteAll, this, _1, _2));
+ dev->addActionMapping("TracksMax",
+ bind(&OHPlaylist::tracksMax, this, _1, _2));
+ dev->addActionMapping("IdArray",
+ bind(&OHPlaylist::idArray, this, _1, _2));
+ dev->addActionMapping("IdArrayChanged",
+ bind(&OHPlaylist::idArrayChanged, this, _1, _2));
+ dev->addActionMapping("ProtocolInfo",
+ bind(&OHPlaylist::protocolInfo, this, _1, _2));
+}
+
+static const int tracksmax = 16384;
+
+// Yes inefficient. whatever...
+static string makesint(int val)
+{
+ char cbuf[30];
+ sprintf(cbuf, "%d", val);
+ return string(cbuf);
+}
+static string mpdstatusToTransportState(MpdStatus::State st)
+{
+ string tstate;
+ switch(st) {
+ case MpdStatus::MPDS_PLAY:
+ tstate = "Playing";
+ break;
+ case MpdStatus::MPDS_PAUSE:
+ tstate = "Paused";
+ break;
+ default:
+ tstate = "Stopped";
+ }
+ return tstate;
+}
+
+// The data format for id lists is an array of msb 32b its ints
+// encoded in base64...
+static string makeIdArray(const vector<unsigned int>& in)
+{
+ string out1;
+ for (vector<unsigned int>::const_iterator it = in.begin();
+ it != in.end(); it++) {
+ out1 += (unsigned char) (((*it) & 0xff000000) >> 24);
+ out1 += (unsigned char) ( ((*it) & 0x00ff0000) >> 16);
+ out1 += (unsigned char) ( ((*it) & 0x0000ff00) >> 8);
+ out1 += (unsigned char) ( (*it) & 0x000000ff);
+ }
+ return base64_encode(out1);
+}
+
bool OHPlaylist::makestate(unordered_map<string, string> &st)
{
st.clear();
+
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+
+ st["TracksMax"] = makesint(tracksmax);
+ st["ProtocolInfo"] = upmpdProtocolInfo;
+
+ st["Id"] = makesint(mpds.songid);
+
+ st["IdArray"] = "";
+
+ st["Repeat"] = makesint(mpds.rept);
+ st["Shuffle"] = makesint(mpds.random);
+ st["TransportState"] = mpdstatusToTransportState(mpds.state);
return true;
}
@@ -59,7 +162,7 @@
bool OHPlaylist::getEventData(bool all, std::vector<std::string>& names,
std::vector<std::string>& values)
{
- LOGDEB("OHPlaylist::getEventData" << endl);
+ //LOGDEB("OHPlaylist::getEventData" << endl);
unordered_map<string, string> state;
makestate(state);
@@ -81,3 +184,278 @@
return true;
}
+int OHPlaylist::play(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::play" << endl);
+ bool ok = m_dev->m_mpdcli->play();
+ m_dev->loopWakeup();
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::pause(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::pause" << endl);
+ bool ok = m_dev->m_mpdcli->pause(true);
+#warning check that using play to disable pause as oh does does not restart from bot
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::stop(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::stop" << endl);
+ bool ok = m_dev->m_mpdcli->stop();
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::next(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::next" << endl);
+ bool ok = m_dev->m_mpdcli->next();
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::previous(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::previous" << endl);
+ bool ok = m_dev->m_mpdcli->previous();
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::setRepeat(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::setRepeat" << endl);
+ bool onoff;
+ bool ok = sc.getBool("Value", &onoff);
+ if (ok) {
+ ok = m_dev->m_mpdcli->repeat(onoff);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::repeat(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::repeat" << endl);
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+ data.addarg("Value", mpds.rept? "1" : "0");
+ return UPNP_E_SUCCESS;
+}
+
+int OHPlaylist::setShuffle(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::setShuffle" << endl);
+ bool onoff;
+ bool ok = sc.getBool("Value", &onoff);
+ if (ok) {
+ // Note that mpd shuffle shuffles the playlist, which is different
+ // from playing at random
+ ok = m_dev->m_mpdcli->random(onoff);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::shuffle(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::shuffle" << endl);
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+ data.addarg("Value", mpds.random ? "1" : "0");
+ return UPNP_E_SUCCESS;
+}
+
+int OHPlaylist::seekSecondAbsolute(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::seekSecondAbsolute" << endl);
+ int seconds;
+ bool ok = sc.getInt("Value", &seconds);
+ if (ok) {
+ ok = m_dev->m_mpdcli->seek(seconds);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::seekSecondRelative(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::seekSecondRelative" << endl);
+ int seconds;
+ bool ok = sc.getInt("Value", &seconds);
+ if (ok) {
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+ bool is_song = (mpds.state == MpdStatus::MPDS_PLAY) ||
+ (mpds.state == MpdStatus::MPDS_PAUSE);
+ if (is_song) {
+ seconds += mpds.songelapsedms / 1000;
+ ok = m_dev->m_mpdcli->seek(seconds);
+ } else {
+ ok = false;
+ }
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::transportState(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::transportState" << endl);
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+ string tstate;
+ switch(mpds.state) {
+ case MpdStatus::MPDS_PLAY:
+ tstate = "Playing";
+ break;
+ case MpdStatus::MPDS_PAUSE:
+ tstate = "Paused";
+ break;
+ default:
+ tstate = "Stopped";
+ }
+ data.addarg("TransportState", tstate);
+ return UPNP_E_SUCCESS;
+}
+
+// Skip to track specified by Id
+int OHPlaylist::seekId(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::seekId" << endl);
+ int id;
+ bool ok = sc.getInt("Value", &id);
+ if (ok) {
+ ok = m_dev->m_mpdcli->playId(id);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+// Skip to track with specified index
+int OHPlaylist::seekIndex(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::seekIndex" << endl);
+ int pos;
+ bool ok = sc.getInt("Value", &pos);
+ if (ok) {
+ ok = m_dev->m_mpdcli->play(pos);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+// Return current Id
+int OHPlaylist::id(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::id" << endl);
+ const MpdStatus &mpds = m_dev->getMpdStatus();
+ data.addarg("Value", makesint(mpds.songid));
+ return UPNP_E_SUCCESS;
+}
+
+// Report the uri and metadata for a given track id.
+// Returns a 800 fault code if the given id is not in the playlist.
+int OHPlaylist::ohread(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::ohread" << endl);
+ int id;
+ bool ok = sc.getInt("Value", &id);
+ unordered_map<string, string> props;
+ if (ok) {
+ ok = m_dev->m_mpdcli->statSong(props, id, true);
+ }
+ if (ok) {
+ data.addarg("Uri", props["uri"]);
+ string metadata; // = didlmake(mpds);
+ data.addarg("Metadata", metadata);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+// Given a space separated list of track Id's, report their associated
+// uri and metadata in the following xml form:
+//
+// <TrackList>
+// <Entry>
+// <Id></Id>
+// <Uri></Uri>
+// <Metadata></Metadata>
+// </Entry>
+// </TrackList>
+//
+// Any ids not in the playlist are ignored.
+int OHPlaylist::readlist(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::readlist" << endl);
+ bool ok = false;
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+// Adds the given uri and metadata as a new track to the playlist.
+// Set the AfterId argument to 0 to insert a track at the start of the
+// playlist.
+// Reports a 800 fault code if AfterId is not 0 and doesn���t appear in
+// the playlist.
+// Reports a 801 fault code if the playlist is full (i.e. already
+// contains TracksMax tracks).
+int OHPlaylist::insert(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::insert" << endl);
+ int afterid;
+ string uri, metadata;
+ bool ok = sc.getInt("AfterId", &afterid);
+ ok = ok && sc.getString("Uri", &uri);
+ ok = ok && sc.getString("Metadata", &metadata);
+
+ LOGDEB("OHPlaylist::insert: afterid " << afterid << " Uri " <<
+ uri << " Metadata " << metadata << endl);
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::deleteId(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::deleteId" << endl);
+ int id;
+ bool ok = sc.getInt("Value", &id);
+ if (ok) {
+ ok = m_dev->m_mpdcli->deleteId(id);
+ }
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::deleteAll(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::deleteAll" << endl);
+ bool ok = m_dev->m_mpdcli->clearQueue();
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::tracksMax(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::tracksMax" << endl);
+ data.addarg("Value", makesint(tracksmax));
+ return UPNP_E_SUCCESS;
+}
+
+// Returns current list of id as array of big endian 32bits integers,
+// base-64-encoded.
+int OHPlaylist::idArray(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::idArray" << endl);
+ bool ok = false;
+
+ data.addarg("Token", "0");
+ data.addarg("Array", "");
+
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+// Check if id array changed since last call (which returned a gen token)
+int OHPlaylist::idArrayChanged(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::idArrayChanged" << endl);
+ bool ok = false;
+
+ data.addarg("Token", "0");
+ // Bool indicating if array changed
+ data.addarg("Value", "");
+
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHPlaylist::protocolInfo(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHPlaylist::protocolInfo" << endl);
+ data.addarg("Value", upmpdProtocolInfo);
+ return UPNP_E_SUCCESS;
+}