--- a/src/ohplaylist.cxx
+++ b/src/ohplaylist.cxx
@@ -244,19 +244,51 @@
return true;
}
+// (private)
+int OHPlaylist::idFromOldId(int oldid)
+{
+ string uri;
+ for (const auto& entry: m_mpdsavedstate.queue) {
+ if (entry.mpdid == oldid) {
+ uri = entry.uri;
+ break;
+ }
+ }
+ if (uri.empty()) {
+ LOGERR("OHPlaylist::idFromOldId: " << oldid << " not found\n");
+ return -1;
+ }
+ vector<UpSong> vdata;
+ if (!m_dev->m_mpdcli->getQueueData(vdata)) {
+ LOGERR("OHPlaylist::idFromUri: getQueueData failed\n");
+ return -1;
+ }
+ for (const auto& entry: vdata) {
+ if (!entry.uri.compare(uri)) {
+ return entry.mpdid;
+ }
+ }
+ LOGERR("OHPlaylist::idFromOldId: uri for " << oldid << " not found\n");
+ return -1;
+}
+
bool OHPlaylist::makestate(unordered_map<string, string> &st)
{
- st.clear();
-
- const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
-
- st["TransportState"] = mpdstatusToTransportState(mpds.state);
- st["Repeat"] = SoapHelp::i2s(mpds.rept);
- st["Shuffle"] = SoapHelp::i2s(mpds.random);
- st["Id"] = mpds.songid == -1 ? "0" : SoapHelp::i2s(mpds.songid);
- st["TracksMax"] = SoapHelp::i2s(tracksmax);
- st["ProtocolInfo"] = Protocolinfo::the()->gettext();
- makeIdArray(st["IdArray"]);
+ if (m_active) {
+ st.clear();
+
+ const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
+
+ st["TransportState"] = mpdstatusToTransportState(mpds.state);
+ st["Repeat"] = SoapHelp::i2s(mpds.rept);
+ st["Shuffle"] = SoapHelp::i2s(mpds.random);
+ st["Id"] = mpds.songid == -1 ? "0" : SoapHelp::i2s(mpds.songid);
+ st["TracksMax"] = SoapHelp::i2s(tracksmax);
+ st["ProtocolInfo"] = Protocolinfo::the()->gettext();
+ makeIdArray(st["IdArray"]);
+ } else {
+ st = m_upnpstate;
+ }
return true;
}
@@ -276,17 +308,20 @@
void OHPlaylist::setActive(bool onoff)
{
- m_active = onoff;
- if (m_active) {
+ if (onoff) {
m_dev->m_mpdcli->clearQueue();
m_dev->m_mpdcli->restoreState(m_mpdsavedstate);
m_dev->m_mpdcli->consume(false);
m_dev->m_mpdcli->single(false);
refreshState();
maybeWakeUp(true);
+ m_active = true;
} else {
+ m_mpdqvers = -1;
+ makestate(m_upnpstate);
m_dev->m_mpdcli->saveState(m_mpdsavedstate);
iStop();
+ m_active = false;
}
}
@@ -325,6 +360,10 @@
int OHPlaylist::next(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::next: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::next" << endl);
bool ok = m_dev->m_mpdcli->next();
maybeWakeUp(ok);
@@ -333,6 +372,10 @@
int OHPlaylist::previous(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::previous: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::previous" << endl);
bool ok = m_dev->m_mpdcli->previous();
maybeWakeUp(ok);
@@ -341,6 +384,10 @@
int OHPlaylist::setRepeat(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::setRepeat: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::setRepeat" << endl);
bool onoff;
bool ok = sc.get("Value", &onoff);
@@ -353,6 +400,10 @@
int OHPlaylist::repeat(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::repeat: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::repeat" << endl);
const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
data.addarg("Value", mpds.rept? "1" : "0");
@@ -361,6 +412,10 @@
int OHPlaylist::setShuffle(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::setShuffle: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::setShuffle" << endl);
bool onoff;
bool ok = sc.get("Value", &onoff);
@@ -375,6 +430,10 @@
int OHPlaylist::shuffle(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::shuffle: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::shuffle" << endl);
const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
data.addarg("Value", mpds.random ? "1" : "0");
@@ -383,6 +442,10 @@
int OHPlaylist::seekSecondAbsolute(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::seekSecond: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::seekSecondAbsolute" << endl);
int seconds;
bool ok = sc.get("Value", &seconds);
@@ -395,6 +458,10 @@
int OHPlaylist::seekSecondRelative(const SoapIncoming& sc, SoapOutgoing& data)
{
+ if (!m_active) {
+ LOGERR("OHPlaylist::seekSecond: not active\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
LOGDEB("OHPlaylist::seekSecondRelative" << endl);
int seconds;
bool ok = sc.get("Value", &seconds);
@@ -435,22 +502,24 @@
// Skip to track specified by Id
int OHPlaylist::seekId(const SoapIncoming& sc, SoapOutgoing& data)
{
+ int id;
+ if (!sc.get("Value", &id)) {
+ LOGERR("OHPlaylist::seekId: no Id\n");
+ return UPNP_E_INVALID_PARAM;
+ }
LOGDEB("OHPlaylist::seekId" << endl);
if (!m_active) {
// If I'm not active, the ids in the playlist are those of
- // another service (e.g. radio). If I activate myself and
- // restore the playlist, the mpd ids are going to be different
- // from what the caller may have in store. This just can't
- // work as long as we use mpd ids directly.
- LOGERR("OHPlaylist::seekId: not active" << endl);
- return UPNP_E_INTERNAL_ERROR;
- }
- int id;
- bool ok = sc.get("Value", &id);
- if (ok) {
- ok = m_dev->m_mpdcli->playId(id);
- maybeWakeUp(ok);
- }
+ // another service (e.g. radio). After activating myself and
+ // restoring the playlist, the input id needs to be mapped.
+ m_dev->m_ohpr->iSetSourceIndexByName("Playlist");
+ id = idFromOldId(id);
+ if (id < 0) {
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ }
+ bool ok = m_dev->m_mpdcli->playId(id);
+ maybeWakeUp(ok);
return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
}
@@ -476,11 +545,11 @@
// Return current Id
int OHPlaylist::id(const SoapIncoming& sc, SoapOutgoing& data)
{
- LOGDEB("OHPlaylist::id" << endl);
if (!m_active) {
LOGERR("OHPlaylist::id: not active" << endl);
return UPNP_E_INTERNAL_ERROR;
}
+ LOGDEB("OHPlaylist::id" << endl);
const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
data.addarg("Value", mpds.songid == -1 ? "0" : SoapHelp::i2s(mpds.songid));
@@ -501,21 +570,21 @@
// Returns a 800 fault code if the given id is not in the playlist.
int OHPlaylist::ohread(const SoapIncoming& sc, SoapOutgoing& data)
{
- if (!m_active) {
- // See comment in seekId()
- LOGERR("OHPlaylist::read: not active" << endl);
- return UPNP_E_INTERNAL_ERROR;
- }
int id;
bool ok = sc.get("Id", &id);
+ if (!ok) {
+ LOGERR("OHPlaylist::ohread: no Id in params\n");
+ return UPNP_E_INVALID_PARAM;
+ }
LOGDEB("OHPlaylist::ohread id " << id << endl);
UpSong song;
- if (ok) {
- ok = m_dev->m_mpdcli->statSong(song, id, true);
- }
- if (ok) {
+ string metadata;
+ if (m_active) {
+ if (!m_dev->m_mpdcli->statSong(song, id, true)) {
+ LOGERR("OHPlaylist::ohread: statsong failed for " << id << endl);
+ return UPNP_E_INTERNAL_ERROR;
+ }
auto cached = m_metacache.find(song.uri);
- string metadata;
if (cached != m_metacache.end()) {
metadata = cached->second;
} else {
@@ -523,10 +592,22 @@
m_metacache[song.uri] = metadata;
m_cachedirty = true;
}
- data.addarg("Uri", song.uri);
- data.addarg("Metadata", metadata);
- }
- return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+ } else {
+ LOGDEB("OHPlaylist::read: not active: using saved queue\n");
+ for (const auto& entry : m_mpdsavedstate.queue) {
+ if (entry.mpdid == id) {
+ song = entry;
+ metadata = didlmake(song);
+ }
+ }
+ if (metadata.empty()) {
+ LOGDEB("OHPlaylist: id " << id << " not found\n");
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ }
+ data.addarg("Uri", SoapHelp::xmlQuote(song.uri));
+ data.addarg("Metadata", metadata);
+ return UPNP_E_SUCCESS;
}
// Given a space separated list of track Id's, report their associated
@@ -543,11 +624,6 @@
// Any ids not in the playlist are ignored.
int OHPlaylist::readList(const SoapIncoming& sc, SoapOutgoing& data)
{
- if (!m_active) {
- // See comment in seekId()
- LOGERR("OHPlaylist::readList: not active" << endl);
- return UPNP_E_INTERNAL_ERROR;
- }
string sids;
bool ok = sc.get("IdList", &sids);
LOGDEB("OHPlaylist::readList: [" << sids << "]" << endl);
@@ -562,24 +638,38 @@
LOGDEB("OHPlaylist::readlist: request for id -1" << endl);
continue;
}
+ string metadata;
UpSong song;
- if (!m_dev->m_mpdcli->statSong(song, id, true)) {
- LOGDEB("OHPlaylist::readList:stat failed for " << id << endl);
- continue;
- }
- auto mit = m_metacache.find(song.uri);
- string metadata;
- if (mit != m_metacache.end()) {
- //LOGDEB("OHPlaylist::readList: meta for id " << id << " uri "
- // << song.uri << " found in cache " << endl);
- metadata = SoapHelp::xmlQuote(mit->second);
+ if (m_active) {
+ if (!m_dev->m_mpdcli->statSong(song, id, true)) {
+ LOGDEB("OHPlaylist::readList:stat failed for " << id <<endl);
+ continue;
+ }
+ auto mit = m_metacache.find(song.uri);
+ if (mit != m_metacache.end()) {
+ LOGDEB1("OHPlaylist::readList: meta for id " << id << " uri "
+ << song.uri << " found in cache " << endl);
+ metadata = SoapHelp::xmlQuote(mit->second);
+ } else {
+ LOGDEB("OHPlaylist::readList: meta for id " << id << " uri "
+ << song.uri << " not found " << endl);
+ metadata = didlmake(song);
+ m_metacache[song.uri] = metadata;
+ m_cachedirty = true;
+ metadata = SoapHelp::xmlQuote(metadata);
+ }
} else {
- //LOGDEB("OHPlaylist::readList: meta for id " << id << " uri "
- // << song.uri << " not found " << endl);
- metadata = didlmake(song);
- m_metacache[song.uri] = metadata;
- m_cachedirty = true;
- metadata = SoapHelp::xmlQuote(metadata);
+ LOGDEB("OHPlaylist::readList: not active: using saved queue\n");
+ for (const auto& entry : m_mpdsavedstate.queue) {
+ if (entry.mpdid == id) {
+ song = entry;
+ metadata = didlmake(song);
+ }
+ }
+ if (metadata.empty()) {
+ LOGDEB("OHPlaylist: id " << id << " not found\n");
+ continue;
+ }
}
out += "<Entry><Id>";
out += SoapHelp::xmlQuote(it->c_str());
@@ -590,7 +680,7 @@
out += "</Metadata></Entry>";
}
out += "</TrackList>";
- //LOGDEB1("OHPlaylist::readList: out: [" << out << "]" << endl);
+ LOGDEB1("OHPlaylist::readList: out: [" << out << "]" << endl);
data.addarg("TrackList", out);
}
return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
@@ -642,15 +732,9 @@
}
if (!m_active) {
- // See comment in seekId()
- // It's not clear if special-casing afterId == 0 is a good
- // idea because it makes the device appear even more
- // unpredictable. Otoh, it allows Bubble (basic, not DS) to
- // switch to playlist by just adding tracks.
- if (afterid == 0 && m_dev->m_ohpr) {
- m_dev->m_ohpr->iSetSourceIndexByName("Playlist");
- } else {
- LOGERR("OHPlaylist::insert: not active" << endl);
+ m_dev->m_ohpr->iSetSourceIndexByName("Playlist");
+ afterid = idFromOldId(afterid);
+ if (afterid < 0) {
return UPNP_E_INTERNAL_ERROR;
}
}
@@ -674,6 +758,7 @@
LOGDEB1("OHPlaylist::insertUri: " << uri << endl);
if (!m_active) {
LOGERR("OHPlaylist::insertUri: not active" << endl);
+ m_dev->m_ohpr->iSetSourceIndexByName("Playlist");
return false;
}
@@ -698,25 +783,28 @@
int OHPlaylist::deleteId(const SoapIncoming& sc, SoapOutgoing& data)
{
+ int id;
+ if (!sc.get("Value", &id)) {
+ LOGERR("OHPlaylist::deleteId: no Id param\n");
+ return UPNP_E_INVALID_PARAM;
+ }
LOGDEB("OHPlaylist::deleteId" << endl);
if (!m_active) {
- // See comment in seekId()
- LOGERR("OHPlaylist::deleteId: not active" << endl);
- return UPNP_E_INTERNAL_ERROR;
- }
- int id;
- bool ok = sc.get("Value", &id);
- if (ok) {
- const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
- if (mpds.songid == id) {
- // MPD skips to the next track if the current one is removed,
- // but I think it's better to stop in this case
- m_dev->m_mpdcli->stop();
- }
- ok = m_dev->m_mpdcli->deleteId(id);
- m_mpdqvers = -1;
- maybeWakeUp(ok);
- }
+ m_dev->m_ohpr->iSetSourceIndexByName("Playlist");
+ id = idFromOldId(id);
+ if (id < 0) {
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ }
+ const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
+ if (mpds.songid == id) {
+ // MPD skips to the next track if the current one is removed,
+ // but I think it's better to stop in this case
+ m_dev->m_mpdcli->stop();
+ }
+ bool ok = m_dev->m_mpdcli->deleteId(id);
+ m_mpdqvers = -1;
+ maybeWakeUp(ok);
return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
}
@@ -739,16 +827,31 @@
return UPNP_E_SUCCESS;
}
+
+bool OHPlaylist::iidArray(string& idarray, int *token)
+{
+ LOGDEB("OHPlaylist::idArray (internal)" << endl);
+ unordered_map<string, string> st;
+ makestate(st);
+ idarray = st["IdArray"];
+ if (token) {
+ if (m_active) {
+ const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
+ LOGDEB("OHPlaylist::idArray: qvers " << mpds.qvers << endl);
+ *token = mpds.qvers;
+ } else {
+ *token = 0;
+ }
+ }
+ return true;
+}
+
+
// Returns current list of id as array of big endian 32bits integers,
// base-64-encoded.
int OHPlaylist::idArray(const SoapIncoming& sc, SoapOutgoing& data)
{
LOGDEB("OHPlaylist::idArray" << endl);
- if (!m_active) {
- // See comment in seekId()
- LOGERR("OHPlaylist::idArray: not active" << endl);
- return UPNP_E_INTERNAL_ERROR;
- }
string idarray;
int token;
if (iidArray(idarray, &token)) {
@@ -759,22 +862,10 @@
return UPNP_E_INTERNAL_ERROR;
}
-bool OHPlaylist::iidArray(string& idarray, int *token)
-{
- LOGDEB("OHPlaylist::idArray (internal)" << endl);
- if (makeIdArray(idarray)) {
- const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
- LOGDEB("OHPlaylist::idArray: qvers " << mpds.qvers << endl);
- if (token)
- *token = mpds.qvers;
- return true;
- }
- return false;
-}
bool OHPlaylist::urlMap(unordered_map<int, string>& umap)
{
- //LOGDEB1("OHPlaylist::urlMap\n");
+ LOGDEB1("OHPlaylist::urlMap\n");
string sarray;
if (iidArray(sarray, 0)) {
vector<int> ids;