Added RSS format for HTTP menus

This commit is contained in:
Frank Schmirler 2013-02-02 23:28:55 +01:00
parent 176df8341d
commit 9bbb74b7fd
4 changed files with 109 additions and 16 deletions

View File

@ -1,6 +1,7 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- Added RSS format for HTTP menus
- Recordings can now also be selected by struct stat "st_dev:st_ino.rec" - Recordings can now also be selected by struct stat "st_dev:st_ino.rec"
- Implemented multi-device support for streamdev client (suggested by johns) - Implemented multi-device support for streamdev client (suggested by johns)
- Basic support for HTTP streaming of recordings - Basic support for HTTP streaming of recordings

View File

@ -417,13 +417,7 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
} }
if (iterator) { if (iterator) {
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) { // assemble base url: http://host/path/
std::string self = Filebase + Fileext;
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty())
self += '?' + it->second;
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) {
std::string base; std::string base;
const static std::string HOST("HTTP_HOST"); const static std::string HOST("HTTP_HOST");
tStrStrMap::const_iterator it = Headers().find(HOST); tStrStrMap::const_iterator it = Headers().find(HOST);
@ -433,7 +427,25 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
base = (std::string) "http://" + LocalIp() + ":" + base = (std::string) "http://" + LocalIp() + ":" +
(const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/"; (const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
base += Path; base += Path;
if (Filebase.empty() || Fileext.compare(".htm") == 0 || Fileext.compare(".html") == 0) {
std::string self = Filebase + Fileext;
std::string rss = Filebase + ".rss";
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty()) {
self += '?' + it->second;
rss += '?' + it->second;
}
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), rss.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) {
return new cM3uChannelList(iterator, base.c_str()); return new cM3uChannelList(iterator, base.c_str());
} else if (Fileext.compare(".rss") == 0) {
std::string html = Filebase + ".html";
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty()) {
html += '?' + it->second;
}
return new cRssChannelList(iterator, base.c_str(), html.c_str());
} else { } else {
delete iterator; delete iterator;
} }

View File

@ -119,8 +119,8 @@ const cChannel* cChannelList::GetGroup(int Index)
const char* cHtmlChannelList::menu = const char* cHtmlChannelList::menu =
"[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] " "[<a href=\"/\">Home</a> (<a href=\"all.html\" tvid=\"RED\">no script</a>)] "
"[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] " "[<a href=\"tree.html\" tvid=\"GREEN\">Tree View</a>] "
"[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a>)] " "[<a href=\"groups.html\" tvid=\"YELLOW\">Groups</a> (<a href=\"groups.m3u\">Playlist</a> | <a href=\"groups.rss\">RSS</a>)] "
"[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a>)] "; "[<a href=\"channels.html\" tvid=\"BLUE\">Channels</a> (<a href=\"channels.m3u\">Playlist</a> | <a href=\"channels.rss\">RSS</a>)] ";
const char* cHtmlChannelList::css = const char* cHtmlChannelList::css =
"<style type=\"text/css\">\n" "<style type=\"text/css\">\n"
@ -215,10 +215,11 @@ std::string cHtmlChannelList::StreamTypeMenu()
return typeMenu; return typeMenu;
} }
cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget): cChannelList(Iterator) cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget): cChannelList(Iterator)
{ {
streamType = StreamType; streamType = StreamType;
self = strdup(Self); self = strdup(Self);
rss = strdup(Rss);
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL; groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
htmlState = hsRoot; htmlState = hsRoot;
current = NULL; current = NULL;
@ -227,6 +228,7 @@ cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType Strea
cHtmlChannelList::~cHtmlChannelList() cHtmlChannelList::~cHtmlChannelList()
{ {
free((void *) self); free((void *) self);
free((void *) rss);
free((void *) groupTarget); free((void *) groupTarget);
} }
@ -311,7 +313,7 @@ std::string cHtmlChannelList::Next()
std::string cHtmlChannelList::HtmlHead() std::string cHtmlChannelList::HtmlHead()
{ {
return (std::string) ""; return (std::string) "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"" + rss + "\"/>";
} }
std::string cHtmlChannelList::PageTop() std::string cHtmlChannelList::PageTop()
@ -432,3 +434,63 @@ std::string cM3uChannelList::Next()
} }
} }
// ******************** cRssChannelList ******************
cRssChannelList::cRssChannelList(cChannelIterator *Iterator, const char *Base, const char *Html)
: cChannelList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{
base = strdup(Base);
html = strdup(Html);
rssState = msFirst;
}
cRssChannelList::~cRssChannelList()
{
free(base);
free(html);
}
bool cRssChannelList::HasNext()
{
return rssState != msLast;
}
std::string cRssChannelList::Next()
{
std::string type_ext;
if (rssState == msFirst)
{
rssState = msContinue;
return (std::string) "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\">\n\t<channel>\n"
"\t\t<title>VDR</title>\n"
"\t\t<link>" + base + html + "</link>\n"
"\t\t<description>VDR channel list</description>\n"
;
}
const cChannel *channel = NextChannel();
if (!channel)
{
rssState = msLast;
return "\t</channel>\n</rss>\n";
}
std::string name = (std::string) m_IConv.Convert(channel->Name());
if (channel->GroupSep())
{
return (std::string) "\t\t<item>\n\t\t\t<title>" +
name + "</title>\n\t\t\t<link>" +
base + "group.rss?group=" + (const char*) itoa(cChannelList::GetGroupIndex(channel)) + "</link>\n\t\t</item>\n";
}
else
{
return (std::string) "\t\t<item>\n\t\t\t<title>" +
(const char*) itoa(channel->Number()) + " " + name + "</title>\n\t\t\t<link>" +
base + (std::string) channel->GetChannelID().ToString() + "</link>\n\t\t\t<enclosure url=\"" +
base + (std::string) channel->GetChannelID().ToString() + "\" type=\"video/mpeg\" />\n\t\t</item>\n";
}
}

View File

@ -107,6 +107,7 @@ class cHtmlChannelList: public cChannelList
const cChannel *current; const cChannel *current;
eStreamType streamType; eStreamType streamType;
const char* self; const char* self;
const char* rss;
const char* groupTarget; const char* groupTarget;
std::string StreamTypeMenu(); std::string StreamTypeMenu();
@ -124,7 +125,7 @@ class cHtmlChannelList: public cChannelList
} }
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget); cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget);
virtual ~cHtmlChannelList(); virtual ~cHtmlChannelList();
}; };
@ -143,6 +144,23 @@ class cM3uChannelList: public cChannelList
virtual ~cM3uChannelList(); virtual ~cM3uChannelList();
}; };
class cRssChannelList: public cChannelList
{
private:
char *base;
char *html;
enum eRssState { msFirst, msContinue, msLast };
eRssState rssState;
cCharSetConv m_IConv;
public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: application/rss+xml\r\n"; };
virtual bool HasNext();
virtual std::string Next();
cRssChannelList(cChannelIterator *Iterator, const char *Base, const char *Html);
virtual ~cRssChannelList();
};
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group) inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
{ {
while (Group && Group->GroupSep() && !*Group->Name()) while (Group && Group->GroupSep() && !*Group->Name())