summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthlo <t.lohmar@gmx.de>2013-02-23 10:31:30 +0100
committerthlo <t.lohmar@gmx.de>2013-02-23 10:31:30 +0100
commit56ef7aaa40b5aff3bce5ace21215acb0be5ab92f (patch)
treeb2d30b11d6cdf7f0a8471da637dfb26b54fe9569
parent9bc23942e1bbac6672d3e00812235e483debd4cc (diff)
downloadvdr-plugin-smarttvweb-56ef7aaa40b5aff3bce5ace21215acb0be5ab92f.tar.gz
vdr-plugin-smarttvweb-56ef7aaa40b5aff3bce5ace21215acb0be5ab92f.tar.bz2
Support for multiple interfaces. Improved Channel Group Separator handling. Updated Web GUI to use the enclosure RSS element. Bugfixes.
-rwxr-xr-xvdr-smarttvweb/httpresource.c102
-rwxr-xr-xvdr-smarttvweb/httpresource.h7
-rwxr-xr-x[-rw-r--r--]vdr-smarttvweb/smarttvfactory.c40
-rw-r--r--vdr-smarttvweb/smarttvfactory.h5
-rw-r--r--vdr-smarttvweb/smarttvweb.conf15
-rwxr-xr-xvdr-smarttvweb/stvw_cfg.c38
-rwxr-xr-xvdr-smarttvweb/stvw_cfg.h24
-rwxr-xr-xvdr-smarttvweb/web/Data.js129
-rwxr-xr-xvdr-smarttvweb/web/Server.js244
9 files changed, 400 insertions, 204 deletions
diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c
index 47fce70..84e6b06 100755
--- a/vdr-smarttvweb/httpresource.c
+++ b/vdr-smarttvweb/httpresource.c
@@ -116,14 +116,14 @@ struct tIndexTs {
};
-
+/*
union tIndexRead {
struct tIndexTs in;
char buf[8];
};
+*/
-
-cHttpResource::cHttpResource(int f, int id,string addr, int port, SmartTvServer* factory): mFactory(factory), mLog(), mServerAddr(addr),
+cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(),
mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0),
mConnected(true), mConnState(WAITING), mContentType(NYD), mReadBuffer(),
mMethod(), mResponseMessagePos(0), mBlkData(NULL), mBlkPos(0), mBlkLen(0),
@@ -138,19 +138,19 @@ cHttpResource::cHttpResource(int f, int id,string addr, int port, SmartTvServer*
setNonBlocking();
mBlkData = new char[MAXLEN];
-#ifndef DEBUG
+ //#ifndef DEBUG
*(mLog->log())<< DEBUGPREFIX
<< " cHttpResource created" << endl;
-#endif
+ //#endif
}
cHttpResource::~cHttpResource() {
-#ifndef DEBUG
+ //#ifndef DEBUG
*(mLog->log())<< DEBUGPREFIX
<< " Destructor of cHttpResource called"
<< endl;
-#endif
+ //#endif
delete[] mBlkData;
if (mFile != NULL) {
*(mLog->log())<< DEBUGPREFIX
@@ -769,6 +769,9 @@ int cHttpResource::handlePost() {
if (mPath.compare("/log") == 0) {
*(mLog->log())<< mPayload
<< endl;
+
+ sendHeaders(200, "OK", NULL, NULL, -1, -1);
+ return OKAY;
}
if (mPath.compare("/getResume.xml") == 0) {
@@ -932,7 +935,7 @@ int cHttpResource::sendDir(struct stat *statbuf) {
}
-int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, time_t start, int dur, double fps, int is_pes, int is_new) {
+int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new) {
string hdr = "";
char f[400];
@@ -944,6 +947,11 @@ int cHttpResource::writeXmlItem(string name, string link, string programme, stri
hdr += "<guid>" + guid + "</guid>\n";
+ snprintf(f, sizeof(f), "%d", no);
+ hdr += "<number>";
+ hdr += f;
+ hdr += "</number>\n";
+
hdr += "<programme>" + programme +"</programme>\n";
hdr += "<description>" + desc + "</description>\n";
@@ -1446,6 +1454,9 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) {
*(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl;
#endif
+ string own_ip = getOwnIp(mFd);
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
+
vector<sFileEntry> entries;
if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) {
@@ -1464,10 +1475,11 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) {
for (uint i=0; i < entries.size(); i++) {
- snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort,
+ // snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort,
+ snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", own_ip.c_str(), mServerPort,
cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str());
if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-",
- entries[i].sStart, -1, -1, -1, -1) == ERROR)
+ -1, entries[i].sStart, -1, -1, -1, -1) == ERROR)
return ERROR;
}
@@ -1702,6 +1714,8 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) {
<< " generating /channels.xml"
<< DEBUGHDR << endl;
#endif
+ string own_ip = getOwnIp(mFd);
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
vector<sQueryAVP> avps;
parseQueryLine(&avps);
@@ -1711,7 +1725,7 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) {
string no_channels_str = "";
int no_channels = -1;
string group_sep = "";
-
+
if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){
if (mode == "nodesc") {
add_desc = false;
@@ -1748,27 +1762,21 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) {
for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
if (channel->GroupSep()) {
- group_sep = cUrlEncode::doXmlSaveEncode(channel->Name());
- /* *(mLog->log())<< DEBUGPREFIX
- << " group= " << group_sep
- << " group_sep[0]= " << group_sep[0] << endl;
- if (group_sep[0] == '@') {
- size_t pos = group_sep.find(' ', 1);
- if (pos != string::npos)
- group_sep = group_sep.substr( pos +1);
- else
- group_sep = "";
- *(mLog->log()) << DEBUGPREFIX
- << " group_sep= " << group_sep << endl;
+ if (mFactory->getConfig()->getGroupSep() != IGNORE) {
+ // if emtpyFolderDown, always.
+ // otherwise, only when not empty
+ if (!((strcmp(channel->Name(), "") == 0) && (mFactory->getConfig()->getGroupSep() == EMPTYIGNORE)))
+ group_sep = cUrlEncode::doXmlSaveEncode(channel->Name());
+
}
-*/
continue;
}
if (--count == 0) {
break;
}
- snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString());
+ // snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString());
+ snprintf(f, sizeof(f), "http://%s:3000/%s.ts", own_ip.c_str(), *(channel->GetChannelID()).ToString());
string link = f;
const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID());
@@ -1805,7 +1813,7 @@ int cHttpResource::sendChannelsXml (struct stat *statbuf) {
string c_name = (group_sep != "") ? (group_sep + "~" + cUrlEncode::doXmlSaveEncode(channel->Name()))
: cUrlEncode::doXmlSaveEncode(channel->Name());
// if (writeXmlItem(channel->Name(), link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration) == ERROR)
- if (writeXmlItem(c_name, link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration, -1, -1, -1) == ERROR)
+ if (writeXmlItem(c_name, link, title, desc, *(channel->GetChannelID()).ToString(), channel->Number(), start_time, duration, -1, -1, -1) == ERROR)
return ERROR;
}
@@ -1950,7 +1958,7 @@ int cHttpResource::deleteRecording() {
if (getQueryAttributeValue(&avps, "id", id) == ERROR){
*(mLog->log())<< DEBUGPREFIX
- << " ERROR: id not found"
+ << " ERROR: id not found in query."
<< DEBUGHDR << endl;
sendError(400, "Bad Request", NULL, "no id in query line");
return OKAY;
@@ -1959,6 +1967,9 @@ int cHttpResource::deleteRecording() {
cRecording* rec = Recordings.GetByName(mPath.c_str());
if (rec == NULL) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: Recording not found. Deletion failed: mPath= " << mPath
+ << endl;
sendError(404, "Not Found.", NULL, "Recording not found. Deletion failed!");
return OKAY;
}
@@ -1967,10 +1978,16 @@ int cHttpResource::deleteRecording() {
// Recordings.DelByName(mPath.c_str());
}
else {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: rec->Delete() returns false. mPath= " << mPath
+ << endl;
sendError(500, "Internal Server Error", NULL, "deletion failed!");
return OKAY;
}
+ *(mLog->log())<< DEBUGPREFIX
+ << " Deleted."
+ << endl;
sendHeaders(200, "OK", NULL, NULL, -1, -1);
return OKAY;
}
@@ -1986,6 +2003,9 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) {
mConnState = SERVING;
+ string own_ip = getOwnIp(mFd);
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
+
vector<sQueryAVP> avps;
parseQueryLine(&avps);
string model = "";
@@ -2098,12 +2118,14 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) {
<< endl;
*/
if (ti->HasFlags(tfRecording) ) {
+ /*
if (ti->Event() == NULL) {
*(mLog->log()) << DEBUGPREFIX
<< " WARNING: Active recording for " << ti->File()
<< " is skipped (No Event()" << endl;
continue;
}
+ */
/* *(mLog->log()) << DEBUGPREFIX
<< " Active Timer: " << ti->File()
<< " Start= " << ti->Event()->StartTime()
@@ -2132,10 +2154,12 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) {
hdr = "";
if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !has_4_hd ))
- snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort,
+ // snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort,
+ snprintf(f, sizeof(f), "http://%s:%d%s", own_ip.c_str(), mServerPort,
cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str());
else
- snprintf(f, sizeof(f), "http://%s:%d%s%s", mServerAddr.c_str(), mServerPort,
+ // snprintf(f, sizeof(f), "http://%s:%d%s%s", mServerAddr.c_str(), mServerPort,
+ snprintf(f, sizeof(f), "http://%s:%d%s%s", own_ip.c_str(), mServerPort,
cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str());
string link = f;
@@ -2164,7 +2188,8 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) {
}
if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc,
- cUrlEncode::doXmlSaveEncode(recording->FileName()).c_str(),
+ cUrlEncode::doXmlSaveEncode(recording->FileName()).c_str(),
+ -1,
recording->Start(), rec_dur, recording->FramesPerSecond(),
(recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR)
// Better Internal Server Error
@@ -2705,6 +2730,19 @@ string cHttpResource::getConnStateName() {
return state_string;
}
+
+string cHttpResource::getOwnIp(int fd) {
+ struct sockaddr_in sock;
+ socklen_t len_inet = sizeof(sock);
+ int ret = getsockname(fd, (struct sockaddr *)&sock, &len_inet);
+ if ( ret == -1 ) {
+ *(mLog->log()) << "Error: Cannot obtain own ip address" << endl;
+ return string("0.0.0.0");
+ // exit(1); /* Failed */
+ }
+ return string (inet_ntoa(sock.sin_addr));
+}
+
int cHttpResource::parseHttpRequestLine(string line) {
mMethod = line.substr(0, line.find_first_of(" "));
mRequest = line.substr(line.find_first_of(" ") +1, (line.find_last_of(" ") - line.find_first_of(" ") -1));
@@ -2753,10 +2791,10 @@ int cHttpResource::parseHttpHeaderLine (string line) {
}
if (hdr_name.compare("Content-Length") == 0) {
mReqContentLength = atoll(hdr_val.c_str());
-#ifndef DEBUG
+ //#ifndef DEBUG
*(mLog->log())<< " Content-Length: " << mReqContentLength
<< endl;
-#endif
+ //#endif
}
return 0;
}
diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h
index 81ecb34..8185026 100755
--- a/vdr-smarttvweb/httpresource.h
+++ b/vdr-smarttvweb/httpresource.h
@@ -74,7 +74,7 @@ class cResumeEntry;
class cHttpResource {
public:
- cHttpResource(int, int, string, int, SmartTvServer*);
+ cHttpResource(int, int, int, SmartTvServer*);
virtual ~cHttpResource();
int handleRead();
@@ -90,7 +90,6 @@ class cHttpResource {
SmartTvServer* mFactory;
Log* mLog;
- string mServerAddr;
int mServerPort;
int mFd;
int mReqId;
@@ -147,7 +146,6 @@ class cHttpResource {
int sendChannelsXml (struct stat *statbuf);
int sendResumeXml ();
int sendVdrStatusXml (struct stat *statbuf);
- // int sendResumeXml (struct stat *statbuf);
int sendEpgXml (struct stat *statbuf);
int sendMediaXml (struct stat *statbuf);
@@ -170,6 +168,7 @@ class cHttpResource {
// Helper Functions
const char *getMimeType(const char *name);
string getConnStateName();
+ string getOwnIp(int fd);
void checkRecording();
bool isTimeRequest(struct stat *statbuf);
int parseRangeHeaderValue(string);
@@ -182,6 +181,6 @@ class cHttpResource {
int getQueryAttributeValue(vector<sQueryAVP> *avps, string id, string &val);
int openFile(const char *name);
- int writeXmlItem(string title, string link, string programme, string desc, string guid, time_t start, int dur, double fps, int is_pes, int is_new);
+ int writeXmlItem(string title, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new);
};
#endif
diff --git a/vdr-smarttvweb/smarttvfactory.c b/vdr-smarttvweb/smarttvfactory.c
index b806021..d0196c9 100644..100755
--- a/vdr-smarttvweb/smarttvfactory.c
+++ b/vdr-smarttvweb/smarttvfactory.c
@@ -143,13 +143,6 @@ void SmartTvServer::loop() {
FD_SET(mServerFd, &mReadState);
maxfd = mServerFd;
- struct ifreq ifr;
-
- ifr.ifr_addr.sa_family = AF_INET;
- strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);
- ioctl(mServerFd, SIOCGIFADDR, &ifr);
- string own_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
-
*(mLog.log()) << "mServerFd= " << mServerFd << endl;
int handeled_fds = 0;
@@ -231,9 +224,10 @@ void SmartTvServer::loop() {
if (clientList.size() < (rfd+1)) {
clientList.resize(rfd+1, NULL); // Check.
}
- clientList[rfd] = new cHttpResource(rfd, req_id, own_ip, serverPort, this);
+ clientList[rfd] = new cHttpResource(rfd, req_id, serverPort, this);
+ // clientList[rfd] = new cHttpResource(rfd, req_id, mOwnIp, serverPort, this);
mActiveSessions ++;
-
+ *(mLog.log()) << " + mActiveSessions= " << mActiveSessions << endl;
}
else{
*(mLog.log()) << "Error accepting " << errno << endl;
@@ -241,8 +235,6 @@ void SmartTvServer::loop() {
}
// Check for data on already accepted connections
- // for (rfd = mServerFd + 1; rfd <= maxfd; ++rfd) {
- // for (rfd = 0; rfd <= maxfd; ++rfd) {
for (rfd = 0; rfd < clientList.size(); rfd++) {
if (clientList[rfd] == NULL)
continue;
@@ -264,6 +256,7 @@ void SmartTvServer::loop() {
delete clientList[rfd];
clientList[rfd] = NULL;
mActiveSessions--;
+ *(mLog.log()) << " - Check Read: mActiveSessions= " << mActiveSessions << endl;
FD_CLR(rfd, &mReadState); /* dead client */
FD_CLR(rfd, &mWriteState);
}
@@ -271,8 +264,6 @@ void SmartTvServer::loop() {
}
// Check for write
- // for (rfd = mServerFd + 1; rfd <= maxfd; ++rfd) {
- // for (rfd = 0; rfd <= maxfd; ++rfd) {
for (rfd = 0; rfd < clientList.size(); rfd++) {
if (clientList[rfd] == NULL)
continue;
@@ -293,6 +284,7 @@ void SmartTvServer::loop() {
delete clientList[rfd];
clientList[rfd] = NULL;
mActiveSessions--;
+ *(mLog.log()) << " - Check Write: mActiveSessions= " << mActiveSessions << endl;
FD_CLR(rfd, &mReadState);
FD_CLR(rfd, &mWriteState);
}
@@ -344,7 +336,6 @@ void SmartTvServer::initServer(string dir) {
#ifndef STANDALONE
mConfig = new cSmartTvConfig(dir);
mLog.init(mConfig->getLogFile());
- // mLog.init("/multimedia/video/smartvvweblog.txt");
esyslog("SmartTvWeb: Logfile created");
*(mLog.log()) << mConfig->getLogFile() << endl;
@@ -352,11 +343,12 @@ void SmartTvServer::initServer(string dir) {
#else
mConfig = new cSmartTvConfig(".");
mLog.init(mConfig->getLogFile());
- // mLog.init("/tmp/smartvvweblog-standalone.txt");
cout << "SmartTvWeb: Logfile created" << endl;
cout << "SmartTvWeb: Listening on port= " << PORT << endl;
#endif
+
+ // mConfig->printConfig();
mSegmentDuration= mConfig->getSegmentDuration();
mHasMinBufferTime= mConfig->getHasMinBufferTime();
@@ -379,7 +371,13 @@ void SmartTvServer::initServer(string dir) {
memset((char *) &sock, 0, sizeof(sock));
sock.sin_family = AF_INET;
- sock.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (mConfig->getServerAddress() == "")
+ sock.sin_addr.s_addr = htonl(INADDR_ANY);
+ else {
+ *(mLog.log()) << "Binding Server to " << mConfig->getServerAddress() << endl;
+ sock.sin_addr.s_addr = inet_addr(mConfig->getServerAddress().c_str());
+ }
sock.sin_port = htons(serverPort);
ret = bind(mServerFd, (struct sockaddr *) &sock, sizeof(sock));
@@ -387,6 +385,16 @@ void SmartTvServer::initServer(string dir) {
*(mLog.log()) << "Error: Cannot bind serving socket, exit" << endl;
exit(1);
}
+
+ /*
+ struct ifreq ifr;
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);
+ ioctl(mServerFd, SIOCGIFADDR, &ifr);
+ string own_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
+ *(mLog.log()) << " own if ip= " << own_ip << endl;
+*/
ret = listen(mServerFd, 5);
if (ret <0) {
diff --git a/vdr-smarttvweb/smarttvfactory.h b/vdr-smarttvweb/smarttvfactory.h
index 7fc7bbd..171b1b8 100644
--- a/vdr-smarttvweb/smarttvfactory.h
+++ b/vdr-smarttvweb/smarttvfactory.h
@@ -38,8 +38,8 @@
using namespace std;
-#define PLG_VERSION "0.9.4"
-#define SERVER "SmartTvWeb/0.9.4"
+#define PLG_VERSION "0.9.5"
+#define SERVER "SmartTvWeb/0.9.5"
class SmartTvServer {
public:
@@ -72,7 +72,6 @@ class SmartTvServer {
int mLiveChannels;
vector<cHttpResource*> clientList;
- // list<int>mFdList;
int mActiveSessions;
string mConfigDir;
cSmartTvConfig *mConfig;
diff --git a/vdr-smarttvweb/smarttvweb.conf b/vdr-smarttvweb/smarttvweb.conf
index 382f5bb..c9f52ed 100644
--- a/vdr-smarttvweb/smarttvweb.conf
+++ b/vdr-smarttvweb/smarttvweb.conf
@@ -4,12 +4,25 @@ LogFile /tmp/smarttvweb.txt
MediaFolder /multimedia/video
+# Media Segment Duration for HLS/ DASH
SegmentDuration 10
+# minBufferTime value for the DASH MPD
HasMinBufferTime 30
+# Bitrate parameter in the DASH MPD
HasBitrate 60000000
+# Default number of Live Channel entries included in the channels.xml, when not requested specifically.
LiveChannels 30
-UseHasForHd false
+# Influence behavior, when you channels.conf contain group separators
+# Valid Values (case sensitive):
+# - Ignore : Ignore group separators)
+# - EmptyIgnore : Ignore empty group separators (which might be set to assign channel numbers)
+# - EmptyFolderDown : Interpretes an empty group channel as "cd ..", so that subsequent channels are not part of the group.
+GroupSeparators Ignore
+
+# Bind the web server to a specific IP address. Otherwise, the Web Server is listening on ALL interfaces.
+#ServerAddress 127.0.0.1
+
diff --git a/vdr-smarttvweb/stvw_cfg.c b/vdr-smarttvweb/stvw_cfg.c
index b624040..e323856 100755
--- a/vdr-smarttvweb/stvw_cfg.c
+++ b/vdr-smarttvweb/stvw_cfg.c
@@ -1,7 +1,7 @@
/*
* stvw_cfg.h: VDR on Smart TV plugin
*
- * Copyright (C) 2012 T. Lohmar
+ * Copyright (C) 2012, 2013 T. Lohmar
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -33,7 +33,7 @@
cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NULL),
mLogFile(), mMediaFolder(), mSegmentDuration(), mHasMinBufferTime(), mHasBitrate(),
- mLiveChannels() {
+ mLiveChannels(), mGroupSep(IGNORE), mServerAddress("") {
#ifndef STANDALONE
mLogFile= "";
@@ -47,7 +47,6 @@ cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NU
mHasMinBufferTime = 40;
mHasBitrate = 6000000;
mLiveChannels = 30;
- mLog = Log::getInstance();
readConfig();
}
@@ -55,6 +54,23 @@ cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NU
cSmartTvConfig::~cSmartTvConfig() {
}
+void cSmartTvConfig::printConfig() {
+ mLog = Log::getInstance();
+
+
+ *(mLog->log()) << "printConfig: " << endl;
+ *(mLog->log()) << " ConfigDir: " << mConfigDir << endl;
+ *(mLog->log()) << " LogFile: " << mLogFile << endl;
+ *(mLog->log()) << " MediaFolder:" << mMediaFolder << endl;
+ *(mLog->log()) << " SegmentDuration: " << mSegmentDuration << endl;
+ *(mLog->log()) << " HasMinBufferTime: " << mHasMinBufferTime << endl;
+ *(mLog->log()) << " HasBitrate: " << mHasBitrate << endl;
+ *(mLog->log()) << " LiveChannels: " << mLiveChannels << endl;
+ *(mLog->log()) << " GroupSeparators: " << ((mGroupSep==IGNORE)? "Ignore" : ((mGroupSep==EMPTYIGNORE)? "EmptyIgnore": "EmptyFolderDown")) << endl;
+ *(mLog->log()) << " ServerAddress: " << mServerAddress << endl;
+}
+
+
void cSmartTvConfig::readConfig() {
string line;
char attr[200];
@@ -112,6 +128,22 @@ void cSmartTvConfig::readConfig() {
continue;
}
+ if (strcmp(attr, "GroupSeparators") == 0) {
+ if (strcmp (value, "EmptyIgnore") == 0) {
+ mGroupSep = EMPTYIGNORE;
+ }
+ else if ( strcmp(value, "EmptyFolderDown") == 0) {
+ mGroupSep = EMPTYFOLDERDOWN;
+ }
+ continue;
+ }
+
+ if (strcmp(attr, "ServerAddress") == 0) {
+ mServerAddress = value;
+ // cout << " Found mLiveChannels= " <<mLiveChannels << endl;
+ continue;
+ }
+
#ifndef STANDALONE
esyslog("WARNING in SmartTvWeb: Attribute= %s with value= %s was not processed, thus ignored.", attr, value);
diff --git a/vdr-smarttvweb/stvw_cfg.h b/vdr-smarttvweb/stvw_cfg.h
index 1f124fb..84cbd07 100755
--- a/vdr-smarttvweb/stvw_cfg.h
+++ b/vdr-smarttvweb/stvw_cfg.h
@@ -1,7 +1,7 @@
/*
* stvw_cfg.h: VDR on Smart TV plugin
*
- * Copyright (C) 2012 T. Lohmar
+ * Copyright (C) 2012, 2013 T. Lohmar
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -33,16 +33,12 @@
using namespace std;
-/*
-class cResumes {
- public:
- cResumes(string t) : mDevice(t) {};
-
- vector<cResumeEntry> mResumes;
-
- string mDevice;
+enum eGroupSep {
+ IGNORE,
+ EMPTYIGNORE,
+ EMPTYFOLDERDOWN
};
-*/
+
class cSmartTvConfig {
private:
string mConfigDir;
@@ -56,13 +52,15 @@ class cSmartTvConfig {
unsigned int mHasBitrate;
int mLiveChannels;
+ eGroupSep mGroupSep;
+ string mServerAddress;
+
public:
cSmartTvConfig(string dir);
~cSmartTvConfig();
void readConfig();
-
- // cResumes* readConfig(string);
+ void printConfig();
string getLogFile() { return mLogFile; };
string getMediaFolder() { return mMediaFolder; };
@@ -70,6 +68,8 @@ class cSmartTvConfig {
int getHasMinBufferTime() { return mHasMinBufferTime; };
unsigned int getHasBitrate() {return mHasBitrate; };
int getLiveChannels() {return mLiveChannels; };
+ eGroupSep getGroupSep() { return mGroupSep; };
+ string getServerAddress() { return mServerAddress; };
};
#endif
diff --git a/vdr-smarttvweb/web/Data.js b/vdr-smarttvweb/web/Data.js
index 5e480d2..49e9026 100755
--- a/vdr-smarttvweb/web/Data.js
+++ b/vdr-smarttvweb/web/Data.js
@@ -1,16 +1,27 @@
+//Diff:
+// getNumString
+// createJQMDomTree
+
var Data =
{
assets : new Item,
folderList : [],
};
+Array.prototype.remove = function(from, to) {
+ var rest = this.slice((to || from) + 1 || this.length);
+ this.length = from < 0 ? this.length + from : from;
+ return this.push.apply(this, rest);
+};
+
Data.reset = function() {
this.assets = null;
this.assets = new Item;
this.folderList = [];
- this.folderList.push({item : this.assets, id: 0});
+// this.folderList.push({item : this.assets, id: 0});
+// Main.log("Data.reset: folderList.push. this.folderList.length= " + this.folderList.length);
};
Data.completed= function(sort) {
@@ -18,26 +29,31 @@ Data.completed= function(sort) {
this.assets.sortPayload();
this.folderList.push({item : this.assets, id: 0});
- console.log ("Data.completed()= " +this.folderList.length);
+// Main.log("Data.completed: folderList.push. this.folderList.length= " + this.folderList.length);
+// Main.log ("Data.completed()= " +this.folderList.length);
};
-Data.selectFolder = function (idx) {
- this.folderList.push({item : this.getCurrentItem().childs[idx], id: idx});
+Data.selectFolder = function (idx, first_idx) {
+ this.folderList.push({item : this.getCurrentItem().childs[idx], id: idx, first:first_idx});
+// Main.log("Data.selectFolder: folderList.push. this.folderList.length= " + this.folderList.length);
+};
+
+Data.folderUp = function () {
+ itm = this.folderList.pop();
+// Main.log("Data.folderUp: folderList.pop. this.folderList.length= " + this.folderList.length);
+ return itm;
+// return itm.id;
};
Data.isRootFolder = function() {
+// Main.log("Data.isRootFolder: this.folderList.length= " + this.folderList.length);
if (this.folderList.length == 1)
return true;
else
return false;
};
-Data.folderUp = function () {
- itm = this.folderList.pop();
- return itm.id;
-};
-
Data.addItem = function(t_list, pyld) {
this.assets.addChild(t_list, pyld, 0);
};
@@ -48,21 +64,24 @@ Data.dumpFolderStruct = function(){
Main.log("---------- dumpFolderStruct Done -------");
};
-Data.createDomTree = function () {
-
- return this.assets.createDomTree(0);
-};
-
Data.createJQMDomTree = function () {
return this.assets.createJQMDomTree(0);
};
+Data.findEpgUpdateTime = function() {
+ return this.assets.findEpgUpdateTime(Display.GetEpochTime() + 10000, "", 0);
+ // min, guid, level
+};
+
+Data.updateEpg = function (guid, entry) {
+ this.assets.updateEpgEntry(guid, entry, 0);
+};
+
Data.getCurrentItem = function () {
return this.folderList[this.folderList.length-1].item;
};
-Data.getVideoCount = function()
-{
+Data.getVideoCount = function() {
return this.folderList[this.folderList.length-1].item.childs.length;
};
@@ -84,7 +103,9 @@ Data.getNumString =function(num, fmt) {
return res;
};
-
+Data.deleteElm = function (pos) {
+ Data.getCurrentItem().childs.remove(pos);
+};
//-----------------------------------------
function Item() {
this.title = "root";
@@ -150,43 +171,71 @@ Item.prototype.addChild = function (key, pyld, level) {
}
};
-Item.prototype.print = function(level) {
+Item.prototype.findEpgUpdateTime = function (min, guid, level) {
var prefix= "";
for (var i = 0; i < level; i++)
- prefix += " ";
-
+ prefix += "-";
+
for (var i = 0; i < this.childs.length; i++) {
- Main.log(prefix + this.childs[i].title);
if (this.childs[i].isFolder == true) {
- Main.log(prefix+"Childs:");
- this.childs[i].print(level +1);
+ var res = this.childs[i].findEpgUpdateTime(min, guid, level+1);
+ min = res.min;
+ guid = res.guid;
}
- }
+ else {
+ var digi =new Date(this.childs[i].payload['start'] * 1000);
+ var str = digi.getHours() + ":" + digi.getMinutes();
+
+// Main.log(prefix + "min= " + min+ " start= " + this.childs[i].payload['start'] + " (" + str+ ") title= " + this.childs[i].title);
+
+ if ((this.childs[i].payload['start'] != 0) && ((this.childs[i].payload['start'] + this.childs[i].payload['dur']) < min)) {
+ min = this.childs[i].payload['start'] + this.childs[i].payload['dur'];
+ guid = this.childs[i].payload['guid'] ;
+// Main.log(prefix + "New Min= " + min + " new id= " + guid + " title= " + this.childs[i].title);
+// Main.logToServer(prefix + "New Min= " + min + " new id= " + guid + " title= " + this.childs[i].title);
+ }
+ }
+ }
+
+ return { "min": min, "guid" : guid};
};
-Item.prototype.createDomTree = function(level) {
+Item.prototype.updateEpgEntry = function (guid, entry, level) {
var prefix= "";
for (var i = 0; i < level; i++)
prefix += "-";
-// var mydiv = $('<div class="folder">' +prefix+this.title+ '</div>');
- var mydiv = $('<ul />');
-
for (var i = 0; i < this.childs.length; i++) {
if (this.childs[i].isFolder == true) {
-// mydiv.appendChild(this.childs[i].createDomTree());
- var myli = $('<li class="folder">' +prefix +this.childs[i].title + '</li>');
- myli.append(this.childs[i].createDomTree(level+1));
- mydiv.append(myli);
+ var res = this.childs[i].updateEpgEntry(guid, entry, level+1);
+ if (res == true)
+ return true;
}
- else {
-// var mya = $('<a class="link">' +prefix +this.childs[i].title + '</a>');
- var mya = $('<a>', { text: prefix +this.childs[i].title, href: this.childs[i].payload['link']} );
- var myli = $('<li class="item"/>');
- myli.append(mya);
- mydiv.append(myli);
+ else {
+ if (this.childs[i].payload['guid'] == guid) {
+ Main.log("updateEpgEntry: Found " + this.childs[i].title);
+ this.childs[i].payload.prog = entry.prog;
+ this.childs[i].payload.desc = entry.desc;
+ this.childs[i].payload.start = entry.start;
+ this.childs[i].payload.dur = entry.dur;
+ return true;
+ }
}
- }
- return mydiv;
+ }
+ return false;
+};
+
+Item.prototype.print = function(level) {
+ var prefix= "";
+ for (var i = 0; i < level; i++)
+ prefix += " ";
+
+ for (var i = 0; i < this.childs.length; i++) {
+ Main.log(prefix + this.childs[i].title);
+ if (this.childs[i].isFolder == true) {
+ Main.log(prefix+"Childs:");
+ this.childs[i].print(level +1);
+ }
+ }
};
Item.prototype.createJQMDomTree = function(level) {
diff --git a/vdr-smarttvweb/web/Server.js b/vdr-smarttvweb/web/Server.js
index 14385df..1ab6d0a 100755
--- a/vdr-smarttvweb/web/Server.js
+++ b/vdr-smarttvweb/web/Server.js
@@ -1,5 +1,4 @@
-var Server =
-{
+var Server = {
dataReceivedCallback : null,
errorCallback : null,
doSort : false,
@@ -10,7 +9,7 @@ var Server =
Server.init = function()
{
- var success = true;
+ var success = true;
if (this.XHRObj) {
this.XHRObj.destroy();
@@ -23,100 +22,159 @@ Server.init = function()
Server.setSort = function (val) {
this.doSort = val;
};
-
+//---------------------------------------------
Server.fetchVideoList = function(url) {
- if (this.XHRObj == null) {
- this.XHRObj = new XMLHttpRequest();
- }
-
- if (this.XHRObj) {
- this.XHRObj.onreadystatechange = function()
- {
- // var splashElement = document.getElementById("splashStatus");
-
- if (Server.XHRObj.readyState == 4) {
- Server.createVideoList();
- }
- };
-
- this.XHRObj.open("GET", url, true);
- this.XHRObj.send(null);
- }
- else {
- console.log("Failed to create XHR");
- if (this.errorCallback != null) {
- this.errorCallback("ServerError");
- }
- }
-};
+ $.ajax({
+ url: url,
+ type : "GET",
+ success : function(data, status, XHR ) {
+// Main.log("Server.fetchVideoList Success Response - status= " + status + " mime= " + XHR.responseType + " data= "+ data);
-Server.createVideoList = function() {
- console.log ("creating Video list now");
- console.log ("status= " + this.XHRObj.status);
-
- if (this.XHRObj.status != 200) {
- if (this.errorCallback != null) {
- this.errorCallback(this.XHRObj.responseText);
- }
- }
- else {
- var xmlResponse = this.XHRObj.responseXML;
- if (xmlResponse == null) {
- if (this.errorCallback != null) {
- this.errorCallback("XmlError");
+ $(data).find("item").each(function () {
+ var title = $(this).find('title').text();
+// var link = $(this).find('link').text();
+ var link = $(this).find('enclosure').attr('url');
+ var guid = $(this).find('guid').text();
+ var programme = $(this).find('programme').text();
+ var description = $(this).find('description').text();
+ var startVal = parseInt($(this).find('start').text());
+ var durVal = parseInt($(this).find('duration').text());
+ var fps = parseFloat($(this).find('fps').text());
+ var ispes = $(this).find('ispes').text();
+ var isnew = $(this).find('isnew').text();
+// Main.log("Server.fetchVideoList: title= " + title + " start= " + startVal + " dur= " + durVal + " fps= " + fps);
+
+/* if (Main.state == Main.eLIVE) {
+ Epg.guidTitle[guid] = title;
+ }
+*/
+ var title_list = title.split("~");
+ Data.addItem( title_list, {link : link, prog: programme, desc: description, guid : guid, start: startVal,
+ dur: durVal, ispes : ispes, isnew : isnew, fps : fps});
+
+ }); // each
+
+ Data.completed(Server.doSort);
+
+ if (Server.dataReceivedCallback) {
+ Server.dataReceivedCallback();
}
- return;
- }
- var xmlElement = xmlResponse.documentElement;
-
- if (!xmlElement) {
- console.log("Failed to get valid XML");
- return;
- }
- else
- {
- var items = xmlElement.getElementsByTagName("item");
- if (items.length == 0) {
- console.log("Something wrong. Response does not contain any item");
- this.errorCallback("Empty Response");
- };
-
- for (var index = 0; index < items.length; index++) {
-
- var titleElement = items[index].getElementsByTagName("title")[0];
- var progElement = items[index].getElementsByTagName("programme")[0];
- var descriptionElement = items[index].getElementsByTagName("description")[0];
- var linkElement = items[index].getElementsByTagName("link")[0];
- var startVal =0;
- var durVal =0;
- try {
- startVal = parseInt(items[index].getElementsByTagName("start")[0].firstChild.data);
- durVal = parseInt(items[index].getElementsByTagName("duration")[0].firstChild.data);
- }
- catch (e) {
- this.errorCallback("XML Parsing Error: " + e);
- console.log("ERROR: "+e);
- }
-
- var desc = descriptionElement.firstChild.data;
-
- if (titleElement && linkElement) {
- var title_list = titleElement.firstChild.data.split("~");
- Data.addItem( title_list, {link : linkElement.firstChild.data,
- prog: progElement.firstChild.data,
- desc: desc,
- start: startVal,
- dur: durVal});
- }
-
+
+ },
+ error : function (jqXHR, status, error) {
+// Main.logToServer("Server.fetchVideoList Error Response - status= " + status + " error= "+ error);
+// Display.showPopup("Error with XML File: " + status);
+ Server.retries ++;
+ },
+ parsererror : function () {
+ // Main.logToServer("Server.fetchVideoList parserError " );
+ // Display.showPopup("Error in XML File");
+ Server.retries ++;
+ if (Server.errorCallback != null) {
+ Server.errorCallback("XmlError");
}
- Data.completed(this.doSort);
- if (this.dataReceivedCallback)
- {
- this.dataReceivedCallback(); /* Notify all data is received and stored */
- }
- }
- }
+ }
+ });
+};
+
+
+//---------------------------------------------
+
+Server.updateVdrStatus = function (){
+ Main.log ("get VDR Status");
+ $.ajax({
+ url: Config.serverUrl + "/vdrstatus.xml",
+ type : "GET",
+ success : function(data, status, XHR){
+ var free = $(data).find('free').text() / 1024.0;
+ var used = $(data).find('used').text() / 1024.0;
+ var percent = $(data).find('percent').text();
+
+ var unit = "GB";
+ var free_str = free.toFixed(2);
+ if (free_str.length > 6) {
+ free = free / 1024.0;
+ free_str = free.toFixed(2);
+ unit = "TB";
+ }
+ $("#logoDisk").text("Free: " +free_str + unit);
+ $("#selectDisk").text("Free: " +free_str + unit);
+ },
+ error: function(jqXHR, status, error){
+ Main.log("VdrStatus: Error");
+ }
+ });
+}
+
+
+Server.getResume = function (guid) {
+// Main.log ("***** getResume *****");
+ $.ajax({
+ url: Config.serverUrl + "/getResume.xml",
+ type : "POST",
+ data : "filename:" + guid +"\n",
+ success : function(data, status, XHR ) {
+ Main.log("**** Resome Success Response - status= " + status + " mime= " + XHR.responseType + " data= "+ data);
+
+ var resume_str = $(data).find("resume").text();
+ if (resume_str != "") {
+ var resume_val = parseFloat(resume_str);
+ Main.log("resume val= " + resume_val );
+ Main.logToServer("resume val= " + resume_val );
+ Player.resumePos = resume_val;
+ Player.playVideo( resume_val);
+ }
+ else {
+ Display.hide();
+ Display.showProgress();
+ Player.playVideo(-1);
+ }
+
+ },
+ error : function (jqXHR, status, error) {
+ Main.log("**** Resome Error Response - status= " + status + " error= "+ error);
+ Display.hide();
+ Display.showProgress();
+ Player.playVideo(-1);
+ }
+ });
+};
+
+Server.saveResume = function() {
+ var msg = "";
+ msg += "filename:" + Data.getCurrentItem().childs[Main.selectedVideo].payload.guid + "\n";
+ msg += "resume:"+ (Player.curPlayTime/1000) + "\n" ;
+
+ $.post(Config.serverUrl + "/setResume.xml", msg, function(data, textStatus, XHR) {
+ Main.logToServer("SaveResume Status= " + XHR.status );
+ }, "text");
+
+};
+
+Server.deleteRecording = function(guid) {
+ Main.log("Server.deleteRecording guid=" + guid);
+ Main.logToServer("Server.deleteRecording guid=" + guid);
+ Notify.handlerShowNotify("Deleting...", false);
+
+ $.ajax({
+ url: Config.serverUrl + "/deleteRecording.xml?id=" +guid,
+ type : "POST",
+ success : function(data, status, XHR ) {
+ Notify.showNotify("Deleted", true);
+ Data.deleteElm(Main.selectedVideo);
+ if (Main.selectedVideo >= Data.getVideoCount())
+ Main.selectedVideo = Data.getVideoCount() -1;
+ Server.updateVdrStatus();
+ Display.setVideoList(Main.selectedVideo, (Main.selectedVideo - Display.currentWindow));
+ Main.logToServer("Server.deleteRecording: Success" );
+ },
+ error : function (XHR, status, error) {
+ Main.logToServer("Server.deleteRecording: Error" );
+
+ // show popup
+ Notify.showNotify("Error", true);
+ }
+ });
};