summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthlo <t.lohmar@gmx.de>2013-04-04 21:05:33 +0200
committerthlo <t.lohmar@gmx.de>2013-04-04 21:05:33 +0200
commitd5c26eb7a9c51e82be0ac41bd04f4433043776cd (patch)
treed79396cb2cfb4bde345a4c8339d38098ff5b3eb0
parent09f926c410cc6cbed1905b8ddf35f04d14a7805a (diff)
downloadvdr-plugin-smarttvweb-d5c26eb7a9c51e82be0ac41bd04f4433043776cd.tar.gz
vdr-plugin-smarttvweb-d5c26eb7a9c51e82be0ac41bd04f4433043776cd.tar.bz2
New urls manager for YouTube URLs. New http client class. Various bug fixes.
-rw-r--r--httpclient.c361
-rw-r--r--httpclient.h90
-rwxr-xr-xhttpresource.c357
-rwxr-xr-xhttpresource.h22
-rw-r--r--httpresource_base.h46
-rw-r--r--mngurls.c106
-rw-r--r--mngurls.h64
-rwxr-xr-xsmarttvfactory.c147
-rw-r--r--smarttvfactory.h39
9 files changed, 1192 insertions, 40 deletions
diff --git a/httpclient.c b/httpclient.c
new file mode 100644
index 0000000..cdf7645
--- /dev/null
+++ b/httpclient.c
@@ -0,0 +1,361 @@
+/*
+ * httpclient.c: VDR on Smart TV plugin
+ *
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#include "httpclient.h"
+#include "smarttvfactory.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <cstdlib>
+#include <sstream>
+
+#define OKAY 0
+#define ERROR (-1)
+
+
+#define DEBUG
+
+#define DEBUGPREFIX "mClient= " << mReqId << " fd= " << mFd
+
+cHttpClientBase::cHttpClientBase(int f, int id, int port, SmartTvServer* factory, string peer) : cHttpResourceBase(f, id, port, factory),
+ mLog(), mRequestMessage(""), mRequestMessagePos(0), mConnState(0), mResponseHdr(), mRespBdyLen(-1),
+ mStatus(-1), mIsChunked(false), mResponseBdy(),
+ mPeer(peer), mTransCount(0) {
+
+ mLog = Log::getInstance();
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << "cHttpClientBase Constructor" << endl;
+#endif
+ mRequestMessage = "";
+}
+
+cHttpClientBase::~cHttpClientBase() {
+ *(mLog->log()) << "cHttpClientBase Destructor" << endl;
+}
+
+int cHttpClientBase::handleRead() {
+ if ((mConnState == 0) || (mConnState ==3)) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " Read during write or wait state. Ok, return." << endl;
+ return OKAY;
+ }
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " handleRead" << endl;
+#endif
+
+ const size_t bufflen = 4096;
+ ssize_t bytesreceived = 0;
+ char buff[bufflen];
+
+ string tmp = "";
+ bytesreceived = recv(mFd, buff, bufflen, 0);
+ if (bytesreceived <0 ) {
+ *(mLog->log()) << DEBUGPREFIX << " ERROR while receiving" << endl;
+ return ERROR;
+ }
+
+ if (bytesreceived ==0 ) {
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " Done: No bytes received anymore -> Closing"
+ << " mRespBdyLen= " << mRespBdyLen
+ << " mResponseBdy.size()= " << mResponseBdy.size()
+ << endl;
+#endif
+ return ERROR;
+ }
+
+ tmp = string(buff, bytesreceived);
+
+ size_t pos ;
+ switch (mConnState) {
+ case 1:
+ // looking for header end
+ pos = tmp.find("\r\n\r\n");
+
+ if (pos == string::npos)
+ mResponseHdr += tmp;
+ else {
+ // Header End found
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " Header End Found. Setting mConnState to 2"
+ << endl;
+#endif
+
+ mResponseHdr += tmp.substr(0, pos);
+ if (pos+4 < tmp.size())
+ mResponseBdy = tmp.substr(pos+4);
+ mConnState = 2;
+ processResponseHeader();
+ }
+
+ break;
+ case 2:
+ // end of state 2 is determined in checkTransactionCompletion
+ mResponseBdy += tmp;
+ break;
+ default:
+ *(mLog->log()) << DEBUGPREFIX
+ << " ERROR in handleRead: Def: " << tmp << endl;
+ break;
+ }
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " handleRead - end" << endl;
+#endif
+ return checkTransactionCompletion();
+}
+
+int cHttpClientBase::handleWrite() {
+ if (mConnState == 3) {
+ // waiting
+ timeval now;
+ gettimeofday(&now, 0);
+
+ long diff; // in ms
+ diff = (now.tv_sec - mWaitStart.tv_sec) *1000;
+ diff += (now.tv_usec - mWaitStart.tv_usec) /1000;
+ mWaitCount++;
+
+ if ((diff > 40) || (mWaitCount>10000)) {
+ createRequestMessage("");
+ }
+ return OKAY;
+
+ }
+ if (mConnState >0) {
+ return OKAY;
+ }
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " ++++ handleWrite ++++" << endl;
+#endif
+
+ int rem_len = mRequestMessage.size() - mRequestMessagePos;
+ if (rem_len == 0) {
+ mConnState = 1; // Change state to Read
+ *(mLog->log()) << DEBUGPREFIX
+ << " WARNING: Should not be here " << endl;
+ return OKAY;
+ }
+
+ string tmp = mRequestMessage.substr(mRequestMessagePos, rem_len);
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " handleWrite: mTransCount= " << mTransCount
+ << " Msg.size= " << mRequestMessage.size()
+ << endl;
+#endif
+ int snd = send(mFd, tmp.c_str(), (size_t) tmp.size(), 0);
+
+ if (snd <0) {
+ *(mLog->log()) << DEBUGPREFIX << " ERROR while sending" << endl;
+ return ERROR;
+ }
+
+ if (snd == rem_len) {
+ // done with sending. Reading now
+ mConnState = 1;
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " -> Done with Sending. Reading now" << endl;
+#endif
+ }
+ else {
+ *(mLog->log()) << DEBUGPREFIX << " Sent " << snd << " byte. Continue writing request." << endl;
+ }
+ mRequestMessagePos+= snd;
+ return OKAY;
+}
+
+int cHttpClientBase::checkStatus() {
+ // TODO: Should check for stalled client
+ return OKAY;
+}
+
+void cHttpClientBase::createRequestMessage(string path) {
+ mResponseHdr= "";
+ mResponseBdy= "";
+ mRespBdyLen = -1;
+ mRequestMessagePos = 0;
+ mStatus = -1;
+ mConnState = 0; // WriteRequest
+
+ string req_body = "";
+ stringstream cont_len;
+
+ string device_id = "12331";
+ string device_name = "VdrOnTv";
+ string vendor = "mVdrOnTv";
+ string product = "SMARTDev";
+
+
+ switch (mTransCount) {
+ case 0:
+ mRequestMessage = "POST /ws/app/VdrOnTv/connect HTTP/1.1\r\n";
+ mRequestMessage += "Host: " + mPeer + "\r\n";
+ mRequestMessage += "SLDeviceID: "+device_id +"\r\n";
+ mRequestMessage += "ProductID: "+product+"\r\n";
+ mRequestMessage += "VendorID: "+vendor+"\r\n";
+ mRequestMessage += "DeviceName: "+device_name+"\r\n";
+ mRequestMessage += "Connection: keep-alive\r\n";
+ mRequestMessage += "Content-Length: 0\r\n";
+ mRequestMessage += "Accept: */*\r\n";
+ mRequestMessage += "\r\n";
+
+ break;
+ case 1:
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " NextMessage: mTransCount= " << mTransCount
+ << endl;
+#endif
+
+ req_body = getMsgBody(mTransCount);
+ cont_len << "Content-Length: " << req_body.size() << "\r\n";
+
+ mRequestMessage = "POST /ws/app/VdrOnTv/queue HTTP/1.1\r\n";
+ mRequestMessage += "Host: " + mPeer + "\r\n";
+ mRequestMessage += "SLDeviceID: "+device_id +"\r\n";
+ mRequestMessage += "Accept: */*\r\n";
+ mRequestMessage += "Connection: keep-alive\r\n";
+ mRequestMessage += "Content-Type: application/json\r\n";
+ mRequestMessage += cont_len.str();
+
+ mRequestMessage += "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n";
+ mRequestMessage += "\r\n";
+
+ mRequestMessage += req_body;
+
+ break;
+
+ default:
+ *(mLog->log()) << DEBUGPREFIX
+ << " ############ ERROR, mTransCount= " << mTransCount
+ << endl;
+ break;
+
+ }
+}
+
+
+void cHttpClientBase::processResponseHeader() {
+ mRespBdyLen = -1;
+
+ size_t s_sp = mResponseHdr.find(" ");
+ size_t s_sp2 = mResponseHdr.find(" ", s_sp+1);
+
+ mStatus = atoi(mResponseHdr.substr(s_sp, (s_sp2-s_sp)).c_str());
+ *(mLog->log()) << DEBUGPREFIX
+ << " mStatus= " << mStatus
+ << endl;
+ size_t c_start = mResponseHdr.find("Content-Length:");
+ if (c_start != string::npos) {
+ size_t c_col = mResponseHdr.find(':', c_start);
+ size_t c_end = mResponseHdr.find('\r', c_start);
+
+ mRespBdyLen = atoi(mResponseHdr.substr(c_col +1, (c_end -c_col -1)).c_str());
+ *(mLog->log()) << DEBUGPREFIX
+ << " Content-Length(val)= " << mRespBdyLen
+ << endl;
+
+ } // if (c_start != string::npos) {
+ else {
+ *(mLog->log()) << DEBUGPREFIX
+ << " Content-Length not found "
+ << endl;
+ }
+
+ size_t tr_start = mResponseHdr.find("Transfer-Encoding:");
+ if (tr_start != string::npos) {
+ size_t tr_col = mResponseHdr.find(':', tr_start);
+ size_t tr_end = mResponseHdr.find('\r', tr_start);
+
+ string chunked = mResponseHdr.substr(tr_col +1, (tr_end -tr_col -1));
+ *(mLog->log()) << DEBUGPREFIX
+ << " Transfer-Encoding(val)= " << chunked
+ << endl;
+ mIsChunked = false;
+
+ } // if (tr_start != string::npos) {
+
+}
+
+
+int cHttpClientBase::checkTransactionCompletion() {
+ // hmm, here I assume that the response is received in first byte chunk
+ // should first check that I am in state 2 (hdr received)
+ if (mConnState < 2)
+ return OKAY;
+
+ //OK, header received
+ if (mStatus != 200) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " Closing.... Status= " << mStatus
+ << endl;
+ return ERROR;
+ }
+
+ if ((mRespBdyLen <= mResponseBdy.size()) || (mRespBdyLen == 0)) {
+
+ *(mLog->log()) << DEBUGPREFIX
+ << " Transaction completed. mTransCount= " << mTransCount
+ << endl;
+
+ if (mTransCount ==0 ) {
+ // ok
+ gettimeofday(&mWaitStart, 0);
+ mWaitCount = 0;
+ mConnState = 3;
+ mTransCount++;
+ return OKAY;
+ }
+ else
+ return ERROR;
+ }
+ return OKAY;
+}
+
+//------------------------------
+//----- cHttpYtPushClient ------
+//------------------------------
+
+cHttpYtPushClient::cHttpYtPushClient(int f, int id, int port, SmartTvServer* fac, string peer, string vid, bool store) : cHttpClientBase(f, id, port, fac, peer),
+ mVideoId(vid), mStore(store) {
+
+ createRequestMessage("");
+}
+
+cHttpYtPushClient::~cHttpYtPushClient() {
+}
+
+string cHttpYtPushClient::getMsgBody(int) {
+ return "{\"type\":\"YT\",payload:{\"id\":\"" + mVideoId +"\", \"store\":"+((mStore)?"true":"false")+"}}";
+}
+
diff --git a/httpclient.h b/httpclient.h
new file mode 100644
index 0000000..b5a39ea
--- /dev/null
+++ b/httpclient.h
@@ -0,0 +1,90 @@
+/*
+ * httpclient.h: VDR on Smart TV plugin
+ *
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#ifndef __HTTPCLIENT_H__
+#define __HTTPCLIENT_H__
+
+#include "log.h"
+#include "httpresource_base.h"
+#include <sys/time.h>
+
+
+
+class SmartTvServer;
+class cHttpClientBase : public cHttpResourceBase {
+
+ public:
+ cHttpClientBase(int, int, int, SmartTvServer*, string peer);
+ virtual ~cHttpClientBase();
+
+ int handleRead();
+ int handleWrite();
+
+ int checkStatus();
+
+ protected:
+ void createRequestMessage(string path);
+ void processResponseHeader();
+
+ int checkTransactionCompletion();
+
+ virtual string getMsgBody(int ) =0;
+
+ Log* mLog;
+
+ string mRequestMessage;
+ int mRequestMessagePos;
+
+ int mConnState;
+ // 0: Write Request
+ // 1: Read Header
+ // 2: Read Payload
+ // 3: Wait
+ string mResponseHdr;
+ int mRespBdyLen;
+ int mStatus;
+ bool mIsChunked; // only value if state== 2,
+ // if mIsChunked == true, then the chunk length is stored in mRespBdyLen
+ string mResponseBdy;
+
+ string mPeer;
+ int mTransCount;
+
+ timeval mWaitStart;
+ int mWaitCount;
+};
+
+
+class cHttpYtPushClient : public cHttpClientBase {
+ public:
+ cHttpYtPushClient(int, int, int, SmartTvServer*, string peer, string vid, bool store);
+ virtual ~cHttpYtPushClient();
+
+ protected:
+ string getMsgBody(int );
+
+ string mVideoId;
+ bool mStore;
+
+};
+
+#endif
diff --git a/httpresource.c b/httpresource.c
index 79d39bc..2da4345 100755
--- a/httpresource.c
+++ b/httpresource.c
@@ -34,12 +34,13 @@
#include <string>
#include <cstring>
+#include <sstream>
#include <iostream>
#include <vector>
#include "httpresource.h"
#include "smarttvfactory.h"
#include "stvw_cfg.h"
-
+#include "mngurls.h"
#include "url.h"
#ifndef STANDALONE
@@ -49,6 +50,10 @@
#include <vdr/videodir.h>
#include <vdr/epg.h>
+#else
+//standalone
+#include <netinet/in.h>
+#include <arpa/inet.h>
#endif
#define PROTOCOL "HTTP/1.1"
@@ -101,8 +106,8 @@ sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {};
// 8 Byte Per Entry
struct tIndexPes {
uint32_t offset;
- uchar type;
- uchar number;
+ uint8_t type; // standalone
+ uint8_t number; // standalone
uint16_t reserved;
};
@@ -115,16 +120,11 @@ struct tIndexTs {
uint16_t number:16; // up to 64K files per recording
};
+//cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(),
+// mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0),
-/*
-union tIndexRead {
- struct tIndexTs in;
- char buf[8];
-};
-*/
-
-cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(),
- mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0),
+cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): cHttpResourceBase(f, id, port, factory),
+ mLog(), mConnTime(0), mHandleReadCount(0),
mConnected(true), mConnState(WAITING), mContentType(NYD), mReadBuffer(),
mMethod(), mResponseMessagePos(0), mBlkData(NULL), mBlkPos(0), mBlkLen(0),
mPath(), mVersion(), protocol(), mReqContentLength(0),
@@ -139,17 +139,17 @@ cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): m
mBlkData = new char[MAXLEN];
//#ifndef DEBUG
- *(mLog->log())<< DEBUGPREFIX
- << " cHttpResource created" << endl;
+ *(mLog->log()) << DEBUGPREFIX
+ << " cHttpResource created" << endl;
//#endif
}
cHttpResource::~cHttpResource() {
//#ifndef DEBUG
- *(mLog->log())<< DEBUGPREFIX
- << " Destructor of cHttpResource called"
- << endl;
+ *(mLog->log()) << DEBUGPREFIX
+ << " Destructor of cHttpResource called"
+ << endl;
//#endif
delete[] mBlkData;
if (mFile != NULL) {
@@ -171,7 +171,7 @@ int cHttpResource::checkStatus() {
if (now - mConnTime > 2) {
*(mLog->log()) << DEBUGPREFIX
<< " checkStatus: no activity for 2sec "
- << "mmConnState= " << getConnStateName()
+ << "mConnState= " << getConnStateName()
<< endl;
return ERROR;
}
@@ -229,7 +229,6 @@ int cHttpResource::handleRead() {
return ERROR; // Nothing to read
}
- // if (( mConnState == WAITING) and ((time(NULL) - mConnTime) > 1)) {
if (( mConnState == WAITING) and (mHandleReadCount > 1000)) {
*(mLog->log()) << DEBUGPREFIX << " hmm, handleRead() no data since 1sec -> closing. mHandleReadCount= " << mHandleReadCount << endl;
return ERROR; // Nothing to read
@@ -389,6 +388,22 @@ int cHttpResource::processRequest() {
#endif
+ if (mPath.compare("/yt-bookmarklet.js") == 0) {
+ if (handleHeadRequest() != 0)
+ return OKAY;
+
+ sendYtBookmarkletJs();
+ return OKAY;
+ }
+
+ if (mPath.compare("/bmlet-inst.html") == 0) {
+ if (handleHeadRequest() != 0)
+ return OKAY;
+
+ sendBmlInstHtml();
+ return OKAY;
+ }
+
if (mPath.compare("/media.xml") == 0) {
if (handleHeadRequest() != 0)
@@ -397,6 +412,13 @@ int cHttpResource::processRequest() {
return OKAY;
}
+ if (mPath.compare("/clients") == 0) {
+ if (handleHeadRequest() != 0)
+ return OKAY;
+ handleClients();
+ return OKAY;
+ }
+
if (mPath.compare("/widget.conf") == 0) {
mPath = mFactory->getConfigDir() + "/widget.conf";
@@ -412,6 +434,7 @@ int cHttpResource::processRequest() {
return sendFile(&statbuf);
}
+
if (mPath.compare("/favicon.ico") == 0) {
mPath = mFactory->getConfigDir() + "/web/favicon.ico";
@@ -427,6 +450,12 @@ int cHttpResource::processRequest() {
return sendFile(&statbuf);
}
+ if (mPath.compare("/urls.xml") == 0) {
+ if (handleHeadRequest() != 0)
+ return OKAY;
+ sendUrlsXml();
+ return OKAY;
+ }
if (mPath.size() > 8) {
if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) {
@@ -791,6 +820,14 @@ int cHttpResource::handlePost() {
return OKAY;
}
+ if (mPath.compare("/setYtUrl") == 0) {
+ if (handleHeadRequest() != 0)
+ return OKAY;
+
+ receiveYtUrl();
+ return OKAY;
+ }
+
if (mPath.compare("/deleteRecording.xml") == 0) {
if (handleHeadRequest() != 0)
return OKAY;
@@ -1181,6 +1218,52 @@ int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) {
return OKAY;
}
+int cHttpResource::receiveYtUrl() {
+ vector<sQueryAVP> avps;
+ parseQueryLine(&avps);
+ string line;
+ string store_str;
+ bool store = true;
+
+ /* *(mLog->log()) << DEBUGPREFIX
+ << " receiveYtUrl: Query= " << mQuery
+ << endl;
+*/
+ if (getQueryAttributeValue(&avps, "store", store_str) == OKAY){
+ if (store_str.compare("false")==0) {
+ store = false;
+ *(mLog->log()) << DEBUGPREFIX
+ << " receiveYtUrl: set store to false "
+ << endl;
+
+ }
+ }
+ if (getQueryAttributeValue(&avps, "line", line) == OKAY){
+ if (line.compare ("") == 0) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " receiveYtUrl: Nothing to push "
+ << endl;
+
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return OKAY;
+ }
+
+ mFactory->pushYtVideoId(line, store);
+
+ if (store)
+ mFactory->storeYtVideoId(line);
+
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return OKAY;
+
+ }
+
+
+ sendError(400, "Bad Reqiest", NULL, "Mandatory Line attribute not present.");
+ return OKAY;
+
+}
+
void cHttpResource::writeM3U8(double duration, int bitrate, float seg_dur, int end_seg) {
mResponseMessage = new string();
mResponseMessagePos = 0;
@@ -1440,6 +1523,67 @@ int cHttpResource::sendMediaSegment (struct stat *statbuf) {
return OKAY;
}
+int cHttpResource::sendUrlsXml () {
+ // read urls file and generate XML
+ string type;
+ string value;
+ string line;
+
+ mResponseMessage = new string();
+ mResponseMessagePos = 0;
+ *mResponseMessage = "";
+ mContentType = MEMBLOCK;
+
+ mConnState = SERVING;
+
+ cManageUrls* urls = mFactory->getUrlsObj();
+
+ // ifstream myfile ((mFactory->getConfigDir() +"/urls.txt").c_str());
+ // An empty xml is provided, if the file does not exist.
+
+ //thlo: here to continue
+ *mResponseMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *mResponseMessage += "<rss version=\"2.0\">\n";
+ *mResponseMessage += "<channel>\n";
+
+ for (uint i = 0; i < urls->size(); i++) {
+ *mResponseMessage += "<item>\n<guid>";
+ *mResponseMessage += (urls->getEntry(i))->mEntry;
+ *mResponseMessage += "</guid>\n</item>\n";
+ }
+
+ /*
+ while ( myfile.good() ) {
+ getline (myfile, line);
+
+ if ((line == "") or (line[0] == '#'))
+ continue;
+
+ size_t pos = line.find('|');
+ type = line.substr(0, pos);
+ value = line.substr(pos+1);
+
+ if (type.compare("YT")==0) {
+ *mResponseMessage += "<item>\n<guid>";
+ *mResponseMessage += value;
+ *mResponseMessage += "</guid>\n</item>\n";
+ continue;
+ }
+
+ *mResponseMessage += "<item>\n<title>";
+ *mResponseMessage += "Unknown: " + line + " type=" + type;
+
+ *mResponseMessage += "</title>\n</item>\n";
+ }
+ myfile.close();
+*/
+ *mResponseMessage += "</channel>\n";
+ *mResponseMessage += "</rss>\n";
+
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
int cHttpResource::sendMediaXml (struct stat *statbuf) {
char pathbuf[4096];
string link;
@@ -1494,6 +1638,48 @@ int cHttpResource::sendMediaXml (struct stat *statbuf) {
return OKAY;
}
+
+void cHttpResource::handleClients() {
+ vector<sQueryAVP> avps;
+ parseQueryLine(&avps);
+
+ string mac = "";
+ string ip = "";
+ string state = "";
+
+ getQueryAttributeValue(&avps, "mac", mac) ;
+ getQueryAttributeValue(&avps, "ip", ip);
+ getQueryAttributeValue(&avps, "state", state);
+
+ /* if (getQueryAttributeValue(&avps, "mac", mac) == OKAY){
+ }
+
+ if (getQueryAttributeValue(&avps, "ip", ip) == OKAY){
+ }
+
+ if (getQueryAttributeValue(&avps, "state", state) == OKAY){
+ }
+*/
+ // state: started, running, stopped
+ *(mLog->log())<< DEBUGPREFIX
+ << " handleClients mac= " << mac << " ip= " << ip << " state= " << state
+ << endl;
+ if (mac.compare ("") == 0) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " mac is empty. Ignoring"
+ << endl;
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return;
+ }
+ if (state.compare("stopped") == 0) {
+ mFactory->removeTvClient(ip, mac, time(NULL));
+ }
+ else {
+ mFactory->updateTvClient(ip, mac, time(NULL));
+ }
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+}
+
int cHttpResource::sendVdrStatusXml (struct stat *statbuf) {
#ifndef STANDALONE
@@ -1533,6 +1719,120 @@ int cHttpResource::sendVdrStatusXml (struct stat *statbuf) {
return OKAY;
}
+int cHttpResource::sendYtBookmarkletJs() {
+ *(mLog->log()) << DEBUGPREFIX
+ << " sendYtBookmarkletJs" << endl;
+
+ vector<sQueryAVP> avps;
+ parseQueryLine(&avps);
+ string store_str = "";
+ bool store= true;
+
+ if (getQueryAttributeValue(&avps, "store", store_str) == OKAY) {
+ if (store_str.compare("false") == 0) {
+ store= false;
+ *(mLog->log()) << DEBUGPREFIX
+ << " store= false " << endl;
+ }
+ }
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ mContentType = MEMBLOCK;
+
+ mConnState = SERVING;
+
+ stringstream own_host ;
+ own_host << "http://"
+ << getOwnIp(mFd)
+ << ":" << mServerPort;
+
+ // string own_host = "http://"+getOwnIp(mFd)+ ":" + str;
+
+ *(mLog->log()) << " Ownhost= " << own_host.str() << endl;
+
+
+ *mResponseMessage = "function get_query_var (querystring, name) { "
+ "var filter = new RegExp( name + \"=([^&]+)\" ); "
+ "var res = null;"
+ "if (querystring != null)"
+ " res = querystring.match(filter);"
+ " if (res != null) return unescape( res[1] );"
+ " else return \"\";"
+ "}"
+ "var vid_id= get_query_var(document.URL, \"v\");"
+
+ "var iframe = document.createElement(\"iframe\");"
+ "iframe.setAttribute(\"name\",\"myiframe\");"
+ "iframe.setAttribute(\"frameborder\",\"0\");"
+ "iframe.setAttribute(\"scrolling\",\"no\");"
+ "iframe.setAttribute(\"src\",\"about:blank\");"
+ "iframe.setAttribute(\"width\",\"1\");"
+ "iframe.setAttribute(\"height\",\"1\");"
+ "document.body.appendChild(iframe);"
+
+ "var form = document.createElement(\"form\");"
+ "form.setAttribute(\"method\", \"POST\");"
+ "form.setAttribute(\"target\", \"myiframe\");"
+ "form.setAttribute(\"action\", \"" + own_host.str() + "/setYtUrl?line=\"+vid_id"+((!store)?"+\"&store=false\"":"")+");"
+ "var hiddenField = document.createElement(\"input\");"
+ "form.appendChild(hiddenField);"
+ "hiddenField.setAttribute(\"type\", \"hidden\");"
+ "hiddenField.setAttribute(\"name\", \"line\");"
+ "hiddenField.setAttribute(\"value\", vid_id);"
+ "document.body.appendChild(form);"
+ "form.submit();"
+ ;
+
+ sendHeaders(200, "OK", NULL, "text/javascript", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
+int cHttpResource::sendBmlInstHtml() {
+ *(mLog->log()) << DEBUGPREFIX
+ << " sendBmlInstHtml" << endl;
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ mContentType = MEMBLOCK;
+
+ mConnState = SERVING;
+
+ stringstream own_host ;
+ own_host << "http://"
+ << getOwnIp(mFd)
+ << ":" << mServerPort;
+
+ *(mLog->log()) << " Ownhost= " << own_host << endl;
+
+ *mResponseMessage = "<html><head>"
+ "<title>SmartTVWeb Bookmarklets</title>"
+ "</head><body>"
+ "<br>"
+ "<h2>Bookmarklet for collecting YouTube Pages</h2>"
+ "<hr width=\"80%\">"
+ "<br>"
+ "<h3>Installation</h3>"
+ "Drag the link below to your Bookmarks toolbar"
+ "<p>or</p>"
+ "<p>Right click and select &#8220;Bookmark This Link&#8221;</p>"
+ "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js&quot;;void(0)'>YT SaveNPlay</a>: Save the video and also Play it.</p>"
+ "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js?store=false&quot;;void(0)'>YT Play</a>: Play the video without saving.</p>"
+ "<br>"
+ "<hr width=\"80%\">"
+ "<h3>Usage</h3>"
+ "<p>Browse to your favorite YouTube page and click the bookmark to the bookmarklet (link above). The YouTube video is then provided to the VDR smarttvweb plugin, stored there and pushed to the TV screen for immediate playback. Tested with Firefox.</p>"
+ "<br>"
+ "<hr width=\"80%\">"
+ "<p>Have fun...<br></p>"
+ "</body>";
+
+ sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
int cHttpResource::sendEpgXml (struct stat *statbuf) {
#ifndef STANDALONE
@@ -1850,7 +2150,9 @@ int cHttpResource::receiveResume() {
string resume_str;
if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){
- entry.mFilename = guid;
+ // entry.mFilename = guid;
+ entry.mFilename = cUrlEncode::doUrlSaveDecode(guid);
+
*(mLog->log())<< DEBUGPREFIX
<< " Found a id Parameter: " << guid
<< endl;
@@ -1911,7 +2213,9 @@ int cHttpResource::sendResumeXml () {
string guid;
if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){
- entry.mFilename = guid;
+ // entry.mFilename = guid;
+ entry.mFilename = cUrlEncode::doUrlSaveDecode(guid);
+
*(mLog->log())<< DEBUGPREFIX
<< " Found a id Parameter: " << guid
<< endl;
@@ -1948,12 +2252,14 @@ int cHttpResource::sendResumeXml () {
sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+#endif
return OKAY;
-#endif
}
int cHttpResource::deleteRecording() {
+#ifndef STANDALONE
+
vector<sQueryAVP> avps;
parseQueryLine(&avps);
string id = "";
@@ -1991,6 +2297,7 @@ int cHttpResource::deleteRecording() {
<< " Deleted."
<< endl;
sendHeaders(200, "OK", NULL, NULL, -1, -1);
+ #endif
return OKAY;
}
@@ -2211,6 +2518,7 @@ int cHttpResource::sendRecordingsXml(struct stat *statbuf) {
bool cHttpResource::isTimeRequest(struct stat *statbuf) {
+#ifndef STANDALONE
vector<sQueryAVP> avps;
parseQueryLine(&avps);
string time_str = "";
@@ -2394,6 +2702,9 @@ bool cHttpResource::isTimeRequest(struct stat *statbuf) {
sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1);
}
return true;
+#else
+ return false;
+#endif
}
int cHttpResource::sendVdrDir(struct stat *statbuf) {
@@ -2826,9 +3137,9 @@ int cHttpResource::parseHttpHeaderLine (string line) {
void cHttpResource::checkRecording() {
// sets mIsRecording to true when the recording is still on-going
mIsRecording = false;
- time_t now = time(NULL);
#ifndef STANDALONE
+ time_t now = time(NULL);
// cRecordings* recordings = mFactory->getRecordings();
cRecordings* recordings = &Recordings;
diff --git a/httpresource.h b/httpresource.h
index fbbab4d..c563b3c 100755
--- a/httpresource.h
+++ b/httpresource.h
@@ -27,6 +27,7 @@
#include <cstring>
#include <pthread.h>
#include "log.h"
+#include "httpresource_base.h"
using namespace std;
@@ -71,7 +72,7 @@ sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) {
class SmartTvServer;
class cResumeEntry;
-class cHttpResource {
+class cHttpResource : public cHttpResourceBase {
public:
cHttpResource(int, int, int, SmartTvServer*);
@@ -79,20 +80,20 @@ class cHttpResource {
int handleRead();
int handleWrite();
-
int checkStatus();
int readFromClient();
+
void threadLoop();
int run();
private:
- SmartTvServer* mFactory;
+ // SmartTvServer* mFactory;
Log* mLog;
- int mServerPort;
- int mFd;
- int mReqId;
+ // int mServerPort;
+ // int mFd;
+ // int mReqId;
time_t mConnTime;
int mHandleReadCount;
@@ -146,14 +147,21 @@ class cHttpResource {
int sendChannelsXml (struct stat *statbuf);
int sendResumeXml ();
int sendVdrStatusXml (struct stat *statbuf);
+ int sendYtBookmarkletJs();
+ int sendBmlInstHtml();
+
int sendEpgXml (struct stat *statbuf);
+ int sendUrlsXml ();
int sendMediaXml (struct stat *statbuf);
-
+
+ void handleClients();
+
int sendManifest (struct stat *statbuf, bool is_hls = true);
int receiveResume();
int deleteRecording();
+ int receiveYtUrl();
void writeM3U8(double duration, int bitrate, float seg_dur, int end_seg);
void writeMPD(double duration, int bitrate, float seg_dur, int end_seg);
diff --git a/httpresource_base.h b/httpresource_base.h
new file mode 100644
index 0000000..81a62c6
--- /dev/null
+++ b/httpresource_base.h
@@ -0,0 +1,46 @@
+/*
+ * httpresource_base.h: VDR on Smart TV plugin
+ *
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#ifndef __HTTPREQUEST_base_H__
+#define __HTTPREQUEST_base_H__
+
+class SmartTvServer;
+
+class cHttpResourceBase {
+
+ public:
+ cHttpResourceBase(int f, int id, int port, SmartTvServer* fac): mFd(f), mReqId(id), mFactory(fac), mServerPort(port) {};
+ virtual ~cHttpResourceBase() {};
+
+ virtual int handleRead() =0;
+ virtual int handleWrite() = 0;
+ virtual int checkStatus() =0;
+
+ protected:
+ int mFd;
+ int mReqId;
+ SmartTvServer* mFactory;
+ int mServerPort;
+
+};
+
+#endif
diff --git a/mngurls.c b/mngurls.c
new file mode 100644
index 0000000..e9f18d0
--- /dev/null
+++ b/mngurls.c
@@ -0,0 +1,106 @@
+/*
+ * mgnurls.c: VDR on Smart TV plugin
+ *
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+#include "mngurls.h"
+
+
+cManageUrls::cManageUrls(string dir): mLog(), mFile(NULL), mEntries() {
+ mLog = Log::getInstance();
+
+ loadEntries(dir);
+ mFile = new ofstream((dir +"/urls.txt").c_str(), ios::out | ios::app);
+ mFile->seekp(ios_base::end);
+};
+
+cManageUrls::~cManageUrls() {
+ if (mFile != NULL) {
+ mFile->close();
+ delete mFile;
+ }
+ //TODO: delete entries
+};
+
+//called from outside to add an entry
+void cManageUrls::appendEntry(string type, string url) {
+ // iter through entries
+ *(mLog->log()) << " cManageUrls::appendEntry: type= " << type << "url= " << url << endl;
+
+ bool found = false;
+ if (type.compare("YT") !=0) {
+ return;
+ }
+ for (int i = 0; i < mEntries.size(); i ++) {
+ if (url.compare(mEntries[i]->mEntry) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ *(mLog->log()) << " cManageUrls::appendEntry: Appending... " << endl;
+ mEntries.push_back (new sUrlEntry (type, url));
+ appendToFile(type+"|"+url);
+ }
+}
+
+size_t cManageUrls::size() {
+ return mEntries.size();
+}
+
+sUrlEntry* cManageUrls::getEntry( int index) {
+ return mEntries[index];
+};
+
+void cManageUrls::loadEntries(string dir) {
+ ifstream myfile ((dir +"/urls.txt").c_str());
+ string line;
+
+ string type;
+
+ while ( myfile.good() ) {
+ getline (myfile, line);
+
+ if ((line == "") or (line[0] == '#'))
+ continue;
+
+ size_t pos = line.find('|');
+ string type = line.substr(0, pos);
+ string value = line.substr(pos+1);
+
+ // sUrlEntry* entry = new sUrlEntry(type, value);
+ mEntries.push_back(new sUrlEntry(type, value));
+ }
+ myfile.close();
+};
+
+void cManageUrls::appendToFile(string s_line) {
+ if (mFile == NULL) {
+ *(mLog->log()) << " ERROR in cManageUrls::appendToFile: no file open... " << endl;
+ return;
+ }
+ *(mLog->log()) << " cManageUrls::appendToFile: writing " << s_line << endl;
+ *mFile << s_line;
+ // mFile->write(s_line.c_str(), s_line.size());
+
+ mFile->flush();
+
+}
+
diff --git a/mngurls.h b/mngurls.h
new file mode 100644
index 0000000..b247808
--- /dev/null
+++ b/mngurls.h
@@ -0,0 +1,64 @@
+/*
+ * mgnurls.h: VDR on Smart TV plugin
+ *
+ * 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
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ */
+
+//Manage Urls
+
+/*
+the object is kept by the factory.
+The object is loaded with the first url request (get or post)
+The object is then kept.
+ The file is updated with every new url entry, so that it does not need to wrte when closing
+*/
+#ifndef __MANAGEURLS_H__
+#define __MANAGEURLS_H__
+
+#include<vector>
+#include<fstream>
+#include "log.h"
+
+struct sUrlEntry {
+ string mType;
+ string mEntry;
+sUrlEntry(string t,string e): mType(t), mEntry(e) {};
+};
+
+class cManageUrls {
+ public:
+ cManageUrls(string dir) ;
+ virtual ~cManageUrls();
+
+ void appendEntry (string type, string guid);
+ size_t size();
+ sUrlEntry* getEntry(int index);
+
+ private:
+ void loadEntries(string dir);
+ void appendToFile(string);
+
+ Log* mLog;
+
+ ofstream* mFile;
+ vector<sUrlEntry*> mEntries;
+
+};
+
+#endif
diff --git a/smarttvfactory.c b/smarttvfactory.c
index 3478e9d..aeee438 100755
--- a/smarttvfactory.c
+++ b/smarttvfactory.c
@@ -48,6 +48,8 @@
#include "smarttvfactory.h"
+#include "httpresource.h"
+#include "httpclient.h"
#ifndef STANDALONE
#define PORT 8000
@@ -72,7 +74,8 @@ void SmartTvServerStartThread(void* arg) {
SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1),
mSegmentDuration(10), mHasMinBufferTime(40), mLiveChannels(20),
- clientList(), mActiveSessions(0), mConfig(NULL) {
+ clientList(), mConTvClients(), mActiveSessions(0), mHttpClients(0), mConfig(NULL), mMaxFd(0),
+ mManagedUrls(NULL){
}
@@ -102,6 +105,138 @@ void SmartTvServer::cleanUp() {
mLog.shutdown();
}
+void SmartTvServer::setNonBlocking(int fd) {
+ int oldflags = fcntl(fd, F_GETFL, 0);
+ oldflags |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, oldflags);
+}
+
+void SmartTvServer::updateTvClient(string ip, string mac, time_t upd) {
+
+ bool found = false;
+ for (uint i = 0; i < mConTvClients.size(); i++) {
+ if (mConTvClients[i]->mac == mac) {
+ *(mLog.log()) << "SmartTvServer::updateTvClient: Found Entry for Mac= " << mac
+ << endl;
+ found = true;
+ mConTvClients[i]->ip = ip;
+ mConTvClients[i]->lastKeepAlive = upd;
+ break;
+ }
+ }
+ if (found == false) {
+ *(mLog.log()) << "SmartTvServer::updateTvClient: Append Entry for Mac= " << mac
+ << endl;
+ sClientEntry * entry = new sClientEntry(mac, ip, upd);
+ mConTvClients.push_back(entry);
+ }
+};
+
+void SmartTvServer::removeTvClient(string ip, string mac, time_t upd) {
+ // remove client with mac from list
+ bool found = false;
+ vector<sClientEntry*>::iterator iter;
+ for (iter = mConTvClients.begin() ; iter != mConTvClients.end(); ++iter)
+ if ((*iter)->mac == mac) {
+ found = true;
+ *(mLog.log()) << "SmartTvServer::removeTvClient: Found Entry for Mac= " << mac
+ << endl;
+ iter = mConTvClients.erase(iter);
+ break;
+ }
+
+ if (!found ) {
+ *(mLog.log()) << "SmartTvServer::removeTvClient: No entry for Mac= " << mac
+ << " found"
+ << endl;
+ }
+}
+
+cManageUrls* SmartTvServer::getUrlsObj() {
+ if (mManagedUrls == NULL)
+ mManagedUrls = new cManageUrls(mConfigDir);
+
+ return mManagedUrls;
+};
+
+void SmartTvServer::storeYtVideoId(string guid) {
+ if (mManagedUrls == NULL)
+ mManagedUrls = new cManageUrls(mConfigDir);
+
+ mManagedUrls->appendEntry("YT", guid);
+}
+
+void SmartTvServer::pushYtVideoId(string vid_id, bool store) {
+ for (uint i = 0; i < mConTvClients.size(); i ++) {
+ if ((mConTvClients[i]->ip).compare("") != 0)
+ pushYtVideoIdToClient(vid_id, mConTvClients[i]->ip, store);
+ }
+}
+
+int SmartTvServer::connectToClient(string peer) {
+ *(mLog.log()) << " SmartTvServer::connectToClient: client= " << peer << endl;
+
+ int cfd;
+ struct sockaddr_in server;
+ cfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (cfd <0) {
+ *(mLog.log()) << "Error: Cannot create client socket" << endl;
+ return -1;
+ }
+
+ memset((char *) &server, 0, sizeof(server));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(80);
+ server.sin_addr.s_addr =inet_addr(peer.c_str());
+
+ setNonBlocking(cfd);
+
+ if (connect(cfd, (const struct sockaddr *) &server, sizeof(struct sockaddr_in)) <0) {
+ if (errno != EINPROGRESS) {
+ *(mLog.log()) << "Error while connecting" << endl;
+ return -1;
+ }
+ else
+ *(mLog.log()) << "Connecting" << endl;
+ }
+ return cfd;
+}
+
+
+void SmartTvServer::addHttpResource(int rfd, cHttpResourceBase* resource) {
+
+ if (clientList.size() < (rfd+1)) {
+ clientList.resize(rfd+1, NULL); // Check.
+ }
+ if (clientList[rfd] == NULL) {
+ FD_SET(rfd, &mReadState);
+ FD_SET(rfd, &mWriteState);
+ clientList[rfd] = resource;
+
+ mHttpClients++;
+ if (rfd > mMaxFd) {
+ mMaxFd = rfd;
+ }
+ mActiveSessions ++;
+ }
+ else {
+ *(mLog.log()) << "Error: clientList idx in use" << endl;
+ // ERROR:
+ }
+}
+
+void SmartTvServer::pushYtVideoIdToClient(string vid_id, string peer, bool store) {
+ *(mLog.log()) << " SmartTvServer::pushYtVideoIdToClient vid_id= " << vid_id
+ << " client= " << peer << endl;
+
+ int cfd= connectToClient(peer);
+ if (cfd < 0)
+ return;
+ addHttpResource(cfd, new cHttpYtPushClient(cfd, mHttpClients, serverPort, this, peer, vid_id, store));
+
+}
+
int SmartTvServer::runAsThread() {
int res = pthread_create(&mThreadId, NULL, (void*(*)(void*))SmartTvServerStartThread, (void *)this);
if (res != 0) {
@@ -129,7 +264,7 @@ void SmartTvServer::loop() {
int ret = 0;
struct timeval timeout;
- int maxfd;
+ // int maxfd;
fd_set read_set;
fd_set write_set;
@@ -141,7 +276,7 @@ void SmartTvServer::loop() {
FD_ZERO(&mWriteState);
FD_SET(mServerFd, &mReadState);
- maxfd = mServerFd;
+ mMaxFd = mServerFd;
*(mLog.log()) << "mServerFd= " << mServerFd << endl;
@@ -179,7 +314,7 @@ void SmartTvServer::loop() {
timeout.tv_sec = 5;
timeout.tv_usec = 0;
- ret = select(maxfd + 1, &read_set, &write_set, NULL, &timeout);
+ ret = select(mMaxFd + 1, &read_set, &write_set, NULL, &timeout);
if (ret == 0) {
// timeout: Check for dead TCP connections
@@ -217,8 +352,8 @@ void SmartTvServer::loop() {
FD_SET(rfd, &mReadState);
FD_SET(rfd, &mWriteState);
- if (rfd > maxfd) {
- maxfd = rfd;
+ if (rfd > mMaxFd) {
+ mMaxFd = rfd;
}
if (clientList.size() < (rfd+1)) {
diff --git a/smarttvfactory.h b/smarttvfactory.h
index 1073ab0..261f2ea 100644
--- a/smarttvfactory.h
+++ b/smarttvfactory.h
@@ -28,9 +28,13 @@
#include <cstring>
#include <vector>
#include <list>
-#include "httpresource.h"
+#include <ctime>
+#include <sys/select.h>
+//#include "httpresource.h"
+#include "httpresource_base.h"
#include "log.h"
#include "stvw_cfg.h"
+#include "mngurls.h"
#ifndef STANDALONE
#include <vdr/recording.h>
@@ -38,8 +42,15 @@
using namespace std;
-#define PLG_VERSION "0.9.6"
-#define SERVER "SmartTvWeb/0.9.6"
+#define PLG_VERSION "0.9.7"
+#define SERVER "SmartTvWeb/0.9.7"
+
+struct sClientEntry {
+ string mac;
+ string ip;
+ time_t lastKeepAlive;
+sClientEntry(string m, string i, time_t t ): mac(m), ip(i), lastKeepAlive(t) {};
+};
class SmartTvServer {
public:
@@ -60,7 +71,21 @@ class SmartTvServer {
string getConfigDir() { return mConfigDir; };
cSmartTvConfig* getConfig() { return mConfig; };
+ void updateTvClient(string ip, string mac, time_t upd);
+ void removeTvClient(string ip, string mac, time_t upd);
+
+ void storeYtVideoId(string);
+
+ cManageUrls* getUrlsObj();
+
+ void pushYtVideoId(string, bool);
+ void pushYtVideoIdToClient(string vid_id, string peer, bool);
+
private:
+ void addHttpResource(int fd, cHttpResourceBase* resource);
+ int connectToClient(string peer);
+ void setNonBlocking(int fd);
+
pthread_t mThreadId;
int mRequestCount;
bool isInited;
@@ -70,14 +95,20 @@ class SmartTvServer {
int mHasMinBufferTime;
int mLiveChannels;
- vector<cHttpResource*> clientList;
+ vector<cHttpResourceBase*> clientList;
+ vector<sClientEntry*> mConTvClients;
+
int mActiveSessions;
+ int mHttpClients;
+
string mConfigDir;
cSmartTvConfig *mConfig;
int mMaxFd;
fd_set mReadState;
fd_set mWriteState;
+
+ cManageUrls* mManagedUrls;
};