Restructured menuHTTP classes

This commit is contained in:
Frank Schmirler 2013-02-03 11:02:25 +01:00
parent 9bbb74b7fd
commit d3dd72072c
5 changed files with 203 additions and 165 deletions

View File

@ -1,6 +1,7 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- Restructured menuHTTP classes
- Added RSS format for HTTP menus - 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)

View File

@ -25,7 +25,7 @@ cConnectionHTTP::cConnectionHTTP(void):
m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType), m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType),
m_Channel(NULL), m_Channel(NULL),
m_Recording(NULL), m_Recording(NULL),
m_ChannelList(NULL) m_MenuList(NULL)
{ {
Dprintf("constructor hsRequest\n"); Dprintf("constructor hsRequest\n");
m_Apid[0] = m_Apid[1] = 0; m_Apid[0] = m_Apid[1] = 0;
@ -169,8 +169,8 @@ bool cConnectionHTTP::ProcessRequest(void)
// should never happen // should never happen
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo"); esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) { } else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_ChannelList) if (m_MenuList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str()); return Respond("%s", true, m_MenuList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
cDevice *device = NULL; cDevice *device = NULL;
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority))
@ -217,9 +217,9 @@ bool cConnectionHTTP::ProcessRequest(void)
return HttpResponse(404, true); return HttpResponse(404, true);
} }
} else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) { } else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_ChannelList) { if (m_MenuList) {
DeferClose(); DeferClose();
return Respond("%s", true, m_ChannelList->HttpHeader().c_str()); return Respond("%s", true, m_MenuList->HttpHeader().c_str());
} }
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) { if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
@ -363,13 +363,13 @@ void cConnectionHTTP::Flushed(void)
if (m_Status != hsBody) if (m_Status != hsBody)
return; return;
if (m_ChannelList) { if (m_MenuList) {
if (m_ChannelList->HasNext()) { if (m_MenuList->HasNext()) {
if (!Respond("%s", true, m_ChannelList->Next().c_str())) if (!Respond("%s", true, m_MenuList->Next().c_str()))
DeferClose(); DeferClose();
} }
else { else {
DELETENULL(m_ChannelList); DELETENULL(m_MenuList);
m_Status = hsFinished; m_Status = hsFinished;
DeferClose(); DeferClose();
} }
@ -387,28 +387,22 @@ void cConnectionHTTP::Flushed(void)
} }
} }
cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const cMenuList* cConnectionHTTP::MenuListFromString(const std::string& Path, const std::string& Filebase, const std::string& Fileext) const
{ {
std::string groupTarget; std::string groupTarget;
cChannelIterator *iterator = NULL; cItemIterator *iterator = NULL;
const static std::string GROUP("group"); const static std::string GROUP("group");
if (Filebase.compare("tree") == 0) { if (Filebase.compare("tree") == 0) {
const cChannel* c = NULL;
tStrStrMap::const_iterator it = m_Params.find(GROUP); tStrStrMap::const_iterator it = m_Params.find(GROUP);
if (it != m_Params.end()) iterator = new cListTree(it == m_Params.end() ? NULL : it->second.c_str());
c = cChannelList::GetGroup(atoi(it->second.c_str()));
iterator = new cListTree(c);
groupTarget = Filebase + Fileext; groupTarget = Filebase + Fileext;
} else if (Filebase.compare("groups") == 0) { } else if (Filebase.compare("groups") == 0) {
iterator = new cListGroups(); iterator = new cListGroups();
groupTarget = (std::string) "group" + Fileext; groupTarget = (std::string) "group" + Fileext;
} else if (Filebase.compare("group") == 0) { } else if (Filebase.compare("group") == 0) {
const cChannel* c = NULL;
tStrStrMap::const_iterator it = m_Params.find(GROUP); tStrStrMap::const_iterator it = m_Params.find(GROUP);
if (it != m_Params.end()) iterator = new cListGroup(it == m_Params.end() ? NULL : it->second.c_str());
c = cChannelList::GetGroup(atoi(it->second.c_str()));
iterator = new cListGroup(c);
} else if (Filebase.compare("channels") == 0) { } else if (Filebase.compare("channels") == 0) {
iterator = new cListChannels(); iterator = new cListChannels();
} else if (Filebase.compare("all") == 0 || } else if (Filebase.compare("all") == 0 ||
@ -436,16 +430,16 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
self += '?' + it->second; self += '?' + it->second;
rss += '?' + it->second; rss += '?' + it->second;
} }
return new cHtmlChannelList(iterator, m_StreamType, self.c_str(), rss.c_str(), groupTarget.c_str()); return new cHtmlMenuList(iterator, m_StreamType, self.c_str(), rss.c_str(), groupTarget.c_str());
} else if (Fileext.compare(".m3u") == 0) { } else if (Fileext.compare(".m3u") == 0) {
return new cM3uChannelList(iterator, base.c_str()); return new cM3uMenuList(iterator, base.c_str());
} else if (Fileext.compare(".rss") == 0) { } else if (Fileext.compare(".rss") == 0) {
std::string html = Filebase + ".html"; std::string html = Filebase + ".html";
tStrStrMap::const_iterator it = Headers().find("QUERY_STRING"); tStrStrMap::const_iterator it = Headers().find("QUERY_STRING");
if (it != Headers().end() && !it->second.empty()) { if (it != Headers().end() && !it->second.empty()) {
html += '?' + it->second; html += '?' + it->second;
} }
return new cRssChannelList(iterator, base.c_str(), html.c_str()); return new cRssMenuList(iterator, base.c_str(), html.c_str());
} else { } else {
delete iterator; delete iterator;
} }
@ -520,7 +514,7 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str()); Dprintf("before channelfromstring: type(%s) filespec(%s) fileext(%s)\n", type.c_str(), filespec.c_str(), fileext.c_str());
if ((m_ChannelList = ChannelListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) { if ((m_MenuList = MenuListFromString(PathInfo.substr(1, file_pos), filespec.c_str(), fileext.c_str())) != NULL) {
Dprintf("Channel list requested\n"); Dprintf("Channel list requested\n");
return true; return true;
} else if ((m_Recording = RecordingFromString(filespec.c_str(), fileext.c_str())) != NULL) { } else if ((m_Recording = RecordingFromString(filespec.c_str(), fileext.c_str())) != NULL) {

View File

@ -13,7 +13,7 @@
#include <tools/select.h> #include <tools/select.h>
class cChannel; class cChannel;
class cChannelList; class cMenuList;
class cConnectionHTTP: public cServerConnection { class cConnectionHTTP: public cServerConnection {
private: private:
@ -36,9 +36,9 @@ private:
// job: replay // job: replay
cRecording *m_Recording; cRecording *m_Recording;
// job: listing // job: listing
cChannelList *m_ChannelList; cMenuList *m_MenuList;
cChannelList* ChannelListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const; cMenuList* MenuListFromString(const std::string &PathInfo, const std::string &Filebase, const std::string &Fileext) const;
cRecording* RecordingFromString(const char* FileBase, const char* FileExt) const; cRecording* RecordingFromString(const char* FileBase, const char* FileExt) const;
bool ProcessURI(const std::string &PathInfo); bool ProcessURI(const std::string &PathInfo);

View File

@ -2,16 +2,60 @@
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
//**************************** cChannelIterator ************** //**************************** cChannelIterator **************
cChannelIterator::cChannelIterator(const cChannel *First): channel(First) cChannelIterator::cChannelIterator(const cChannel *First)
{}
const cChannel* cChannelIterator::Next()
{ {
const cChannel *current = channel; first = First;
channel = NextChannel(channel); current = NULL;
}
bool cChannelIterator::Next()
{
if (first)
{
current = first;
first = NULL;
}
else
current = NextChannel(current);
return current; return current;
} }
const cString cChannelIterator::ItemId() const
{
if (current)
{
if (current->GroupSep())
{
int index = 0;
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
{
if (Channels.Get(curr) == current)
return itoa(index);
index++;
}
}
else
{
return itoa(current->Number());
}
}
return cString("-1");
}
const cChannel* cChannelIterator::GetGroup(const char* GroupId)
{
int group = -1;
if (GroupId)
{
int Index = atoi(GroupId);
group = Channels.GetNextGroup(-1);
while (Index-- && group >= 0)
group = Channels.GetNextGroup(group);
}
return group >= 0 ? Channels.Get(group) : NULL;
}
//**************************** cListAll ************** //**************************** cListAll **************
cListAll::cListAll(): cChannelIterator(Channels.First()) cListAll::cListAll(): cChannelIterator(Channels.First())
{} {}
@ -46,7 +90,7 @@ const cChannel* cListGroups::NextChannel(const cChannel *Channel)
} }
// //
// ********************* cListGroup **************** // ********************* cListGroup ****************
cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group)) cListGroup::cListGroup(const char *GroupId): cChannelIterator(GetNextChannelInGroup(GetGroup(GroupId)))
{} {}
const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel) const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
@ -62,9 +106,9 @@ const cChannel* cListGroup::NextChannel(const cChannel *Channel)
} }
// //
// ********************* cListTree **************** // ********************* cListTree ****************
cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1))) cListTree::cListTree(const char *SelectedGroupId): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1)))
{ {
selectedGroup = SelectedGroup; selectedGroup = GetGroup(SelectedGroupId);
currentGroup = Channels.Get(Channels.GetNextGroup(-1)); currentGroup = Channels.Get(Channels.GetNextGroup(-1));
} }
@ -86,43 +130,23 @@ const cChannel* cListTree::NextChannel(const cChannel *Channel)
return Channel; return Channel;
} }
// ******************** cChannelList ****************** // ******************** cMenuList ******************
cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator) cMenuList::cMenuList(cItemIterator *Iterator) : iterator(Iterator)
{} {}
cChannelList::~cChannelList() cMenuList::~cMenuList()
{ {
delete iterator; delete iterator;
} }
int cChannelList::GetGroupIndex(const cChannel *Group) // ******************** cHtmlMenuList ******************
{ const char* cHtmlMenuList::menu =
int index = 0;
for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
{
if (Channels.Get(curr) == Group)
return index;
index++;
}
return -1;
}
const cChannel* cChannelList::GetGroup(int Index)
{
int group = Channels.GetNextGroup(-1);
while (Index-- && group >= 0)
group = Channels.GetNextGroup(group);
return group >= 0 ? Channels.Get(group) : NULL;
}
// ******************** cHtmlChannelList ******************
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.rss\">RSS</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.rss\">RSS</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* cHtmlMenuList::css =
"<style type=\"text/css\">\n" "<style type=\"text/css\">\n"
"<!--\n" "<!--\n"
"a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n" "a:link, a:visited, a:hover, a:active, a:focus { color:#333399; }\n"
@ -138,7 +162,7 @@ const char* cHtmlChannelList::css =
"-->\n" "-->\n"
"</style>"; "</style>";
const char* cHtmlChannelList::js = const char* cHtmlMenuList::js =
"<script language=\"JavaScript\">\n" "<script language=\"JavaScript\">\n"
"<!--\n" "<!--\n"
@ -199,7 +223,7 @@ const char* cHtmlChannelList::js =
"</script>"; "</script>";
std::string cHtmlChannelList::StreamTypeMenu() std::string cHtmlMenuList::StreamTypeMenu()
{ {
std::string typeMenu; std::string typeMenu;
typeMenu += (streamType == stTS ? (std::string) "[TS] " : typeMenu += (streamType == stTS ? (std::string) "[TS] " :
@ -215,29 +239,29 @@ std::string cHtmlChannelList::StreamTypeMenu()
return typeMenu; return typeMenu;
} }
cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget): cChannelList(Iterator) cHtmlMenuList::cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget): cMenuList(Iterator)
{ {
streamType = StreamType; streamType = StreamType;
self = strdup(Self); self = strdup(Self);
rss = strdup(Rss); rss = strdup(Rss);
groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL; groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL;
htmlState = hsRoot; htmlState = hsRoot;
current = NULL; onItem = true;
} }
cHtmlChannelList::~cHtmlChannelList() cHtmlMenuList::~cHtmlMenuList()
{ {
free((void *) self); free((void *) self);
free((void *) rss); free((void *) rss);
free((void *) groupTarget); free((void *) groupTarget);
} }
bool cHtmlChannelList::HasNext() bool cHtmlMenuList::HasNext()
{ {
return htmlState != hsPageBottom; return htmlState != hsPageBottom;
} }
std::string cHtmlChannelList::Next() std::string cHtmlMenuList::Next()
{ {
switch (htmlState) switch (htmlState)
{ {
@ -254,39 +278,39 @@ std::string cHtmlChannelList::Next()
htmlState = hsPageTop; htmlState = hsPageTop;
break; break;
case hsPageTop: case hsPageTop:
current = NextChannel(); onItem = NextItem();
htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom; htmlState = onItem ? (IsGroup() ? hsGroupTop : hsPlainTop) : hsPageBottom;
break; break;
case hsPlainTop: case hsPlainTop:
htmlState = hsPlainItem; htmlState = hsPlainItem;
break; break;
case hsPlainItem: case hsPlainItem:
current = NextChannel(); onItem = NextItem();
htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom; htmlState = onItem && !IsGroup() ? hsPlainItem : hsPlainBottom;
break; break;
case hsPlainBottom: case hsPlainBottom:
htmlState = current ? hsGroupTop : hsPageBottom; htmlState = onItem ? hsGroupTop : hsPageBottom;
break; break;
case hsGroupTop: case hsGroupTop:
current = NextChannel(); onItem = NextItem();
htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom; htmlState = onItem && !IsGroup() ? hsItemsTop : hsGroupBottom;
break; break;
case hsItemsTop: case hsItemsTop:
htmlState = hsItem; htmlState = hsItem;
break; break;
case hsItem: case hsItem:
current = NextChannel(); onItem = NextItem();
htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom; htmlState = onItem && !IsGroup() ? hsItem : hsItemsBottom;
break; break;
case hsItemsBottom: case hsItemsBottom:
htmlState = hsGroupBottom; htmlState = hsGroupBottom;
break; break;
case hsGroupBottom: case hsGroupBottom:
htmlState = current ? hsGroupTop : hsPageBottom; htmlState = onItem ? hsGroupTop : hsPageBottom;
break; break;
case hsPageBottom: case hsPageBottom:
default: default:
esyslog("streamdev-server cHtmlChannelList: invalid call to Next()"); esyslog("streamdev-server cHtmlMenuList: invalid call to Next()");
break; break;
} }
switch (htmlState) switch (htmlState)
@ -311,36 +335,35 @@ std::string cHtmlChannelList::Next()
} }
} }
std::string cHtmlChannelList::HtmlHead() std::string cHtmlMenuList::HtmlHead()
{ {
return (std::string) "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"" + rss + "\"/>"; return (std::string) "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"" + rss + "\"/>";
} }
std::string cHtmlChannelList::PageTop() std::string cHtmlMenuList::PageTop()
{ {
return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>"; return (std::string) "<div class=\"menu\"><div>" + menu + "</div><div>" + StreamTypeMenu() + "</div></div>";
} }
std::string cHtmlChannelList::PageBottom() std::string cHtmlMenuList::PageBottom()
{ {
return (std::string) ""; return (std::string) "";
} }
std::string cHtmlChannelList::GroupTitle() std::string cHtmlMenuList::GroupTitle()
{ {
if (groupTarget) if (groupTarget)
{ {
return (std::string) "<a href=\"" + groupTarget + "?group=" + return (std::string) "<a href=\"" + groupTarget + "?group=" + (const char*) ItemId() + "\">" +
(const char*) itoa(cChannelList::GetGroupIndex(current)) + ItemTitle() + "</a>";
"\">" + current->Name() + "</a>";
} }
else else
{ {
return (std::string) current->Name(); return (std::string) ItemTitle();
} }
} }
std::string cHtmlChannelList::ItemText() std::string cHtmlMenuList::ItemText()
{ {
std::string line; std::string line;
std::string suffix; std::string suffix;
@ -352,58 +375,58 @@ std::string cHtmlChannelList::ItemText()
case stPES: suffix = (std::string) ".vdr"; break; case stPES: suffix = (std::string) ".vdr"; break;
default: suffix = ""; default: suffix = "";
} }
line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">"; line += (std::string) "<li value=\"" + (const char*) ItemId() + "\">";
line += (std::string) "<a href=\"" + (std::string) current->GetChannelID().ToString() + suffix + "\""; line += (std::string) "<a href=\"" + (const char*) ItemRessource() + suffix + "\"";
// for Network Media Tank // for Network Media Tank
line += (std::string) " vod "; line += (std::string) " vod ";
if (current->Number() < 1000) if (strlen(ItemId()) < 4)
line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\""; line += (std::string) " tvid=\"" + (const char*) ItemId() + "\"";
line += (std::string) ">" + current->Name() + "</a>"; line += (std::string) ">" + ItemTitle() + "</a>";
int count = 0; // TS always streams all PIDs
for (int i = 0; current->Apid(i) != 0; ++i, ++count) if (streamType != stTS)
;
for (int i = 0; current->Dpid(i) != 0; ++i, ++count)
;
if (count > 1)
{ {
int index = 1; int index = 1;
for (int i = 0; current->Apid(i) != 0; ++i, ++index) { const char* lang;
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() + std::string pids;
"+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + "</a>"; for (int i = 0; (lang = Alang(i)) != NULL; ++i, ++index) {
} pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
for (int i = 0; current->Dpid(i) != 0; ++i, ++index) { "+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + (const char*) lang + "</a>";
line += (std::string) " <a href=\"" + (std::string) current->GetChannelID().ToString() + }
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + "</a>"; for (int i = 0; (lang = Dlang(i)) != NULL; ++i, ++index) {
} pids += (std::string) " <a href=\"" + (const char*) ItemRessource() +
"+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + (const char*) lang + "</a>";
}
// always show audio PIDs for stES to select audio only
if (index > 2 || streamType == stES)
line += pids;
} }
line += "</li>"; line += "</li>";
return line; return line;
} }
// ******************** cM3uChannelList ****************** // ******************** cM3uMenuList ******************
cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base) cM3uMenuList::cM3uMenuList(cItemIterator *Iterator, const char* Base)
: cChannelList(Iterator), : cMenuList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8") m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{ {
base = strdup(Base); base = strdup(Base);
m3uState = msFirst; m3uState = msFirst;
} }
cM3uChannelList::~cM3uChannelList() cM3uMenuList::~cM3uMenuList()
{ {
free(base); free(base);
} }
bool cM3uChannelList::HasNext() bool cM3uMenuList::HasNext()
{ {
return m3uState != msLast; return m3uState != msLast;
} }
std::string cM3uChannelList::Next() std::string cM3uMenuList::Next()
{ {
if (m3uState == msFirst) if (m3uState == msFirst)
{ {
@ -411,32 +434,30 @@ std::string cM3uChannelList::Next()
return "#EXTM3U"; return "#EXTM3U";
} }
const cChannel *channel = NextChannel(); if (!NextItem())
if (!channel)
{ {
m3uState = msLast; m3uState = msLast;
return ""; return "";
} }
std::string name = (std::string) m_IConv.Convert(channel->Name()); std::string name = (std::string) m_IConv.Convert(ItemTitle());
if (channel->GroupSep()) if (IsGroup())
{ {
return (std::string) "#EXTINF:-1," + name + "\r\n" + return (std::string) "#EXTINF:-1," + name + "\r\n" +
base + "group.m3u?group=" + base + "group.m3u?group=" + (const char*) ItemId();
(const char*) itoa(cChannelList::GetGroupIndex(channel));
} }
else else
{ {
return (std::string) "#EXTINF:-1," + return (std::string) "#EXTINF:-1," +
(const char*) itoa(channel->Number()) + " " + name + "\r\n" + (const char*) ItemId() + " " + name + "\r\n" +
base + (std::string) channel->GetChannelID().ToString(); base + (const char*) ItemRessource();
} }
} }
// ******************** cRssChannelList ****************** // ******************** cRssMenuList ******************
cRssChannelList::cRssChannelList(cChannelIterator *Iterator, const char *Base, const char *Html) cRssMenuList::cRssMenuList(cItemIterator *Iterator, const char *Base, const char *Html)
: cChannelList(Iterator), : cMenuList(Iterator),
m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8") m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8")
{ {
base = strdup(Base); base = strdup(Base);
@ -444,18 +465,18 @@ cRssChannelList::cRssChannelList(cChannelIterator *Iterator, const char *Base, c
rssState = msFirst; rssState = msFirst;
} }
cRssChannelList::~cRssChannelList() cRssMenuList::~cRssMenuList()
{ {
free(base); free(base);
free(html); free(html);
} }
bool cRssChannelList::HasNext() bool cRssMenuList::HasNext()
{ {
return rssState != msLast; return rssState != msLast;
} }
std::string cRssChannelList::Next() std::string cRssMenuList::Next()
{ {
std::string type_ext; std::string type_ext;
@ -469,27 +490,26 @@ std::string cRssChannelList::Next()
; ;
} }
const cChannel *channel = NextChannel(); if (!NextItem())
if (!channel)
{ {
rssState = msLast; rssState = msLast;
return "\t</channel>\n</rss>\n"; return "\t</channel>\n</rss>\n";
} }
std::string name = (std::string) m_IConv.Convert(channel->Name()); std::string name = (std::string) m_IConv.Convert(ItemTitle());
if (channel->GroupSep()) if (IsGroup())
{ {
return (std::string) "\t\t<item>\n\t\t\t<title>" + return (std::string) "\t\t<item>\n\t\t\t<title>" +
name + "</title>\n\t\t\t<link>" + name + "</title>\n\t\t\t<link>" +
base + "group.rss?group=" + (const char*) itoa(cChannelList::GetGroupIndex(channel)) + "</link>\n\t\t</item>\n"; base + "group.rss?group=" + (const char*) ItemId() + "</link>\n\t\t</item>\n";
} }
else else
{ {
return (std::string) "\t\t<item>\n\t\t\t<title>" + return (std::string) "\t\t<item>\n\t\t\t<title>" +
(const char*) itoa(channel->Number()) + " " + name + "</title>\n\t\t\t<link>" + (const char*) ItemId() + " " + name + "</title>\n\t\t\t<link>" +
base + (std::string) channel->GetChannelID().ToString() + "</link>\n\t\t\t<enclosure url=\"" + base + (const char*) ItemRessource() + "</link>\n\t\t\t<enclosure url=\"" +
base + (std::string) channel->GetChannelID().ToString() + "\" type=\"video/mpeg\" />\n\t\t</item>\n"; base + (const char*) ItemRessource() + "\" type=\"video/mpeg\" />\n\t\t</item>\n";
} }
} }

