Switch to unified view

a/src/ohradio.cxx b/src/ohradio.cxx
...
...
21
21
22
#include <upnp/upnp.h>
22
#include <upnp/upnp.h>
23
23
24
#include <functional>
24
#include <functional>
25
#include <iostream>
25
#include <iostream>
26
#include <sstream>
26
#include <string>
27
#include <string>
27
#include <utility>
28
#include <utility>
28
#include <vector>
29
#include <vector>
30
#include <json/json.h>
29
31
30
#include "libupnpp/base64.hxx"
32
#include "libupnpp/base64.hxx"
31
#include "libupnpp/log.hxx"
33
#include "libupnpp/log.hxx"
32
#include "libupnpp/soaphelp.hxx"
34
#include "libupnpp/soaphelp.hxx"
33
#include "libupnpp/upnpavutils.hxx"
35
#include "libupnpp/upnpavutils.hxx"
...
...
48
50
49
static const string sTpProduct("urn:av-openhome-org:service:Radio:1");
51
static const string sTpProduct("urn:av-openhome-org:service:Radio:1");
50
static const string sIdProduct("urn:av-openhome-org:serviceId:Radio");
52
static const string sIdProduct("urn:av-openhome-org:serviceId:Radio");
51
53
52
struct RadioMeta {
54
struct RadioMeta {
53
    RadioMeta(const string& t, const string& u, const string& au = string(),
55
    RadioMeta(const string& t, const string& u, const string& au,
54
              const string& as = string())
56
              const string& as, const string& ms)
55
        : title(t), uri(u), artUri(au), dynArtUri(au) {
57
        : title(t), uri(u), artUri(au), dynArtUri(au) {
56
        if (!as.empty()) {
58
        if (!as.empty()) {
57
            stringToStrings(as, artScript);
59
            stringToStrings(as, artScript);
58
        }
60
        }
61
        if (!ms.empty()) {
62
            stringToStrings(ms, metaScript);
63
        }
59
    }
64
    }
60
    string title;
65
    string title;
61
    string uri;
66
    string uri;
62
    string artUri;
67
    string artUri;
68
    // Script to retrieve current art
63
    vector<string> artScript;
69
    vector<string> artScript; 
70
    // Script to retrieve all metadata
71
    vector<string> metaScript; 
72
    // Time after which we should re-fire the metadata script
73
    time_t nextMetaScriptExecTime{0}; 
64
    string dynArtUri;
74
    string dynArtUri;
75
    string dynTitle;
76
    string dynArtist;
65
};
77
};
66
78
67
static vector<RadioMeta> o_radios;
79
static vector<RadioMeta> o_radios;
68
80
69
OHRadio::OHRadio(UpMpd *dev)
81
OHRadio::OHRadio(UpMpd *dev)
...
...
119
static void getRadiosFromConf(ConfSimple* conf)
131
static void getRadiosFromConf(ConfSimple* conf)
120
{
132
{
121
    vector<string> allsubk = conf->getSubKeys_unsorted();
133
    vector<string> allsubk = conf->getSubKeys_unsorted();
122
    for (auto it = allsubk.begin(); it != allsubk.end(); it++) {
134
    for (auto it = allsubk.begin(); it != allsubk.end(); it++) {
123
        if (it->find("radio ") == 0) {
135
        if (it->find("radio ") == 0) {
124
            string uri, artUri, artScript;
136
            string uri, artUri, artScript, metaScript;
125
            string title = it->substr(6);
137
            string title = it->substr(6);
126
            bool ok = conf->get("url", uri, *it);
138
            bool ok = conf->get("url", uri, *it);
127
            conf->get("artUrl", artUri, *it);
139
            conf->get("artUrl", artUri, *it);
128
            conf->get("artScript", artScript, *it);
140
            conf->get("artScript", artScript, *it);
129
            trimstring(artScript, " \t\n\r");
141
            trimstring(artScript, " \t\n\r");
142
            conf->get("metaScript", metaScript, *it);
143
            trimstring(metaScript, " \t\n\r");
130
            if (ok && !uri.empty()) {
144
            if (ok && !uri.empty()) {
131
                o_radios.push_back(RadioMeta(title, uri, artUri, artScript));
145
                o_radios.push_back(RadioMeta(title, uri, artUri, artScript,
146
                                             metaScript));
132
                LOGDEB1("OHRadio::readRadios:RADIO: [" << title << "] uri ["
147
                LOGDEB0("OHRadio::readRadios:RADIO: [" << title << "] uri ["
133
                        << uri << "] artUri [" << artUri << "]\n");
148
                        << uri << "] artUri [" << artUri << "]\n");
134
            }
149
            }
135
        }
150
        }
136
    }
151
    }
137
}
152
}
138
153
139
bool OHRadio::readRadios()
154
bool OHRadio::readRadios()
140
{
155
{
141
    // Id 0 means no selection
156
    // Id 0 means no selection
142
    o_radios.push_back(RadioMeta("Unknown radio", "", ""));
157
    o_radios.push_back(RadioMeta("Unknown radio", "", "", "", ""));
143
    
158
    
144
    std::unique_lock<std::mutex>(g_configlock);
159
    std::unique_lock<std::mutex>(g_configlock);
145
    getRadiosFromConf(g_config);
160
    getRadiosFromConf(g_config);
146
    // Also if radiolist is defined, get from there
161
    // Also if radiolist is defined, get from there
147
    string radiolistfn;
162
    string radiolistfn;
...
...
198
    MpdStatus mpds = m_dev->getMpdStatusNoUpdate();
213
    MpdStatus mpds = m_dev->getMpdStatusNoUpdate();
199
214
200
    st["ChannelsMax"] = SoapHelp::i2s(o_radios.size());
215
    st["ChannelsMax"] = SoapHelp::i2s(o_radios.size());
201
    st["Id"] = SoapHelp::i2s(m_id);
216
    st["Id"] = SoapHelp::i2s(m_id);
202
    makeIdArray(st["IdArray"]);
217
    makeIdArray(st["IdArray"]);
218
203
    if (m_active && m_id >= 0 && m_id < o_radios.size()) {
219
    if (m_active && m_id >= 0 && m_id < o_radios.size()) {
204
        if (mpds.currentsong.album.empty()) {
220
        if (mpds.currentsong.album.empty()) {
205
            mpds.currentsong.album = o_radios[m_id].title;
221
            mpds.currentsong.album = o_radios[m_id].title;
206
        }
222
        }
207
223
208
        // Some radios provide a url to the art for the current song. Possibly
209
        // execute script to retrieve it
210
        RadioMeta& radio = o_radios[m_id];
224
        RadioMeta& radio = o_radios[m_id];
211
        LOGDEB2("OHRadio::makestate: artScript: " << radio.artScript << endl);
225
226
        // Some radios do not insert icy metadata in the stream, but rather
227
        // provide a script to retrieve it.
228
        if (mpds.currentsong.title.empty() && mpds.currentsong.artist.empty()
229
            && radio.metaScript.size()) {
230
            if (time(0) > radio.nextMetaScriptExecTime) {
231
                string data;
232
                if (ExecCmd::backtick(radio.metaScript, data)) {
233
                    LOGDEB0("OHRadio::makestate: metaScript got: [" << data <<
234
                            "]\n");
235
                    // The data is in JSON format
236
                    try {
237
                        Json::Value decoded;
238
                        istringstream input(data);
239
                        input >> decoded;
240
                        radio.dynTitle = decoded.get("title", "").asString();
241
                        radio.dynArtist = decoded.get("artist", "").asString();
242
                        radio.dynArtUri = decoded.get("artUrl", "").asString();
243
                        radio.nextMetaScriptExecTime = time(0) +
244
                            decoded.get("reload", "10").asInt();
245
                    } catch (...) {
246
                        LOGERR("OHRadio::makestate: Json decode failed for [" <<
247
                               data << "]");
248
                        radio.nextMetaScriptExecTime = time(0) + 10;
249
                    }
250
                }
251
            }
252
            mpds.currentsong.title = radio.dynTitle;
253
            mpds.currentsong.artist = radio.dynArtist;
254
        }
255
256
        // Some radios provide a url to the art for the current song. 
257
        // Execute script to retrieve it if the current title+artist changed
212
        if (radio.artScript.size()) {
258
        if (radio.artScript.size()) {
213
            string nsong(mpds.currentsong.title + mpds.currentsong.artist);
259
            string nsong(mpds.currentsong.title + mpds.currentsong.artist);
214
            if (nsong.compare(m_currentsong)) {
260
            if (nsong.compare(m_currentsong)) {
215
                m_currentsong = nsong;
261
                m_currentsong = nsong;
216
                string uri;
262
                string uri;
217
                radio.dynArtUri.clear();
263
                radio.dynArtUri.clear();
218
                if (ExecCmd::backtick(radio.artScript, uri)) {
264
                if (ExecCmd::backtick(radio.artScript, uri)) {
219
                    trimstring(uri, " \t\r\n");
265
                    trimstring(uri, " \t\r\n");
220
                    LOGDEB("OHRadio::makestate: artScript got: [" << uri <<
266
                    LOGDEB0("OHRadio::makestate: artScript got: [" << uri <<
221
                           "]\n");
267
                            "]\n");
222
                    radio.dynArtUri = uri;
268
                    radio.dynArtUri = uri;
223
                }
269
                }
224
            }
270
            }
225
        }
271
        }
226
        mpds.currentsong.artUri = radio.dynArtUri.empty() ? radio.artUri :
272
        mpds.currentsong.artUri = radio.dynArtUri.empty() ? radio.artUri :
...
...
253
    if (m_id > o_radios.size() || o_radios[m_id].uri.empty()) {
299
    if (m_id > o_radios.size() || o_radios[m_id].uri.empty()) {
254
        LOGERR("OHRadio::setPlaying: called with bad id (" << m_id <<
300
        LOGERR("OHRadio::setPlaying: called with bad id (" << m_id <<
255
               ") or empty preset uri [" << o_radios[m_id].uri << "]\n");
301
               ") or empty preset uri [" << o_radios[m_id].uri << "]\n");
256
        return UPNP_E_INTERNAL_ERROR;
302
        return UPNP_E_INTERNAL_ERROR;
257
    }
303
    }
304
305
    RadioMeta& radio = o_radios[m_id];
306
    radio.nextMetaScriptExecTime = 0;
258
    
307
    
259
    string cmdpath = path_cat(g_datadir, "rdpl2stream");
308
    string cmdpath = path_cat(g_datadir, "rdpl2stream");
260
    cmdpath = path_cat(cmdpath, "fetchStream.py");
309
    cmdpath = path_cat(cmdpath, "fetchStream.py");
261
310
262
    // Execute the playlist parser
311
    // Execute the playlist parser
263
    ExecCmd cmd;
312
    ExecCmd cmd;
264
    vector<string> args;
313
    vector<string> args;
265
    args.push_back(o_radios[m_id].uri);
314
    args.push_back(radio.uri);
266
    LOGDEB("OHRadio::setPlaying: exec: " << cmdpath << " " << args[0] << endl);
315
    LOGDEB("OHRadio::setPlaying: exec: " << cmdpath << " " << args[0] << endl);
267
    if (cmd.startExec(cmdpath, args, false, true) < 0) {
316
    if (cmd.startExec(cmdpath, args, false, true) < 0) {
268
        LOGDEB("OHRadio::setPlaying: startExec failed for " <<
317
        LOGDEB("OHRadio::setPlaying: startExec failed for " <<
269
               cmdpath << " " << args[0] << endl);
318
               cmdpath << " " << args[0] << endl);
270
        return UPNP_E_INTERNAL_ERROR;
319
        return UPNP_E_INTERNAL_ERROR;
...
...
283
    }
332
    }
284
333
285
    // Send url to mpd
334
    // Send url to mpd
286
    m_dev->m_mpdcli->clearQueue();
335
    m_dev->m_mpdcli->clearQueue();
287
    UpSong song;
336
    UpSong song;
288
    song.album = o_radios[m_id].title;
337
    song.album = radio.title;
289
    song.uri = o_radios[m_id].uri;
338
    song.uri = radio.uri;
290
    if (m_dev->m_mpdcli->insert(audiourl, 0, song) < 0) {
339
    if (m_dev->m_mpdcli->insert(audiourl, 0, song) < 0) {
291
        LOGDEB("OHRadio::setPlaying: mpd insert failed\n");
340
        LOGDEB("OHRadio::setPlaying: mpd insert failed\n");
292
        return UPNP_E_INTERNAL_ERROR;
341
        return UPNP_E_INTERNAL_ERROR;
293
    }
342
    }
294
    m_dev->m_mpdcli->single(true);
343
    m_dev->m_mpdcli->single(true);