View File

@ -6,16 +6,38 @@
class cChannel; class cChannel;
// ******************** cChannelIterator ****************** // ******************** cItemIterator ******************
class cChannelIterator class cItemIterator
{
public:
virtual bool Next() = 0;
virtual bool IsGroup() const = 0;
virtual const cString ItemId() const = 0;
virtual const char* ItemTitle() const = 0;
virtual const cString ItemRessource() const = 0;
virtual const char* Alang(int i) const = 0;
virtual const char* Dlang(int i) const = 0;
};
class cChannelIterator: public cItemIterator
{ {
private: private:
const cChannel *channel; const cChannel *first;
const cChannel *current;
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel) = 0; virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
static inline const cChannel* SkipFakeGroups(const cChannel *Channel); static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
// Helper which returns the group by its index
static const cChannel* GetGroup(const char* GroupId);
public: public:
const cChannel* Next();
virtual bool Next();
virtual bool IsGroup() const { return current && current->GroupSep(); }
virtual const cString ItemId() const;
virtual const char* ItemTitle() const { return current ? current->Name() : ""; }
virtual const cString ItemRessource() const { return (current ? current->GetChannelID() : tChannelID::InvalidID).ToString(); }
virtual const char* Alang(int i) const { return current && current->Apid(i) ? current->Alang(i) : NULL; }
virtual const char* Dlang(int i) const { return current && current->Dpid(i) ? current->Dlang(i) : NULL; }
cChannelIterator(const cChannel *First); cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {}; virtual ~cChannelIterator() {};
}; };
@ -54,7 +76,7 @@ class cListGroup: public cChannelIterator
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
cListGroup(const cChannel *Group); cListGroup(const char *GroupId);
virtual ~cListGroup() {}; virtual ~cListGroup() {};
}; };
@ -66,31 +88,32 @@ class cListTree: public cChannelIterator
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
cListTree(const cChannel *SelectedGroup); cListTree(const char *SelectedGroupId);
virtual ~cListTree() {}; virtual ~cListTree() {};
}; };
// ******************** cChannelList ****************** // ******************** cMenuList ******************
class cChannelList class cMenuList
{ {
private: private:
cChannelIterator *iterator; cItemIterator *iterator;
protected: protected:
const cChannel* NextChannel() { return iterator->Next(); } bool NextItem() { return iterator->Next(); }
bool IsGroup() { return iterator->IsGroup(); }
const cString ItemId() { return iterator->ItemId(); }
const char* ItemTitle() { return iterator->ItemTitle(); }
const cString ItemRessource() { return iterator->ItemRessource(); }
const char* Alang(int i) { return iterator->Alang(i); }
const char* Dlang(int i) { return iterator->Dlang(i); }
public: public:
// Helper which returns the group index
static int GetGroupIndex(const cChannel* Group);
// Helper which returns the group by its index
static const cChannel* GetGroup(int Index);
virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; }; virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; };
virtual bool HasNext() = 0; virtual bool HasNext() = 0;
virtual std::string Next() = 0; virtual std::string Next() = 0;
cChannelList(cChannelIterator *Iterator); cMenuList(cItemIterator *Iterator);
virtual ~cChannelList(); virtual ~cMenuList();
}; };
class cHtmlChannelList: public cChannelList class cHtmlMenuList: public cMenuList
{ {
private: private:
static const char* menu; static const char* menu;
@ -104,7 +127,7 @@ class cHtmlChannelList: public cChannelList
hsItemsTop, hsItem, hsItemsBottom hsItemsTop, hsItem, hsItemsBottom
}; };
eHtmlState htmlState; eHtmlState htmlState;
const cChannel *current; bool onItem;
eStreamType streamType; eStreamType streamType;
const char* self; const char* self;
const char* rss; const char* rss;
@ -118,18 +141,18 @@ class cHtmlChannelList: public cChannelList
std::string PageBottom(); std::string PageBottom();
public: public:
virtual std::string HttpHeader() { virtual std::string HttpHeader() {
return cChannelList::HttpHeader() return cMenuList::HttpHeader()
+ "Content-type: text/html; charset=" + "Content-type: text/html; charset="
+ (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8") + (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8")
+ "\r\n"; + "\r\n";
} }
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget); cHtmlMenuList(cItemIterator *Iterator, eStreamType StreamType, const char *Self, const char *Rss, const char *GroupTarget);
virtual ~cHtmlChannelList(); virtual ~cHtmlMenuList();
}; };
class cM3uChannelList: public cChannelList class cM3uMenuList: public cMenuList
{ {
private: private:
char *base; char *base;
@ -137,14 +160,14 @@ class cM3uChannelList: public cChannelList
eM3uState m3uState; eM3uState m3uState;
cCharSetConv m_IConv; cCharSetConv m_IConv;
public: public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; }; virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; };
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cM3uChannelList(cChannelIterator *Iterator, const char* Base); cM3uMenuList(cItemIterator *Iterator, const char* Base);
virtual ~cM3uChannelList(); virtual ~cM3uMenuList();
}; };
class cRssChannelList: public cChannelList class cRssMenuList: public cMenuList
{ {
private: private:
char *base; char *base;
@ -153,12 +176,12 @@ class cRssChannelList: public cChannelList
eRssState rssState; eRssState rssState;
cCharSetConv m_IConv; cCharSetConv m_IConv;
public: public:
virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: application/rss+xml\r\n"; }; virtual std::string HttpHeader() { return cMenuList::HttpHeader() + "Content-type: application/rss+xml\r\n"; };
virtual bool HasNext(); virtual bool HasNext();
virtual std::string Next(); virtual std::string Next();
cRssChannelList(cChannelIterator *Iterator, const char *Base, const char *Html); cRssMenuList(cItemIterator *Iterator, const char *Base, const char *Html);
virtual ~cRssChannelList(); virtual ~cRssMenuList();
}; };
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group) inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)